summaryrefslogtreecommitdiff
path: root/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2018-04-10 18:23:57 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2018-04-10 18:23:57 -0400
commit13f31e8b725e46ca8442a943a5675723d22b4fdc (patch)
tree3ab8d45c71032e9960dbb87d888eea07f7ef9a32 /src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
parentc6c2302baf830a441b269ef7402068bd9dece22e (diff)
downloadSMAPI-13f31e8b725e46ca8442a943a5675723d22b4fdc.tar.gz
SMAPI-13f31e8b725e46ca8442a943a5675723d22b4fdc.tar.bz2
SMAPI-13f31e8b725e46ca8442a943a5675723d22b4fdc.zip
warn for fields which no longer work (#471)
Diffstat (limited to 'src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs')
-rw-r--r--src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
new file mode 100644
index 00000000..00565329
--- /dev/null
+++ b/src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace StardewModdingAPI.ModBuildConfig.Analyzer
+{
+ /// <summary>Detects references to a field which has been replaced.</summary>
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class ObsoleteFieldAnalyzer : DiagnosticAnalyzer
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>Maps obsolete fields/properties to their non-obsolete equivalent.</summary>
+ private readonly IDictionary<string, string> ReplacedFields = new Dictionary<string, string>
+ {
+ // Farmer
+ ["StardewValley.Farmer::friendships"] = "friendshipData"
+ };
+
+ /// <summary>Describes the diagnostic rule covered by the analyzer.</summary>
+ private readonly IDictionary<string, DiagnosticDescriptor> Rules = new Dictionary<string, DiagnosticDescriptor>
+ {
+ ["SMAPI003"] = new DiagnosticDescriptor(
+ id: "SMAPI003",
+ title: "Reference to obsolete field",
+ messageFormat: "The '{0}' field is obsolete and should be replaced with '{1}'. See https://smapi.io/buildmsg/smapi003 for details.",
+ category: "SMAPI.CommonErrors",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ helpLinkUri: "https://smapi.io/buildmsg/smapi003"
+ )
+ };
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The descriptors for the diagnostics that this analyzer is capable of producing.</summary>
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public ObsoleteFieldAnalyzer()
+ {
+ this.SupportedDiagnostics = ImmutableArray.CreateRange(this.Rules.Values);
+ }
+
+ /// <summary>Called once at session start to register actions in the analysis context.</summary>
+ /// <param name="context">The analysis context.</param>
+ public override void Initialize(AnalysisContext context)
+ {
+ // SMAPI003: avoid obsolete fields
+ context.RegisterSyntaxNodeAction(
+ this.AnalyzeObsoleteFields,
+ SyntaxKind.SimpleMemberAccessExpression
+ );
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Analyse a syntax node and add a diagnostic message if it references an obsolete field.</summary>
+ /// <param name="context">The analysis context.</param>
+ private void AnalyzeObsoleteFields(SyntaxNodeAnalysisContext context)
+ {
+ try
+ {
+ // get reference info
+ MemberAccessExpressionSyntax node = (MemberAccessExpressionSyntax)context.Node;
+ ITypeSymbol declaringType = context.SemanticModel.GetTypeInfo(node.Expression).Type;
+ string propertyName = node.Name.Identifier.Text;
+
+ // suggest replacement
+ for (ITypeSymbol type = declaringType; type != null; type = type.BaseType)
+ {
+ if (this.ReplacedFields.TryGetValue($"{type}::{propertyName}", out string replacement))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(this.Rules["SMAPI003"], context.Node.GetLocation(), $"{type}.{propertyName}", replacement));
+ break;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Failed processing expression: '{context.Node}'. Exception details: {ex.ToString().Replace('\r', ' ').Replace('\n', ' ')}");
+ }
+ }
+ }
+}