Skip to content

Commit 99d4c4a

Browse files
authored
Add analyzer 'Declare explicit/implicit type' (#1335)
1 parent c5b77c2 commit 99d4c4a

File tree

35 files changed

+1186
-480
lines changed

35 files changed

+1186
-480
lines changed

ChangeLog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Add analyzer "Add/remove blank line between switch sections" ([RCS0061](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0061)) ([PR](https://github.com/dotnet/roslynator/pull/1302))
1313
- Option (required): `roslynator_blank_line_between_switch_sections = include|omit|omit_after_block`
1414
- Make analyzer [RCS0014](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0014) obsolete
15+
- Add analyzer "Declare explicit/implicit type" ([RCS1264](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1264)) ([PR](https://github.com/dotnet/roslynator/pull/1335))
16+
- Required option: `roslynator_use_var = always | never | when_type_is_obvious`
17+
- This analyzer consolidates following analyzers (which are made obsolete):
18+
- [RCS1008](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1008)
19+
- [RCS1009](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1009)
20+
- [RCS1010](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1010)
21+
- [RCS1012](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1012)
22+
- [RCS1176](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1176)
23+
- [RCS1177](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1177)
1524
- Add code fix "Declare as nullable" ([PR](https://github.com/dotnet/roslynator/pull/1333))
1625
- Applicable to: `CS8600`, `CS8610`, `CS8765` and `CS8767`
1726
- Add option `roslynator_use_collection_expression = true|false` ([PR](https://github.com/dotnet/roslynator/pull/1325))

src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarCodeFixProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using System.Composition;
56
using System.Diagnostics;
@@ -14,6 +15,7 @@
1415

1516
namespace Roslynator.CSharp.CodeFixes;
1617

18+
[Obsolete("Use code fix provider 'UseVarOrExplicitTypeCodeFixProvider' instead.")]
1719
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseExplicitTypeInsteadOfVarCodeFixProvider))]
1820
[Shared]
1921
public sealed class UseExplicitTypeInsteadOfVarCodeFixProvider : BaseCodeFixProvider

src/Analyzers.CodeFixes/CSharp/CodeFixes/UseExplicitTypeInsteadOfVarInForEachCodeFixProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace Roslynator.CSharp.CodeFixes;
1515

16+
[Obsolete("Use code fix provider 'UseVarOrExplicitTypeCodeFixProvider' instead.")]
1617
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseExplicitTypeInsteadOfVarInForEachCodeFixProvider))]
1718
[Shared]
1819
public sealed class UseExplicitTypeInsteadOfVarInForEachCodeFixProvider : BaseCodeFixProvider

src/Analyzers.CodeFixes/CSharp/CodeFixes/UseVarInsteadOfExplicitTypeCodeFixProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace Roslynator.CSharp.CodeFixes;
1515

16+
[Obsolete("Use code fix provider 'UseVarOrExplicitTypeCodeFixProvider' instead.")]
1617
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseVarInsteadOfExplicitTypeCodeFixProvider))]
1718
[Shared]
1819
public sealed class UseVarInsteadOfExplicitTypeCodeFixProvider : BaseCodeFixProvider
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System.Collections.Immutable;
4+
using System.Composition;
5+
using System.Diagnostics;
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CodeActions;
9+
using Microsoft.CodeAnalysis.CodeFixes;
10+
using Microsoft.CodeAnalysis.CSharp;
11+
using Microsoft.CodeAnalysis.CSharp.Syntax;
12+
using Roslynator.CodeFixes;
13+
using Roslynator.CSharp;
14+
using static Roslynator.CSharp.CodeActionFactory;
15+
16+
namespace Roslynator.CSharp.CodeFixes;
17+
18+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseVarOrExplicitTypeCodeFixProvider))]
19+
[Shared]
20+
public sealed class UseVarOrExplicitTypeCodeFixProvider : BaseCodeFixProvider
21+
{
22+
public override ImmutableArray<string> FixableDiagnosticIds
23+
{
24+
get { return ImmutableArray.Create(DiagnosticIdentifiers.UseVarOrExplicitType); }
25+
}
26+
27+
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
28+
{
29+
SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);
30+
31+
if (!TryFindFirstAncestorOrSelf(
32+
root,
33+
context.Span,
34+
out SyntaxNode node,
35+
predicate: f => f is TypeSyntax
36+
|| f.IsKind(
37+
SyntaxKind.VariableDeclaration,
38+
SyntaxKind.DeclarationExpression,
39+
SyntaxKind.ForEachStatement,
40+
SyntaxKind.ForEachVariableStatement,
41+
SyntaxKind.TupleExpression)))
42+
{
43+
return;
44+
}
45+
46+
Document document = context.Document;
47+
Diagnostic diagnostic = context.Diagnostics[0];
48+
49+
SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);
50+
51+
if (node is TypeSyntax type)
52+
{
53+
if (type.IsVar)
54+
{
55+
if (node.IsParentKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement))
56+
{
57+
node = node.Parent;
58+
}
59+
else if (node.IsParentKind(SyntaxKind.DeclarationExpression))
60+
{
61+
node = node.Parent;
62+
63+
if (node.IsParentKind(SyntaxKind.ForEachVariableStatement))
64+
node = node.Parent;
65+
}
66+
}
67+
else
68+
{
69+
CodeAction codeAction = ChangeTypeToVar(document, type, equivalenceKey: GetEquivalenceKey(diagnostic, "ToImplicit"));
70+
context.RegisterCodeFix(codeAction, diagnostic);
71+
return;
72+
}
73+
}
74+
75+
switch (node)
76+
{
77+
case TupleExpressionSyntax tupleExpression:
78+
{
79+
CodeAction codeAction = ChangeTypeToVar(document, tupleExpression, equivalenceKey: GetEquivalenceKey(diagnostic, "ToImplicit"));
80+
context.RegisterCodeFix(codeAction, diagnostic);
81+
break;
82+
}
83+
case VariableDeclarationSyntax variableDeclaration:
84+
{
85+
ExpressionSyntax value = variableDeclaration.Variables[0].Initializer.Value;
86+
ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(value, context.CancellationToken);
87+
88+
if (typeSymbol is null)
89+
{
90+
var localSymbol = semanticModel.GetDeclaredSymbol(variableDeclaration.Variables[0], context.CancellationToken) as ILocalSymbol;
91+
92+
if (localSymbol is not null)
93+
{
94+
typeSymbol = localSymbol.Type;
95+
value = value.WalkDownParentheses();
96+
97+
Debug.Assert(
98+
value.IsKind(SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression),
99+
value.Kind().ToString());
100+
101+
if (value.IsKind(SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression))
102+
typeSymbol = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated);
103+
}
104+
else
105+
{
106+
SyntaxDebug.Fail(variableDeclaration.Variables[0]);
107+
return;
108+
}
109+
}
110+
111+
CodeAction codeAction = UseExplicitType(document, variableDeclaration.Type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic, "ToExplicit"));
112+
context.RegisterCodeFix(codeAction, diagnostic);
113+
break;
114+
}
115+
case DeclarationExpressionSyntax declarationExpression:
116+
{
117+
ITypeSymbol typeSymbol = null;
118+
if (declarationExpression.Parent is AssignmentExpressionSyntax assignment)
119+
{
120+
typeSymbol = semanticModel.GetTypeSymbol(assignment.Right, context.CancellationToken);
121+
}
122+
else if (declarationExpression.Parent is ArgumentSyntax argument)
123+
{
124+
IParameterSymbol parameterSymbol = DetermineParameterHelper.DetermineParameter(argument, semanticModel, cancellationToken: context.CancellationToken);
125+
typeSymbol = parameterSymbol?.Type;
126+
}
127+
128+
if (typeSymbol is null)
129+
{
130+
var localSymbol = semanticModel.GetDeclaredSymbol(declarationExpression.Designation, context.CancellationToken) as ILocalSymbol;
131+
typeSymbol = (localSymbol?.Type) ?? semanticModel.GetTypeSymbol(declarationExpression, context.CancellationToken);
132+
}
133+
134+
CodeAction codeAction = UseExplicitType(document, declarationExpression.Type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic, "ToExplicit"));
135+
context.RegisterCodeFix(codeAction, diagnostic);
136+
break;
137+
}
138+
case ForEachStatementSyntax forEachStatement:
139+
{
140+
ITypeSymbol typeSymbol = semanticModel.GetForEachStatementInfo((CommonForEachStatementSyntax)node).ElementType;
141+
142+
CodeAction codeAction = UseExplicitType(document, forEachStatement.Type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic, "ToExplicit"));
143+
context.RegisterCodeFix(codeAction, diagnostic);
144+
break;
145+
}
146+
case ForEachVariableStatementSyntax forEachVariableStatement:
147+
{
148+
var declarationExpression = (DeclarationExpressionSyntax)forEachVariableStatement.Variable;
149+
ITypeSymbol typeSymbol = semanticModel.GetForEachStatementInfo((CommonForEachStatementSyntax)node).ElementType;
150+
151+
CodeAction codeAction = UseExplicitType(document, declarationExpression.Type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic, "ToExplicit"));
152+
context.RegisterCodeFix(codeAction, diagnostic);
153+
break;
154+
}
155+
}
156+
}
157+
}

