Skip to content

Commit b76e5d9

Browse files
committed
Merge remote-tracking branch 'DotNetAnalyzers/master' into feature/sa1008-parent-in-collection-expression-3931
2 parents f76adf3 + 40f4cca commit b76e5d9

File tree

131 files changed

+6067
-332
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+6067
-332
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.UsingsSorter.cs

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,39 @@ public List<UsingDirectiveSyntax> GetContainedUsings(TreeTextSpan directiveSpan)
9292
return result;
9393
}
9494

95-
public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(TreeTextSpan directiveSpan, string indentation, bool withLeadingBlankLine, bool withTrailingBlankLine, bool qualifyNames)
95+
public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(TreeTextSpan directiveSpan, string indentation, bool withLeadingBlankLine, bool withTrailingBlankLine, bool qualifyNames, bool includeGlobal, bool includeLocal)
9696
{
9797
var usingList = new List<UsingDirectiveSyntax>();
9898
List<SyntaxTrivia> triviaToMove = new List<SyntaxTrivia>();
99+
int lastGlobalDirective = -1;
99100

100-
usingList.AddRange(this.GenerateUsings(this.systemUsings, directiveSpan, indentation, triviaToMove, qualifyNames));
101-
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, directiveSpan, indentation, triviaToMove, qualifyNames));
102-
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, directiveSpan, indentation, triviaToMove, qualifyNames));
103-
usingList.AddRange(this.GenerateUsings(this.staticImports, directiveSpan, indentation, triviaToMove, qualifyNames));
104-
usingList.AddRange(this.GenerateUsings(this.aliases, directiveSpan, indentation, triviaToMove, qualifyNames));
101+
if (includeGlobal)
102+
{
103+
usingList.AddRange(this.GenerateUsings(this.systemUsings, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: true));
104+
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: true));
105+
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: true));
106+
usingList.AddRange(this.GenerateUsings(this.staticImports, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: true));
107+
usingList.AddRange(this.GenerateUsings(this.aliases, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: true));
108+
lastGlobalDirective = usingList.Count - 1;
109+
}
110+
111+
if (includeLocal)
112+
{
113+
usingList.AddRange(this.GenerateUsings(this.systemUsings, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: false));
114+
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: false));
115+
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: false));
116+
usingList.AddRange(this.GenerateUsings(this.staticImports, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: false));
117+
usingList.AddRange(this.GenerateUsings(this.aliases, directiveSpan, indentation, triviaToMove, qualifyNames, isGlobal: false));
118+
}
119+
120+
if (!this.insertBlankLinesBetweenGroups && lastGlobalDirective >= 0 && lastGlobalDirective < usingList.Count - 1)
121+
{
122+
// Need to ensure there is a blank line after the global usings so they are separated from the local
123+
// usings
124+
var last = usingList[lastGlobalDirective];
125+
126+
usingList[lastGlobalDirective] = last.WithTrailingTrivia(last.GetTrailingTrivia().Add(SyntaxFactory.CarriageReturnLineFeed));
127+
}
105128

106129
if (triviaToMove.Count > 0)
107130
{
@@ -129,11 +152,27 @@ public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(List<UsingDirectiv
129152
var usingList = new List<UsingDirectiveSyntax>();
130153
List<SyntaxTrivia> triviaToMove = new List<SyntaxTrivia>();
131154

132-
usingList.AddRange(this.GenerateUsings(this.systemUsings, usingsList, indentation, triviaToMove, qualifyNames));
133-
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, usingsList, indentation, triviaToMove, qualifyNames));
134-
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, usingsList, indentation, triviaToMove, qualifyNames));
135-
usingList.AddRange(this.GenerateUsings(this.staticImports, usingsList, indentation, triviaToMove, qualifyNames));
136-
usingList.AddRange(this.GenerateUsings(this.aliases, usingsList, indentation, triviaToMove, qualifyNames));
155+
usingList.AddRange(this.GenerateUsings(this.systemUsings, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: true));
156+
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: true));
157+
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: true));
158+
usingList.AddRange(this.GenerateUsings(this.staticImports, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: true));
159+
usingList.AddRange(this.GenerateUsings(this.aliases, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: true));
160+
int lastGlobalDirective = usingList.Count - 1;
161+
162+
usingList.AddRange(this.GenerateUsings(this.systemUsings, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: false));
163+
usingList.AddRange(this.GenerateUsings(this.namespaceUsings, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: false));
164+
usingList.AddRange(this.GenerateUsings(this.systemStaticImports, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: false));
165+
usingList.AddRange(this.GenerateUsings(this.staticImports, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: false));
166+
usingList.AddRange(this.GenerateUsings(this.aliases, usingsList, indentation, triviaToMove, qualifyNames, isGlobal: false));
167+
168+
if (!this.insertBlankLinesBetweenGroups && lastGlobalDirective >= 0 && lastGlobalDirective < usingList.Count - 1)
169+
{
170+
// Need to ensure there is a blank line after the global usings so they are separated from the local
171+
// usings
172+
var last = usingList[lastGlobalDirective];
173+
174+
usingList[lastGlobalDirective] = last.WithTrailingTrivia(last.GetTrailingTrivia().Add(SyntaxFactory.CarriageReturnLineFeed));
175+
}
137176

