Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analyzer for SemanticModel.GetDeclaredSymbol #6779

Merged
merged 17 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
; Please do not edit this file manually, it should only be updated through code fix application.

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RS1039 | MicrosoftCodeAnalysisCorrectness | Warning | SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer
RS1040 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis.Analyzers;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers
{
using static CodeAnalysisDiagnosticsResources;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer : DiagnosticAnalyzer
{
internal static readonly DiagnosticDescriptor DiagnosticDescriptor = new(
DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNull,
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)),
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage)),
DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);

internal static readonly DiagnosticDescriptor FieldDiagnosticDescriptor = new(
DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField,
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)),
CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage)),
DiagnosticCategory.MicrosoftCodeAnalysisCorrectness,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription)),
helpLinkUri: null,
customTags: WellKnownDiagnosticTagsExtensions.Telemetry);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptor, FieldDiagnosticDescriptor);

public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(static context =>
{
var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
IMethodSymbol? getDeclaredSymbolMethod;
if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpExtensions, out var csharpExtensions)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisModelExtensions, out var modelExtensions)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxBaseFieldDeclarationSyntax, out var baseFieldDeclaration)
|| !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisSyntaxNode, out var syntaxNode)
|| (getDeclaredSymbolMethod = (IMethodSymbol?)modelExtensions.GetMembers(nameof(ModelExtensions.GetDeclaredSymbol)).FirstOrDefault(m => m is IMethodSymbol { Parameters.Length: >= 2 })) is null)
{
return;
}

var allowedTypes = csharpExtensions.GetMembers(nameof(CSharpExtensions.GetDeclaredSymbol))
.OfType<IMethodSymbol>()
.Where(m => m.Parameters.Length >= 2)
.Select(m => m.Parameters[1].Type);

context.RegisterOperationAction(ctx => AnalyzeInvocation(ctx, getDeclaredSymbolMethod, allowedTypes, baseFieldDeclaration, syntaxNode), OperationKind.Invocation);
});
}

private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol getDeclaredSymbolMethod, IEnumerable<ITypeSymbol> allowedTypes, INamedTypeSymbol baseFieldDeclarationType, INamedTypeSymbol syntaxNodeType)
{
var invocation = (IInvocationOperation)context.Operation;
if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, getDeclaredSymbolMethod))
{
var syntaxNodeDerivingType = invocation.Arguments.GetArgumentForParameterAtIndex(1).Value.WalkDownConversion().Type;
if (syntaxNodeDerivingType is null || syntaxNodeDerivingType.Equals(syntaxNodeType))
{
return;
}

if (syntaxNodeDerivingType.DerivesFrom(baseFieldDeclarationType))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(FieldDiagnosticDescriptor, syntaxNodeDerivingType.Name));
}
else if (allowedTypes.All(type => !syntaxNodeDerivingType.DerivesFrom(type, baseTypesOnly: true, checkTypeParameterConstraints: false)))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(DiagnosticDescriptor, syntaxNodeDerivingType.Name));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -571,4 +571,19 @@
<data name="DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleTitle" xml:space="preserve">
<value>Compiler extensions should be implemented in assemblies with compiler-provided references</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription" xml:space="preserve">
<value>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage" xml:space="preserve">
<value>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle" xml:space="preserve">
<value>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription" xml:space="preserve">
<value>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</value>
</data>
<data name="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage" xml:space="preserve">
<value>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</value>
333fred marked this conversation as resolved.
Show resolved Hide resolved
</data>
</root>
2 changes: 2 additions & 0 deletions src/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ internal static class DiagnosticIds
public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036";
public const string AddCompilationEndCustomTagRuleId = "RS1037";
public const string DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId = "RS1038";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNull = "RS1039";
public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField = "RS1040";

// Release tracking analyzer IDs
public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Nepřidávat odebraná ID diagnostiky analyzátoru do nevydané verze analyzátoru</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Symbol byl označen jako zakázaný pro použití v analyzátorech a místo toho by se měla použít náhrada.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Fügen Sie einem nicht veröffentlichten Analysetoolrelease keine entfernten Analysetooldiagnose-IDs hinzu.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Das Symbol wurde für die Verwendung in dem Analysetool als gesperrt gekennzeichnet, und es muss stattdessen eine Alternative verwendet werden.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">No agregar identificadores de diagnóstico del analizador eliminados a una versión del analizador no incluida</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">El símbolo ha sido marcado como de uso prohibido en los analizadores, y en su lugar debe usarse uno alternativo.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,31 @@
<target state="translated">Ne pas ajouter d'ID de diagnostic d'analyseur supprimé à une version d'analyseur non fournie</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription">
<source>Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</source>
<target state="new">Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage">
<source>A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</source>
<target state="new">A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle">
<source>This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</source>
<target state="new">This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'</target>
<note />
</trans-unit>
<trans-unit id="SymbolIsBannedInAnalyzersDescription">
<source>The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.</source>
<target state="translated">Le symbole a été marqué comme étant interdit d’utilisation dans les analyseurs, et un autre symbole doit être utilisé à la place.</target>
Expand Down
Loading