diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Resources/CSharpMarkAssembliesWithNeutralResourcesLanguage.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Resources/CSharpMarkAssembliesWithNeutralResourcesLanguage.cs index 616a48f1e1..c736608ba9 100644 --- a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Resources/CSharpMarkAssembliesWithNeutralResourcesLanguage.cs +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Resources/CSharpMarkAssembliesWithNeutralResourcesLanguage.cs @@ -15,16 +15,17 @@ namespace Microsoft.NetCore.CSharp.Analyzers.Resources [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class CSharpMarkAssembliesWithNeutralResourcesLanguageAnalyzer : MarkAssembliesWithNeutralResourcesLanguageAnalyzer { - protected override void RegisterAttributeAnalyzer(CompilationStartAnalysisContext context, Action onResourceFound) + protected override void RegisterAttributeAnalyzer(CompilationStartAnalysisContext context, Action onResourceFound, INamedTypeSymbol generatedCode) { - context.RegisterSyntaxNodeAction(nc => + context.RegisterSyntaxNodeAction(context => { - if (!CheckAttribute(nc.Node)) + var attributeSyntax = (AttributeSyntax)context.Node; + if (!CheckAttribute(attributeSyntax)) { return; } - if (!CheckResxGeneratedFile(nc.SemanticModel, nc.Node, ((AttributeSyntax)nc.Node).ArgumentList.Arguments[0].Expression, nc.CancellationToken)) + if (!CheckResxGeneratedFile(context.SemanticModel, attributeSyntax, attributeSyntax.ArgumentList.Arguments[0].Expression, generatedCode, context.CancellationToken)) { return; } @@ -33,9 +34,8 @@ protected override void RegisterAttributeAnalyzer(CompilationStartAnalysisContex }, SyntaxKind.Attribute); } - private static bool CheckAttribute(SyntaxNode node) + private static bool CheckAttribute(AttributeSyntax attribute) { - var attribute = node as AttributeSyntax; return attribute?.Name?.GetLastToken().Text?.Equals(GeneratedCodeAttribute, StringComparison.Ordinal) == true && attribute.ArgumentList.Arguments.Count > 0; } diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Resources/MarkAssembliesWithNeutralResourcesLanguage.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Resources/MarkAssembliesWithNeutralResourcesLanguage.cs index 5ac662a334..7ff37c9d9a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Resources/MarkAssembliesWithNeutralResourcesLanguage.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Resources/MarkAssembliesWithNeutralResourcesLanguage.cs @@ -38,7 +38,7 @@ public abstract class MarkAssembliesWithNeutralResourcesLanguageAnalyzer : Diagn isDataflowRule: false, isReportedAtCompilationEnd: true); - protected abstract void RegisterAttributeAnalyzer(CompilationStartAnalysisContext context, Action onResourceFound); + protected abstract void RegisterAttributeAnalyzer(CompilationStartAnalysisContext context, Action onResourceFound, INamedTypeSymbol generatedCode); public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); @@ -51,13 +51,29 @@ public override void Initialize(AnalysisContext context) // any diagnostics from it. context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); - context.RegisterCompilationStartAction(cc => + context.RegisterCompilationStartAction(context => { - var hasResource = false; + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCodeDomCompilerGeneratedCodeAttribute, out var generatedCode)) + { + return; + } - RegisterAttributeAnalyzer(cc, () => hasResource = true); + INamedTypeSymbol? attribute = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemResourcesNeutralResourcesLanguageAttribute); + if (attribute is null) + { + return; + } - cc.RegisterCompilationEndAction(ce => + if (TryCheckNeutralResourcesLanguageAttribute(context.Compilation, attribute, out var data)) + { + // attribute already exist + return; + } + + var hasResource = false; + RegisterAttributeAnalyzer(context, () => hasResource = true, generatedCode); + + context.RegisterCompilationEndAction(context => { // there is nothing to do. if (!hasResource) @@ -65,21 +81,15 @@ public override void Initialize(AnalysisContext context) return; } - if (TryCheckNeutralResourcesLanguageAttribute(ce, out AttributeData data)) - { - // attribute already exist - return; - } - if (data != null) { // we have the attribute but its doing it wrong. - ce.ReportDiagnostic(data.ApplicationSyntaxReference.GetSyntax(ce.CancellationToken).CreateDiagnostic(Rule)); + context.ReportDiagnostic(data.ApplicationSyntaxReference.GetSyntax(context.CancellationToken).CreateDiagnostic(Rule)); return; } // attribute just don't exist - ce.ReportNoLocationDiagnostic(Rule); + context.ReportNoLocationDiagnostic(Rule); }); }); } @@ -89,14 +99,13 @@ protected static bool CheckDesignerFile(SyntaxTree tree) return tree.FilePath?.IndexOf(Designer, StringComparison.OrdinalIgnoreCase) > 0; } - protected static bool CheckResxGeneratedFile(SemanticModel model, SyntaxNode attribute, SyntaxNode argument, CancellationToken cancellationToken) + protected static bool CheckResxGeneratedFile(SemanticModel model, SyntaxNode attribute, SyntaxNode argument, INamedTypeSymbol generatedCode, CancellationToken cancellationToken) { if (!CheckDesignerFile(model.SyntaxTree)) { return false; } - INamedTypeSymbol? generatedCode = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCodeDomCompilerGeneratedCodeAttribute); if (model.GetSymbolInfo(attribute, cancellationToken).Symbol?.ContainingType?.Equals(generatedCode) != true) { return false; @@ -121,15 +130,12 @@ protected static bool CheckResxGeneratedFile(SemanticModel model, SyntaxNode att return true; } - private static bool TryCheckNeutralResourcesLanguageAttribute(CompilationAnalysisContext context, out AttributeData attributeData) + private static bool TryCheckNeutralResourcesLanguageAttribute(Compilation compilation, INamedTypeSymbol attribute, out AttributeData? attributeData) { - INamedTypeSymbol? attribute = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemResourcesNeutralResourcesLanguageAttribute); - INamedTypeSymbol? @string = context.Compilation.GetSpecialType(SpecialType.System_String); - - IEnumerable attributes = context.Compilation.Assembly.GetAttributes().Where(d => d.AttributeClass?.Equals(attribute) == true); + IEnumerable attributes = compilation.Assembly.GetAttributes().Where(d => SymbolEqualityComparer.Default.Equals(attribute, d.AttributeClass)); foreach (AttributeData data in attributes) { - if (data.ConstructorArguments.Any(c => c.Type?.Equals(@string) == true && !string.IsNullOrWhiteSpace((string)c.Value))) + if (data.ConstructorArguments.Any(c => c.Value is string constantValue && !string.IsNullOrWhiteSpace(constantValue))) { // found one that already does right thing. attributeData = data; diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Resources/BasicMarkAssembliesWithNeutralResourcesLanguage.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Resources/BasicMarkAssembliesWithNeutralResourcesLanguage.vb index 06123a00b7..7e231817df 100644 --- a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Resources/BasicMarkAssembliesWithNeutralResourcesLanguage.vb +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Resources/BasicMarkAssembliesWithNeutralResourcesLanguage.vb @@ -13,14 +13,15 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Resources Public NotInheritable Class BasicMarkAssembliesWithNeutralResourcesLanguageAnalyzer Inherits MarkAssembliesWithNeutralResourcesLanguageAnalyzer - Protected Overrides Sub RegisterAttributeAnalyzer(context As CompilationStartAnalysisContext, onResourceFound As Action) + Protected Overrides Sub RegisterAttributeAnalyzer(context As CompilationStartAnalysisContext, onResourceFound As Action, generatedCode As INamedTypeSymbol) context.RegisterSyntaxNodeAction( Sub(nc) - If Not CheckBasicAttribute(nc.Node) Then + Dim attributeSyntax = DirectCast(nc.Node, AttributeSyntax) + If Not CheckBasicAttribute(attributeSyntax) Then Return End If - If Not CheckResxGeneratedFile(nc.SemanticModel, nc.Node, DirectCast(nc.Node, AttributeSyntax).ArgumentList.Arguments(0).GetExpression(), nc.CancellationToken) Then + If Not CheckResxGeneratedFile(nc.SemanticModel, attributeSyntax, attributeSyntax.ArgumentList.Arguments(0).GetExpression(), generatedCode, nc.CancellationToken) Then Return End If @@ -28,8 +29,7 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Resources End Sub, SyntaxKind.Attribute) End Sub - Private Shared Function CheckBasicAttribute(node As SyntaxNode) As Boolean - Dim attribute = TryCast(node, AttributeSyntax) + Private Shared Function CheckBasicAttribute(attribute As AttributeSyntax) As Boolean Return (attribute?.Name?.GetLastToken().Text.Equals(GeneratedCodeAttribute, StringComparison.Ordinal) = True AndAlso attribute.ArgumentList.Arguments.Count > 0).GetValueOrDefault() End Function