src/Analyzers.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,8 @@ else if (condition2)
17991799
<Analyzer>
18001800
<Id>RCS1008</Id>
18011801
<Identifier>UseExplicitTypeInsteadOfVarWhenTypeIsNotObvious</Identifier>
1802+
<Status>Obsolete</Status>
1803+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
18021804
<Title>Use explicit type instead of 'var' (when the type is not obvious)</Title>
18031805
<MessageFormat>Use explicit type instead of 'var'</MessageFormat>
18041806
<DefaultSeverity>Hidden</DefaultSeverity>
@@ -1814,6 +1816,8 @@ else if (condition2)
18141816
<Id>RCS1009</Id>
18151817
<Identifier>UseExplicitTypeInsteadOfVarInForEach</Identifier>
18161818
<Title>Use explicit type instead of 'var' (foreach variable)</Title>
1819+
<Status>Obsolete</Status>
1820+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
18171821
<MessageFormat>Use explicit type instead of 'var'</MessageFormat>
18181822
<DefaultSeverity>Hidden</DefaultSeverity>
18191823
<IsEnabledByDefault>false</IsEnabledByDefault>
@@ -1835,6 +1839,8 @@ foreach (var item in items)
18351839
<Analyzer>
18361840
<Id>RCS1010</Id>
18371841
<Identifier>UseVarInsteadOfExplicitTypeWhenTypeIsObvious</Identifier>
1842+
<Status>Obsolete</Status>
1843+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
18381844
<Title>Use 'var' instead of explicit type (when the type is obvious)</Title>
18391845
<MessageFormat>Use 'var' instead of explicit type</MessageFormat>
18401846
<DefaultSeverity>Hidden</DefaultSeverity>
@@ -1850,6 +1856,8 @@ foreach (var item in items)
18501856
<Analyzer>
18511857
<Id>RCS1012</Id>
18521858
<Identifier>UseExplicitTypeInsteadOfVarWhenTypeIsObvious</Identifier>
1859+
<Status>Obsolete</Status>
1860+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
18531861
<Title>Use explicit type instead of 'var' (when the type is obvious)</Title>
18541862
<MessageFormat>Use explicit type instead of 'var'</MessageFormat>
18551863
<DefaultSeverity>Hidden</DefaultSeverity>
@@ -5411,6 +5419,8 @@ else
54115419
<Analyzer>
54125420
<Id>RCS1176</Id>
54135421
<Identifier>UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious</Identifier>
5422+
<Status>Obsolete</Status>
5423+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
54145424
<Title>Use 'var' instead of explicit type (when the type is not obvious)</Title>
54155425
<MessageFormat>Use 'var' instead of explicit type</MessageFormat>
54165426
<DefaultSeverity>Hidden</DefaultSeverity>
@@ -5425,6 +5435,8 @@ else
54255435
<Analyzer>
54265436
<Id>RCS1177</Id>
54275437
<Identifier>UseVarInsteadOfExplicitTypeInForEach</Identifier>
5438+
<Status>Obsolete</Status>
5439+
<ObsoleteMessage>Use RCS1264 instead</ObsoleteMessage>
54285440
<Title>Use 'var' instead of explicit type (in foreach)</Title>
54295441
<MessageFormat>Use 'var' instead of explicit type</MessageFormat>
54305442
<DefaultSeverity>Hidden</DefaultSeverity>
@@ -7633,6 +7645,17 @@ public string Foo(string bar)
76337645
</Sample>
76347646
</Samples>
76357647
</Analyzer>
7648+
<Analyzer>
7649+
<Id>RCS1264</Id>
7650+
<Identifier>UseVarOrExplicitType</Identifier>
7651+
<Title>Use 'var' or explicit type</Title>
7652+
<MessageFormat>{0}</MessageFormat>
7653+
<DefaultSeverity>Info</DefaultSeverity>
7654+
<IsEnabledByDefault>false</IsEnabledByDefault>
7655+
<ConfigOptions>
7656+
<Option Key="use_var" IsRequired="true" />
7657+
</ConfigOptions>
7658+
</Analyzer>
76367659
<Analyzer>
76377660
<Id>RCS9001</Id>
76387661
<Identifier>UsePatternMatching</Identifier>

