Skip to content

Commit c93507b

Browse files
authored
Improve MA0084 to detect primary constructor parameters (#656)
1 parent 06f781a commit c93507b

2 files changed

Lines changed: 72 additions & 2 deletions

File tree

src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Collections.Generic;
22
using System.Collections.Immutable;
3+
using System.Threading;
34
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
46
using Microsoft.CodeAnalysis.CSharp.Syntax;
57
using Microsoft.CodeAnalysis.Diagnostics;
68
using Microsoft.CodeAnalysis.Operations;
@@ -41,7 +43,7 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context)
4143
if (containingType == null)
4244
return;
4345

44-
foreach (var member in GetSymbols(containingType, localSymbol.Name))
46+
foreach (var member in GetSymbols(containingType, localSymbol.Name, context.CancellationToken))
4547
{
4648
if (!semanticModel.IsAccessible(operation.Syntax.SpanStart, member))
4749
continue;
@@ -57,6 +59,12 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context)
5759
ReportDiagnostic("property");
5860
return;
5961
}
62+
63+
if (member is IParameterSymbol && !operation.IsInStaticContext(context.CancellationToken))
64+
{
65+
ReportDiagnostic("parameter");
66+
return;
67+
}
6068
}
6169

6270
void ReportDiagnostic(string type)
@@ -72,8 +80,32 @@ void ReportDiagnostic(string type)
7280
}
7381
}
7482

75-
private static IEnumerable<ISymbol> GetSymbols(INamedTypeSymbol? type, string name)
83+
private static IEnumerable<ISymbol> GetSymbols(INamedTypeSymbol? type, string name, CancellationToken cancellationToken)
7684
{
85+
#if CSHARP12_OR_GREATER
86+
if (type?.InstanceConstructors is not null)
87+
{
88+
foreach (var constructor in type.InstanceConstructors)
89+
{
90+
if (constructor.Parameters.Length == 0)
91+
continue;
92+
93+
foreach (var syntaxRef in constructor.DeclaringSyntaxReferences)
94+
{
95+
var syntax = syntaxRef.GetSyntax(cancellationToken);
96+
if (syntax.IsKind(SyntaxKind.ClassDeclaration) || syntax.IsKind(SyntaxKind.StructDeclaration))
97+
{
98+
var typeDeclaration = (TypeDeclarationSyntax)syntax;
99+
foreach (var param in constructor.Parameters)
100+
{
101+
yield return param;
102+
}
103+
}
104+
}
105+
}
106+
}
107+
#endif
108+
77109
while (type != null)
78110
{
79111
var members = type.GetMembers(name);

tests/Meziantou.Analyzer.Test/Rules/LocalVariablesShouldNotHideSymbolsAnalyzerTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,44 @@ await CreateProjectBuilder()
7070
.ValidateAsync();
7171
}
7272

73+
#if CSHARP12_OR_GREATER
74+
[Fact]
75+
public async Task LocalVariableHidePrimaryConstructorParameter()
76+
{
77+
const string SourceCode = """
78+
class Test(int a)
79+
{
80+
void A()
81+
{
82+
var [|a|] = 10;
83+
}
84+
}
85+
""";
86+
await CreateProjectBuilder()
87+
.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12)
88+
.WithSourceCode(SourceCode)
89+
.ValidateAsync();
90+
}
91+
92+
[Fact]
93+
public async Task LocalVariableDoesNotHidePrimaryConstructorParameterInStaticMethod()
94+
{
95+
const string SourceCode = """
96+
class Test(int a)
97+
{
98+
static void A()
99+
{
100+
var a = 10;
101+
}
102+
}
103+
""";
104+
await CreateProjectBuilder()
105+
.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12)
106+
.WithSourceCode(SourceCode)
107+
.ValidateAsync();
108+
}
109+
#endif
110+
73111
[Fact]
74112
public async Task LocalVariableHideNotVisibleFieldFromParentClass()
75113
{

0 commit comments

Comments
 (0)