Skip to content

Commit 9b14293

Browse files
committed
Merge remote-tracking branch 'DotNetAnalyzers/stabilization'
2 parents 72da7cb + 1d2ac6e commit 9b14293

6 files changed

Lines changed: 177 additions & 21 deletions

File tree

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ namespace StyleCop.Analyzers.OrderingRules
2727
internal sealed class UsingCodeFixProvider : CodeFixProvider
2828
{
2929
private static readonly List<UsingDirectiveSyntax> EmptyUsingsList = new List<UsingDirectiveSyntax>();
30-
private static readonly SyntaxAnnotation UsingCodeFixAnnotation = new SyntaxAnnotation(nameof(UsingCodeFixProvider));
30+
private static readonly SyntaxAnnotation UsingCodeFixAnnotation = new SyntaxAnnotation(nameof(UsingCodeFixAnnotation));
31+
private static readonly SyntaxAnnotation FileHeaderStrippedAnnotation = new SyntaxAnnotation(nameof(FileHeaderStrippedAnnotation));
3132

3233
/// <inheritdoc/>
3334
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
@@ -175,6 +176,13 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
175176

176177
private static SyntaxNode ReAddFileHeader(SyntaxNode syntaxRoot, SyntaxNode newSyntaxRoot)
177178
{
179+
// Only re-add the file header if it was stripped.
180+
var usingDirectives = newSyntaxRoot.GetAnnotatedNodes(FileHeaderStrippedAnnotation);
181+
if (!usingDirectives.Any())
182+
{
183+
return newSyntaxRoot;
184+
}
185+
178186
var oldFirstToken = syntaxRoot.GetFirstToken();
179187
if (!oldFirstToken.HasLeadingTrivia)
180188
{
@@ -584,12 +592,6 @@ internal static List<SyntaxTrivia> GetFileHeader(SyntaxTriviaList newLeadingTriv
584592
return hasHeader ? fileHeader : new List<SyntaxTrivia>();
585593
}
586594

587-
private static List<SyntaxTrivia> StripFileHeader(SyntaxTriviaList leadingTrivia)
588-
{
589-
var fileHeader = GetFileHeader(leadingTrivia);
590-
return leadingTrivia.Skip(fileHeader.Count).ToList();
591-
}
592-
593595
private List<UsingDirectiveSyntax> GenerateUsings(Dictionary<DirectiveSpan, List<UsingDirectiveSyntax>> usingsGroup, DirectiveSpan directiveSpan, string indentation, List<SyntaxTrivia> triviaToMove, bool qualifyNames)
594596
{
595597
List<UsingDirectiveSyntax> result = new List<UsingDirectiveSyntax>();
@@ -614,15 +616,29 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
614616

615617
for (var i = 0; i < usingsList.Count; i++)
616618
{
619+
bool strippedFileHeader = false;
617620
var currentUsing = usingsList[i];
618621

619622
if (qualifyNames)
620623
{
621624
currentUsing = this.QualifyUsingDirective(currentUsing);
622625
}
623626

627+
// strip the file header, if the using is the first node in the source file.
628+
List<SyntaxTrivia> leadingTrivia;
629+
if ((i == 0) && currentUsing.GetFirstToken().GetPreviousToken().IsMissingOrDefault())
630+
{
631+
var trivia = currentUsing.GetLeadingTrivia();
632+
var fileHeader = GetFileHeader(trivia);
633+
leadingTrivia = trivia.Skip(fileHeader.Count).ToList();
634+
strippedFileHeader = fileHeader.Count > 0;
635+
}
636+
else
637+
{
638+
leadingTrivia = currentUsing.GetLeadingTrivia().ToList();
639+
}
640+
624641
// when there is a directive trivia, add it (and any trivia before it) to the triviaToMove collection.
625-
var leadingTrivia = (i == 0) ? StripFileHeader(currentUsing.GetLeadingTrivia()) : currentUsing.GetLeadingTrivia().ToList();
626642
for (var m = leadingTrivia.Count - 1; m >= 0; m--)
627643
{
628644
if (leadingTrivia[m].IsDirective)
@@ -689,6 +705,11 @@ private List<UsingDirectiveSyntax> GenerateUsings(List<UsingDirectiveSyntax> usi
689705
.WithTrailingTrivia(newTrailingTrivia)
690706
.WithAdditionalAnnotations(UsingCodeFixAnnotation);
691707

708+
if (strippedFileHeader)
709+
{
710+
processedUsing = processedUsing.WithAdditionalAnnotations(FileHeaderStrippedAnnotation);
711+
}
712+
692713
// filter duplicate using declarations, preferring to keep the one with an alias
693714
var existingUsing = result.Find(u => string.Equals(u.Name.ToNormalizedString(), processedUsing.Name.ToNormalizedString(), StringComparison.Ordinal));
694715
if (existingUsing != null)

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
//
3636
// You can specify all the values or you can default the Build and Revision Numbers
3737
// by using the '*' as shown below:
38-
[assembly: AssemblyVersion("1.0.0.29")]
39-
[assembly: AssemblyFileVersion("1.0.0.29")]
38+
[assembly: AssemblyVersion("1.0.0.30")]
39+
[assembly: AssemblyFileVersion("1.0.0.30")]
4040
[assembly: AssemblyInformationalVersion("1.0.0-dev")]
4141

4242
[assembly: InternalsVisibleTo("StyleCop.Analyzers.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c36d40d996fcc95fb6a89754728616758f459026e31478ce93633b3e27a4af416f103aa3d7a9e7998f829f8715cc1240d30724fd662042550fa71357b19562622424267e9e4640c403edbe64709a9ca5918128a9b9020b0db6e770d0dd1eac888869c23a835b74bde00e171984b1d1c24636cf030f0b23106e73035a2be145a6")]

StyleCop.Analyzers/StyleCop.Analyzers.Test/OrderingRules/SA1210UnitTests.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,101 @@ public async Task TestPreprocessorDirectivesAsync()
317317
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
318318
}
319319

320+
/// <summary>
321+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#1897.
322+
/// </summary>
323+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
324+
[Fact]
325+
public async Task TestInvalidOrderedUsingDirectivesInNamespaceDeclarationWithFileHeaderAsync()
326+
{
327+
var testCode = @"// <copyright file=""VoiceCommandService.cs"" company=""Foo Corporation"">
328+
// Copyright (c) FooCorporation. All rights reserved.
329+
// </copyright>
330+
331+
namespace Foo.Garage.XYZ
332+
{
333+
using System;
334+
using Newtonsoft.Json;
335+
using Foo.Garage.XYZ;
336+
}
337+
338+
namespace Newtonsoft.Json
339+
{
340+
}
341+
";
342+
343+
var fixedTestCode = @"// <copyright file=""VoiceCommandService.cs"" company=""Foo Corporation"">
344+
// Copyright (c) FooCorporation. All rights reserved.
345+
// </copyright>
346+
347+
namespace Foo.Garage.XYZ
348+
{
349+
using System;
350+
using Foo.Garage.XYZ;
351+
using Newtonsoft.Json;
352+
}
353+
354+
namespace Newtonsoft.Json
355+
{
356+
}
357+
";
358+
359+
// The same diagnostic is reported multiple times due to a bug in Roslyn 1.0
360+
DiagnosticResult[] expected =
361+
{
362+
this.CSharpDiagnostic().WithLocation(8, 5),
363+
this.CSharpDiagnostic().WithLocation(8, 5),
364+
this.CSharpDiagnostic().WithLocation(8, 5),
365+
};
366+
367+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
368+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
369+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
370+
}
371+
372+
/// <summary>
373+
/// Verifies that the first using statement will preserve its leading comment.
374+
/// </summary>
375+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
376+
[Fact]
377+
public async Task TestLeadingCommentForFirstUsingInNamespaceIsPreservedAsync()
378+
{
379+
var testCode = @"namespace TestNamespace
380+
{
381+
// With test comment
382+
using System;
383+
using TestNamespace;
384+
using Newtonsoft.Json;
385+
}
386+
387+
namespace Newtonsoft.Json
388+
{
389+
}
390+
";
391+
392+
var fixedTestCode = @"namespace TestNamespace
393+
{
394+
// With test comment
395+
using System;
396+
using Newtonsoft.Json;
397+
using TestNamespace;
398+
}
399+
400+
namespace Newtonsoft.Json
401+
{
402+
}
403+
";
404+
405+
DiagnosticResult[] expected =
406+
{
407+
this.CSharpDiagnostic().WithLocation(5, 5),
408+
};
409+
410+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
411+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
412+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
413+
}
414+
320415
/// <inheritdoc/>
321416
protected override IEnumerable<string> GetDisabledDiagnostics()
322417
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1115UnitTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,31 @@ public void Baz()
179179
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
180180
}
181181

182+
[Fact]
183+
public async Task TestMethodCallPragmaDirectiveBetweenParametersAsync()
184+
{
185+
var testCode = @"
186+
class Foo
187+
{
188+
public void Bar(int i, int z)
189+
{
190+
}
191+
192+
public void Baz()
193+
{
194+
Bar(
195+
#pragma warning disable CS4014
196+
1,
197+
#pragma warning restore CS4014
198+
2);
199+
}
200+
}";
201+
202+
DiagnosticResult[] expected = EmptyDiagnosticResults;
203+
204+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
205+
}
206+
182207
[Fact]
183208
public async Task TestMethodCallSecondParameterOnTheNextLineAsync()
184209
{

StyleCop.Analyzers/StyleCop.Analyzers/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
//
3636
// You can specify all the values or you can default the Build and Revision Numbers
3737
// by using the '*' as shown below:
38-
[assembly: AssemblyVersion("1.0.0.29")]
39-
[assembly: AssemblyFileVersion("1.0.0.29")]
38+
[assembly: AssemblyVersion("1.0.0.30")]
39+
[assembly: AssemblyFileVersion("1.0.0.30")]
4040
[assembly: AssemblyInformationalVersion("1.0.0-dev")]
4141

4242
#if DEVELOPMENT_KEY

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1115ParameterMustFollowComma.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,32 @@ private static void AnalyzeArgumentList(SyntaxNodeAnalysisContext context, BaseA
270270
return;
271271
}
272272

273-
var previousLine = argumentListSyntax.Arguments[0].GetLineSpan().EndLinePosition.Line;
273+
var previousArgumentLine = argumentListSyntax.Arguments[0].GetLineSpan().EndLinePosition.Line;
274274
for (int i = 1; i < argumentListSyntax.Arguments.Count; i++)
275275
{
276276
var currentArgument = argumentListSyntax.Arguments[i];
277-
var lineSpan = currentArgument.GetLineSpan();
278-
var currentLine = lineSpan.StartLinePosition.Line;
279-
if (currentLine - previousLine > 1)
277+
int currentArgumentStartLine;
278+
int currentArgumentEndLine;
279+
280+
if (currentArgument.HasLeadingTrivia && currentArgument.GetLeadingTrivia().All(trivia => IsValidTrivia(trivia)))
281+
{
282+
var lineSpan = currentArgument.SyntaxTree.GetLineSpan(currentArgument.FullSpan);
283+
currentArgumentStartLine = lineSpan.StartLinePosition.Line;
284+
currentArgumentEndLine = lineSpan.EndLinePosition.Line;
285+
}
286+
else
287+
{
288+
var lineSpan = currentArgument.GetLineSpan();
289+
currentArgumentStartLine = lineSpan.StartLinePosition.Line;
290+
currentArgumentEndLine = lineSpan.EndLinePosition.Line;
291+
}
292+
293+
if (currentArgumentStartLine - previousArgumentLine > 1)
280294
{
281295
context.ReportDiagnostic(Diagnostic.Create(Descriptor, currentArgument.GetLocation()));
282296
}
283297

284-
previousLine = lineSpan.EndLinePosition.Line;
298+
previousArgumentLine = currentArgumentEndLine;
285299
}
286300
}
287301

@@ -319,12 +333,13 @@ private static void AnalyzeParameterList(SyntaxNodeAnalysisContext context, Base
319333

320334
private static bool IsValidTrivia(SyntaxTrivia trivia)
321335
{
336+
if (trivia.IsDirective)
337+
{
338+
return true;
339+
}
340+
322341
switch (trivia.Kind())
323342
{
324-
case SyntaxKind.IfDirectiveTrivia:
325-
case SyntaxKind.ElseDirectiveTrivia:
326-
case SyntaxKind.ElifDirectiveTrivia:
327-
case SyntaxKind.EndIfDirectiveTrivia:
328343
case SyntaxKind.DisabledTextTrivia:
329344
case SyntaxKind.WhitespaceTrivia:
330345
return true;

0 commit comments

Comments
 (0)