src/Analyzers/CSharp/Analysis/UseExplicitTypeInsteadOfVarInForEachAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseExplicitTypeInsteadOfVarInForEachAnalyzer : BaseDiagnosticAnalyzer
1315
{

src/Analyzers/CSharp/Analysis/UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseExplicitTypeInsteadOfVarWhenTypeIsNotObviousAnalyzer : BaseDiagnosticAnalyzer
1315
{

src/Analyzers/CSharp/Analysis/UseExplicitTypeInsteadOfVarWhenTypeIsObviousAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseExplicitTypeInsteadOfVarWhenTypeIsObviousAnalyzer : BaseDiagnosticAnalyzer
1315
{

src/Analyzers/CSharp/Analysis/UseVarInsteadOfExplicitTypeInForEachAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseVarInsteadOfExplicitTypeInForEachAnalyzer : BaseDiagnosticAnalyzer
1315
{

src/Analyzers/CSharp/Analysis/UseVarInsteadOfExplicitTypeWhenTypeIsNotObviousAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseVarInsteadOfExplicitTypeWhenTypeIsNotObviousAnalyzer : BaseDiagnosticAnalyzer
1315
{

src/Analyzers/CSharp/Analysis/UseVarInsteadOfExplicitTypeWhenTypeIsObviousAnalyzer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System;
34
using System.Collections.Immutable;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
@@ -8,6 +9,7 @@
89

910
namespace Roslynator.CSharp.Analysis;
1011

12+
[Obsolete("Use analyzer 'UseVarOrExplicitTypeAnalyzer' instead.")]
1113
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1214
public sealed class UseVarInsteadOfExplicitTypeWhenTypeIsObviousAnalyzer : BaseDiagnosticAnalyzer
1315
{

0 commit comments

Comments
 (0)