summaryrefslogtreecommitdiff
path: root/src/SMAPI.ModBuildConfig.Analyzer
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.ModBuildConfig.Analyzer')
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs12
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs30
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs2
3 files changed, 40 insertions, 4 deletions
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
index 77e7812f..e0c0cd63 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/AnalyzerUtilities.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -42,5 +43,16 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
memberName = null;
return false;
}
+
+ /// <summary>Get the class types in a type's inheritance chain, including itself.</summary>
+ /// <param name="type">The initial type.</param>
+ public static IEnumerable<ITypeSymbol> GetConcreteTypes(ITypeSymbol type)
+ {
+ while (type != null)
+ {
+ yield return type;
+ type = type.BaseType;
+ }
+ }
}
}
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
index 895eebf0..7c8b804e 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/NetFieldAnalyzer.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
@@ -17,6 +19,9 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
/// <summary>The namespace for Stardew Valley's <c>Netcode</c> types.</summary>
private const string NetcodeNamespace = "Netcode";
+ /// <summary>The full name for Stardew Valley's <c>Netcode.NetList</c> type.</summary>
+ private readonly string NetListTypeFullName = "Netcode.NetList";
+
/// <summary>Maps net fields to their equivalent non-net properties where available.</summary>
private readonly IDictionary<string, string> NetFieldWrapperProperties = new Dictionary<string, string>
{
@@ -190,10 +195,9 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
return;
if (!this.IsNetType(memberType.Type))
return;
- bool isConverted = !this.IsNetType(memberType.ConvertedType);
// warn: use property wrapper if available
- for (ITypeSymbol type = declaringType; type != null; type = type.BaseType)
+ foreach (ITypeSymbol type in AnalyzerUtilities.GetConcreteTypes(declaringType))
{
if (this.NetFieldWrapperProperties.TryGetValue($"{type}::{memberName}", out string suggestedPropertyName))
{
@@ -203,7 +207,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
}
// warn: implicit conversion
- if (isConverted)
+ if (this.IsInvalidConversion(memberType))
context.ReportDiagnostic(Diagnostic.Create(this.Rules["AvoidImplicitNetFieldCast"], context.Node.GetLocation(), context.Node, memberType.Type.Name, memberType.ConvertedType));
}
catch (Exception ex)
@@ -212,6 +216,26 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
}
}
+ /// <summary>Get whether a net field was converted in an error-prone way.</summary>
+ /// <param name="typeInfo">The member access type info.</param>
+ private bool IsInvalidConversion(TypeInfo typeInfo)
+ {
+ // no conversion
+ if (!this.IsNetType(typeInfo.Type) || this.IsNetType(typeInfo.ConvertedType))
+ return false;
+
+ // list conversion to an implemented interface is OK
+ if (AnalyzerUtilities.GetConcreteTypes(typeInfo.Type).Any(p => p.ToString().StartsWith(this.NetListTypeFullName))) // StartsWith to ignore generics
+ {
+ string toType = typeInfo.ConvertedType.ToString();
+ if (toType.StartsWith(typeof(IEnumerable<>).Namespace) || toType == typeof(IEnumerable).FullName)
+ return false;
+ }
+
+ // avoid any other conversions
+ return true;
+ }
+
/// <summary>Get whether a type symbol references a <c>Netcode</c> type.</summary>
/// <param name="typeSymbol">The type symbol.</param>
private bool IsNetType(ITypeSymbol typeSymbol)
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
index dc21e505..943d0350 100644
--- a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
+++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
@@ -79,7 +79,7 @@ namespace StardewModdingAPI.ModBuildConfig.Analyzer
return;
// suggest replacement
- for (ITypeSymbol type = declaringType; type != null; type = type.BaseType)
+ foreach (ITypeSymbol type in AnalyzerUtilities.GetConcreteTypes(declaringType))
{
if (this.ReplacedFields.TryGetValue($"{type}::{memberName}", out string replacement))
{