diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-04-10 18:23:57 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-04-10 18:23:57 -0400 |
commit | 13f31e8b725e46ca8442a943a5675723d22b4fdc (patch) | |
tree | 3ab8d45c71032e9960dbb87d888eea07f7ef9a32 /src/SMAPI.ModBuildConfig.Analyzer/ObsoleteFieldAnalyzer.cs | |
parent | c6c2302baf830a441b269ef7402068bd9dece22e (diff) | |
download | SMAPI-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.cs | 98 |
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', ' ')}"); + } + } + } +} |