Skip to content

Commit 6a40c02

Browse files
committed
Merge pull request dotnet#207 from srivatsn/fixcoby
Introduce local\global classification for rules based on analyzers
2 parents 97a3e82 + 4260784 commit 6a40c02

19 files changed

+536
-57
lines changed

src/CodeFormatter/App.config

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,34 @@
3737
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral" />
3838
<bindingRedirect oldVersion="0.0.0.0-0.7.0.0" newVersion="0.7.0.0" />
3939
</dependentAssembly>
40+
<dependentAssembly>
41+
<assemblyIdentity name="Microsoft.Build.Engine" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
42+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
43+
</dependentAssembly>
44+
<dependentAssembly>
45+
<assemblyIdentity name="Microsoft.Build" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
46+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
47+
</dependentAssembly>
48+
<dependentAssembly>
49+
<assemblyIdentity name="Microsoft.Build.Conversion.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
50+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
51+
</dependentAssembly>
52+
<dependentAssembly>
53+
<assemblyIdentity name="Microsoft.Build.Tasks.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
54+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
55+
</dependentAssembly>
56+
<dependentAssembly>
57+
<assemblyIdentity name="Microsoft.Build.Utilities.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
58+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
59+
</dependentAssembly>
60+
<dependentAssembly>
61+
<assemblyIdentity name="Microsoft.CompactFramework.Build.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
62+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="9.0.0.0"/>
63+
</dependentAssembly>
64+
<dependentAssembly>
65+
<assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
66+
<bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="14.0.0.0"/>
67+
</dependentAssembly>
4068
</assemblyBinding>
4169
</runtime>
4270
</configuration>

src/Microsoft.DotNet.CodeFormatter.Analyzers.Tests/Microsoft.DotNet.CodeFormatter.Analyzers.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="AnalyzerFixerTestBase.cs" />
8989
<Compile Include="ExplicitThisAnalyzerTests.cs" />
9090
<Compile Include="OptimizeNamespaceImportsTests.cs" />
91+
<Compile Include="PlaceImportsOutsideNamespaceTests.cs" />
9192
<Compile Include="UnwrittenWritableFieldAnalyzerTests.cs" />
9293
<Compile Include="ProvideExplicitVariableTypeAnalyzerTests.cs" />
9394
</ItemGroup>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.CodeAnalysis.Options;
5+
6+
using Xunit;
7+
8+
namespace Microsoft.DotNet.CodeFormatter.Analyzers.Tests
9+
{
10+
public sealed class PlaceImportsOutsideNamespaceTests : AnalyzerFixerTestBase
11+
{
12+
public PlaceImportsOutsideNamespaceTests()
13+
{
14+
OptionsHelper.GetPropertiesImplementation = (analyzerOptions) =>
15+
{
16+
PropertyBag properties = CreatePolicyThatDisablesAllAnalysis();
17+
properties.SetProperty(OptionsHelper.BuildDefaultEnabledProperty(PlaceImportsOutsideNamespaceAnalyzer.AnalyzerName), true);
18+
return properties;
19+
};
20+
}
21+
22+
[Fact]
23+
public void PlaceImportsOutsideNamespace_Simple()
24+
{
25+
string input = @"
26+
namespace N1
27+
{
28+
using System.Runtime.InteropServices;
29+
using AnotherUnreferencedNamespace;
30+
using System.Threading;
31+
using System;
32+
using System.IO;
33+
using System.Xml;
34+
35+
public class Test
36+
{
37+
public static void Main()
38+
{
39+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
40+
}
41+
}
42+
}
43+
";
44+
45+
string expected = @"
46+
using System.Runtime.InteropServices;
47+
using System.Threading;
48+
using System;
49+
using System.IO;
50+
51+
namespace N1
52+
{
53+
using AnotherUnreferencedNamespace;
54+
using System.Xml;
55+
56+
public class Test
57+
{
58+
public static void Main()
59+
{
60+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
61+
}
62+
}
63+
}
64+
";
65+
Verify(input, expected, runFormatter: false);
66+
}
67+
68+
[Fact]
69+
public void PlaceImportsOutsideNamespace_AddToExisting()
70+
{
71+
string input = @"
72+
using System.Runtime.InteropServices;
73+
74+
namespace N1
75+
{
76+
using System;
77+
using System.IO;
78+
79+
public class Test
80+
{
81+
public static void Main()
82+
{
83+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
84+
}
85+
}
86+
}
87+
";
88+
89+
string expected = @"
90+
using System.Runtime.InteropServices;
91+
using System;
92+
using System.IO;
93+
94+
namespace N1
95+
{
96+
public class Test
97+
{
98+
public static void Main()
99+
{
100+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
101+
}
102+
}
103+
}
104+
";
105+
Verify(input, expected, runFormatter: false);
106+
}
107+
108+
[Fact]
109+
public void PlaceImportsOutsideNamespace_Trivia()
110+
{
111+
string input = @"
112+
// Copyright Header
113+
114+
namespace N1
115+
{
116+
using System;
117+
using System.IO; //Some comments
118+
119+
public class Test
120+
{
121+
public static void Main()
122+
{
123+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
124+
}
125+
}
126+
}
127+
";
128+
129+
string expected = @"
130+
// Copyright Header
131+
132+
using System;
133+
using System.IO; //Some comments
134+
135+
namespace N1
136+
{
137+
public class Test
138+
{
139+
public static void Main()
140+
{
141+
Console.WriteLine(""Calling Console.WriteLine, a dependency on System namespace."");
142+
}
143+
}
144+
}
145+
";
146+
Verify(input, expected, runFormatter: false);
147+
}
148+
}
149+
}

