From 13f31e8b725e46ca8442a943a5675723d22b4fdc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 10 Apr 2018 18:23:57 -0400 Subject: warn for fields which no longer work (#471) --- .../ObsoleteFieldAnalyzer.cs | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs (limited to 'src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs') 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 +{ + /// Detects references to a field which has been replaced. + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ObsoleteFieldAnalyzer : DiagnosticAnalyzer + { + /********* + ** Properties + *********/ + /// Maps obsolete fields/properties to their non-obsolete equivalent. + private readonly IDictionary ReplacedFields = new Dictionary + { + // Farmer + ["StardewValley.Farmer::friendships"] = "friendshipData" + }; + + /// Describes the diagnostic rule covered by the analyzer. + private readonly IDictionary Rules = new Dictionary + { + ["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 + *********/ + /// The descriptors for the diagnostics that this analyzer is capable of producing. + public override ImmutableArray SupportedDiagnostics { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public ObsoleteFieldAnalyzer() + { + this.SupportedDiagnostics = ImmutableArray.CreateRange(this.Rules.Values); + } + + /// Called once at session start to register actions in the analysis context. + /// The analysis context. + public override void Initialize(AnalysisContext context) + { + // SMAPI003: avoid obsolete fields + context.RegisterSyntaxNodeAction( + this.AnalyzeObsoleteFields, + SyntaxKind.SimpleMemberAccessExpression + ); + } + + + /********* + ** Private methods + *********/ + /// Analyse a syntax node and add a diagnostic message if it references an obsolete field. + /// The analysis context. + 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', ' ')}"); + } + } + } +} -- cgit