138177
if (triviaToMove.Count > 0)
139178
{
@@ -156,7 +195,7 @@ public SyntaxList<UsingDirectiveSyntax> GenerateGroupedUsings(List<UsingDirectiv
156195
return SyntaxFactory.List(usingList);
157196
}
158197

159-
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<TreeTextSpan, List<UsingDirectiveSyntax>> usingsGroup, TreeTextSpan directiveSpan, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
198+
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<TreeTextSpan, List<UsingDirectiveSyntax>> usingsGroup, TreeTextSpan directiveSpan, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames, bool isGlobal)
160199
{
161200
List<UsingDirectiveSyntax> result = new List<UsingDirectiveSyntax>();
162201
List<UsingDirectiveSyntax> usingsList;
@@ -166,10 +205,10 @@ private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<TreeTextSpan, List<
166205
return result;
167206
}
168207

169-
return this.GenerateUsings(usingsList, indentation, triviaToMove, qualifyNames);
208+
return this.GenerateUsings(usingsList, indentation, triviaToMove, qualifyNames, isGlobal);
170209
}
171210

172-
private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usingsList, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
211+
private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usingsList, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames, bool isGlobal)
173212
{
174213
List<UsingDirectiveSyntax> result = new List<UsingDirectiveSyntax>();
175214

@@ -181,6 +220,10 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
181220
for (var i = 0; i < usingsList.Count; i++)
182221
{
183222
var currentUsing = usingsList[i];
223+
if (currentUsing.GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword) != isGlobal)
224+
{
225+
continue;
226+
}
184227

185228
// strip the file header, if the using is the first node in the source file.
186229
List<SyntaxTrivia> leadingTrivia;
@@ -335,7 +378,7 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
335378

336379
result.Sort(this.CompareUsings);
337380

338-
if (this.insertBlankLinesBetweenGroups)
381+
if (this.insertBlankLinesBetweenGroups && result.Count > 0)
339382
{
340383
var last = result[result.Count - 1];
341384

@@ -533,11 +576,11 @@ private void AddUsingDirective(Dictionary<TreeTextSpan, List<UsingDirectiveSynta
533576
usingList.Add(usingDirective);
534577
}
535578

536-
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<TreeTextSpan, List<UsingDirectiveSyntax>> usingsGroup, List<UsingDirectiveSyntax> usingsList, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
579+
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<TreeTextSpan, List<UsingDirectiveSyntax>> usingsGroup, List<UsingDirectiveSyntax> usingsList, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames, bool isGlobal)
537580
{
538581
var filteredUsingsList = this.FilterRelevantUsings(usingsGroup, usingsList);
539582

540-
return this.GenerateUsings(filteredUsingsList, indentation, triviaToMove, qualifyNames);
583+
return this.GenerateUsings(filteredUsingsList, indentation, triviaToMove, qualifyNames, isGlobal);
541584
}
542585

543586
private List<UsingDirectiveSyntax> FilterRelevantUsings(Dictionary<TreeTextSpan, List<UsingDirectiveSyntax>> usingsGroup, List<UsingDirectiveSyntax> usingsList)

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,14 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6969
continue;
7070
}
7171