src/Microsoft.DotNet.CodeFormatter.Analyzers/AnalyzerIds.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ internal static class AnalyzerIds
1010
public const string OrderModifiers = "DNS1002";
1111
public const string OptimizeNamespaceImports = "DNS1003";
1212
public const string ProvideExplicitVariableType = "DNS1004";
13+
public const string PlaceImportsOutsideNamespace = "DNS1005";
1314
}
1415
}

src/Microsoft.DotNet.CodeFormatter.Analyzers/ExplicitThisAnalyzer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public class ExplicitThisAnalyzer : DiagnosticAnalyzer
2323
ResourceHelper.MakeLocalizableString(nameof(Resources.ExplicitThisAnalyzer_MessageFormat)),
2424
"Style",
2525
DiagnosticSeverity.Warning,
26-
true);
26+
true,
27+
customTags: RuleType.LocalSemantic);
2728

2829
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
2930
=> ImmutableArray.Create(s_rule);

src/Microsoft.DotNet.CodeFormatter.Analyzers/Microsoft.DotNet.CodeFormatter.Analyzers.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
<Compile Include="OptimizeNamespaceImportsFixer.cs" />
7878
<Compile Include="OptimizeNamespaceImportsOptions.cs" />
7979
<Compile Include="OptionsHelper.cs" />
80+
<Compile Include="PlaceImportsOutsideNamespaceAnalyzer.cs" />
81+
<Compile Include="PlaceImportsOutsideNamespaceFixer.cs" />
8082
<Compile Include="Resources.Designer.cs">
8183
<AutoGen>True</AutoGen>
8284
<DesignTime>True</DesignTime>
Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Collections.Immutable;
67
using System.Composition;
78
using System.Linq;
89

910
using Microsoft.CodeAnalysis;
1011
using Microsoft.CodeAnalysis.CSharp;
12+
using Microsoft.CodeAnalysis.CSharp.Syntax;
1113
using Microsoft.CodeAnalysis.Diagnostics;
1214
using Microsoft.CodeAnalysis.Options;
1315

@@ -23,7 +25,8 @@ public class OptimizeNamespaceImportsAnalyzer : DiagnosticAnalyzer
2325
ResourceHelper.MakeLocalizableString(nameof(Resources.OptimizeNamespaceImportsAnalyzer_MessageFormat)),
2426
"Style",
2527
DiagnosticSeverity.Warning,
26-
true);
28+
true,
29+
customTags: RuleType.GlobalSemantic);
2730

2831
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
2932
=> ImmutableArray.Create(s_rule);
@@ -32,12 +35,9 @@ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
3235

3336
public override void Initialize(AnalysisContext context)
3437
{
35-
context.RegisterSemanticModelAction(semanticModelAnalysisContext =>
38+
context.RegisterCompilationStartAction(compilationContext =>
3639
{
37-
SyntaxNode root;
38-
SemanticModel semanticModel;
39-
40-
PropertyBag properties = OptionsHelper.GetProperties(semanticModelAnalysisContext.Options);
40+
PropertyBag properties = OptionsHelper.GetProperties(compilationContext.Options);
4141

4242
if (!properties.GetProperty(
4343
OptionsHelper.BuildDefaultEnabledProperty(OptimizeNamespaceImportsAnalyzer.AnalyzerName)))
@@ -46,40 +46,43 @@ public override void Initialize(AnalysisContext context)
4646
return;
4747
}
4848

49-
if (!properties.GetProperty(OptimizeNamespaceImportsOptions.RemoveUnnecessaryImports))
49+
if (properties.GetProperty(OptimizeNamespaceImportsOptions.RemoveUnnecessaryImports))
5050
{
51-
// All currently implemented analyzer behaviors are disabled,
52-
// and so therefore have no work to do.
53-
return;
51+
context.RegisterSemanticModelAction(LookForUnusedImports);
5452
}
53+
});
54+
}
5555