72+
// Force preserving the placement of using directives when we are fixing a diagnostic not directly
73+
// related to placement of using directives inside/outside namespaces.
74+
bool forcePreservePlacement = !isSA1200;
75+
7276
context.RegisterCodeFix(
7377
CodeAction.Create(
7478
OrderingResources.UsingCodeFix,
75-
cancellationToken => GetTransformedDocumentAsync(context.Document, syntaxRoot, !isSA1200, cancellationToken),
79+
cancellationToken => GetTransformedDocumentAsync(context.Document, syntaxRoot, forcePreservePlacement, cancellationToken),
7680
nameof(UsingCodeFixProvider)),
7781
diagnostic);
7882
}
@@ -117,9 +121,11 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
117121
{
118122
newSyntaxRoot = AddUsingsToNamespace(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
119123
}
120-
else if (usingDirectivesPlacement == UsingDirectivesPlacement.OutsideNamespace)
124+
125+
if (usingDirectivesPlacement != UsingDirectivesPlacement.Preserve)
121126
{
122-
newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, usingsIndentation, replaceMap.Any());
127+
bool onlyGlobal = usingDirectivesPlacement == UsingDirectivesPlacement.InsideNamespace;
128+
newSyntaxRoot = AddUsingsToCompilationRoot(newSyntaxRoot, usingsHelper, replaceMap.Any(), onlyGlobal);
123129
}
124130

125131
// Final cleanup
@@ -257,7 +263,7 @@ private static void BuildReplaceMapForConditionalDirectives(UsingsSorter usingsH
257263

258264
var indentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationSteps);
259265

260-
var modifiedUsings = usingsHelper.GenerateGroupedUsings(childSpan, indentation, false, false, qualifyNames: false);
266+
var modifiedUsings = usingsHelper.GenerateGroupedUsings(childSpan, indentation, false, false, qualifyNames: false, includeGlobal: true, includeLocal: true);
261267

262268
for (var i = 0; i < originalUsings.Count; i++)
263269
{
@@ -280,7 +286,7 @@ private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsS
280286
var withLeadingBlankLine = rootNamespace.SyntaxNode.IsKind(SyntaxKindEx.FileScopedNamespaceDeclaration);
281287
var withTrailingBlankLine = hasConditionalDirectives || rootNamespace.Members.Any() || rootNamespace.Externs.Any();
282288

283-
var groupedUsings = usingsHelper.GenerateGroupedUsings(TreeTextSpan.Empty, usingsIndentation, withLeadingBlankLine, withTrailingBlankLine, qualifyNames: false);
289+
var groupedUsings = usingsHelper.GenerateGroupedUsings(TreeTextSpan.Empty, usingsIndentation, withLeadingBlankLine, withTrailingBlankLine, qualifyNames: false, includeGlobal: false, includeLocal: true);
284290
groupedUsings = groupedUsings.AddRange(rootNamespace.Usings);
285291

286292
var newRootNamespace = rootNamespace.WithUsings(groupedUsings);
@@ -289,12 +295,12 @@ private static SyntaxNode AddUsingsToNamespace(SyntaxNode newSyntaxRoot, UsingsS
289295
return newSyntaxRoot;
290296
}
291297

292-
private static SyntaxNode AddUsingsToCompilationRoot(SyntaxNode newSyntaxRoot, UsingsSorter usingsHelper, string usingsIndentation, bool hasConditionalDirectives)
298+
private static SyntaxNode AddUsingsToCompilationRoot(SyntaxNode newSyntaxRoot, UsingsSorter usingsHelper, bool hasConditionalDirectives, bool onlyGlobal)
293299
{
294300
var newCompilationUnit = (CompilationUnitSyntax)newSyntaxRoot;
295301
var withTrailingBlankLine = hasConditionalDirectives || newCompilationUnit.AttributeLists.Any() || newCompilationUnit.Members.Any() || newCompilationUnit.Externs.Any();
296302

297-
var groupedUsings = usingsHelper.GenerateGroupedUsings(TreeTextSpan.Empty, usingsIndentation, withLeadingBlankLine: false, withTrailingBlankLine, qualifyNames: true);
303+
var groupedUsings = usingsHelper.GenerateGroupedUsings(TreeTextSpan.Empty, indentation: string.Empty, withLeadingBlankLine: false, withTrailingBlankLine, qualifyNames: true, includeGlobal: true, includeLocal: !onlyGlobal);
298304
groupedUsings = groupedUsings.AddRange(newCompilationUnit.Usings);
299305
newSyntaxRoot = newCompilationUnit.WithUsings(groupedUsings);
300306

@@ -513,9 +519,12 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
513519
return null;
514520
}
515521

522+
var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
523+
524+
// Force preserving the placement of using directives when we are fixing any diagnostic not directly
525+
// related to placement of using directives inside/outside namespaces.
516526
var forcePreserve = diagnostics.All(d => d.Id != SA1200UsingDirectivesMustBePlacedCorrectly.DiagnosticId);
517527

518-
var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
519528
Document newDocument = await GetTransformedDocumentAsync(document, syntaxRoot, forcePreserve, fixAllContext.CancellationToken).ConfigureAwait(false);
520529
return await newDocument.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
521530
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.PrivateCodeFixes;
5+
6+
using System.IO;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Analyzer.Utilities;
10+
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.CodeActions;
12+
using Microsoft.CodeAnalysis.CodeRefactorings;
13+
using Microsoft.CodeAnalysis.CSharp;
14+
using Microsoft.CodeAnalysis.CSharp.Syntax;
15+
using Microsoft.CodeAnalysis.Text;
16+
17+
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(GeneratePartCodeRefactoringProvider))]
18+
internal sealed class GeneratePartCodeRefactoringProvider
19+
: CodeRefactoringProvider
20+
{
21+
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
22+
{
23+
var partialType = await context.TryGetRelevantNodeAsync<ClassDeclarationSyntax>(CSharpRefactoringHelpers.Instance).ConfigureAwait(false);
24+
if (partialType is not { Modifiers: var modifiers }
25+
|| !modifiers.Any(SyntaxKind.PartialKeyword))
26+
{
27+
return;
28+
}
29+
30+
context.RegisterRefactoring(CodeAction.Create(
31+
"Generate additional part",
32+
async cancellationToken =>
33+
{
34+
var namespaceDeclaration = partialType.FirstAncestorOrSelf<BaseNamespaceDeclarationSyntax>();
35+
if (namespaceDeclaration is null)
36+
{
37+
return context.Document.Project.Solution;
38+
}
39+
40+
var firstUsing = namespaceDeclaration.Usings.FirstOrDefault()?.Name.ToString();
41+
42+
var namespaceName = namespaceDeclaration.Name.ToString();
43+
var subNamespace = namespaceName;
44+
var rootNamespace = context.Document.Project.DefaultNamespace;
45+
if (!string.IsNullOrEmpty(rootNamespace) && namespaceName.StartsWith(rootNamespace + "."))
46+
{
47+
subNamespace = namespaceName[(rootNamespace.Length + 1)..];
48+
}
49+
50+
var content = $@"// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
51+
// Licensed under the MIT License. See LICENSE in the project root for license information.
52+
53+
namespace {namespaceName};
54+
55+
using {firstUsing};
56+
57+
public partial class {partialType.Identifier.ValueText} {partialType.BaseList}
58+
{{
59+
}}
60+
";
61+
62+
var fileName = partialType.Identifier.ValueText + ".cs";
63+
var directory = Path.GetDirectoryName(context.Document.Project.FilePath)!;
64+
var existingText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false);
65+
66+
var addedDocument = context.Document.Project.AddDocument(
67+
fileName,
68+
SourceText.From(content, new UTF8Encoding(true), existingText.ChecksumAlgorithm),
69+
folders: subNamespace.Split('.'),
70+
filePath: Path.Combine(directory, Path.Combine(subNamespace.Split('.')), fileName));
71+
72+
return addedDocument.Project.Solution;
73+
},
74+
nameof(GeneratePartCodeRefactoringProvider)));
75+
}
76+
}

0 commit comments

Comments
 (0)