56-
semanticModel = semanticModelAnalysisContext.SemanticModel;
57-
root = semanticModel.SyntaxTree.GetRoot();
56+
private static void LookForUnusedImports(SemanticModelAnalysisContext semanticModelAnalysisContext)
57+
{
58+
SyntaxNode root;
59+
SemanticModel semanticModel;
5860

59-
// If we encounter any conditionally included code, we cannot be sure
60-
// that unused namespaces might not be relevant for some other compilation
61-
if (root.DescendantTrivia().Any(x => x.Kind() == SyntaxKind.IfDirectiveTrivia))
62-
return;
61+
semanticModel = semanticModelAnalysisContext.SemanticModel;
62+
root = semanticModel.SyntaxTree.GetRoot();
6363

64-
var diagnostics = semanticModel.GetDiagnostics(null, semanticModelAnalysisContext.CancellationToken);
65-
Diagnostic firstDiagnostic = null;
66-
var locations = new List<Location>();
64+
// If we encounter any conditionally included code, we cannot be sure
65+
// that unused namespaces might not be relevant for some other compilation
66+
if (root.DescendantTrivia().Any(x => x.Kind() == SyntaxKind.IfDirectiveTrivia))
67+
return;
6768

68-
foreach (Diagnostic diagnostic in diagnostics)
69-
{
70-
if (diagnostic.Id == "CS8019")
71-
{
72-
firstDiagnostic = firstDiagnostic ?? diagnostic;
73-
locations.Add(diagnostic.Location);
74-
}
75-
}
76-
77-
if (locations.Count > 0)
69+
var diagnostics = semanticModel.GetDiagnostics(null, semanticModelAnalysisContext.CancellationToken);
70+
Diagnostic firstDiagnostic = null;
71+
var locations = new List<Location>();
72+
73+
foreach (Diagnostic diagnostic in diagnostics)
74+
{
75+
if (diagnostic.Id == "CS8019")
7876
{
79-
semanticModelAnalysisContext.ReportDiagnostic(
80-
Diagnostic.Create(s_rule, firstDiagnostic.Location, locations));
81-
}
82-
});
77+
firstDiagnostic = firstDiagnostic ?? diagnostic;
78+
locations.Add(diagnostic.Location);
79+
}
80+
}
81+
82+
if (locations.Count > 0)
83+
{
84+
semanticModelAnalysisContext.ReportDiagnostic(Diagnostic.Create(s_rule, firstDiagnostic.Location, locations));
85+
}
8386
}
8487
}
8588
}

src/Microsoft.DotNet.CodeFormatter.Analyzers/OptimizeNamespaceImportsFixer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Collections.Immutable;
67
using System.Diagnostics;
@@ -10,6 +11,8 @@
1011
using Microsoft.CodeAnalysis;
1112
using Microsoft.CodeAnalysis.CodeActions;
1213
using Microsoft.CodeAnalysis.CodeFixes;
14+
using Microsoft.CodeAnalysis.CSharp.Syntax;
15+
using Microsoft.CodeAnalysis.Editing;
1316

1417
namespace Microsoft.DotNet.CodeFormatter.Analyzers
1518
{
@@ -32,6 +35,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
3235
usingDirectiveNodes.Add(usingDirectiveNode);
3336
}
3437

38+
3539
context.RegisterCodeFix(
3640
CodeAction.Create(
3741
Resources.OptimizeNamespaceImportsFixer_Title,

src/Microsoft.DotNet.CodeFormatter.Analyzers/OptimizeNamespaceImportsOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public IEnumerable<IOption> GetOptions()
3737
/// <summary>
3838
/// Place import directives outside namespace declaration (or within if value is false).
3939
/// </summary>
40-
public static PerLanguageOption<bool> PlaceImportsOutsideNamespaceDeclaration { get; } = new PerLanguageOption<bool>(OptimizeNamespaceImportsAnalyzer.AnalyzerName, nameof(PlaceImportsOutsideNamespaceDeclaration), defaultValue: true);
40+
public static PerLanguageOption<bool> PlaceImportsOutsideNamespaceDeclaration { get; } = new PerLanguageOption<bool>(PlaceImportsOutsideNamespaceAnalyzer.AnalyzerName, nameof(PlaceImportsOutsideNamespaceDeclaration), defaultValue: true);
4141

4242
/// <summary>
4343
/// Place system namespaces first when writing import directives.

0 commit comments

Comments
 (0)