Skip to content

Commit 1d22cdb

Browse files
Update SA1649CodeFixProvider to remove linked files before re-adding them (to not end up with two identical files in those projects) and also to re-add files with correct path (to not get a copy where there was a link before)
1 parent 51c772e commit 1d22cdb

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/SA1649CodeFixProvider.cs

+16-10
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,30 @@ private static async Task<Solution> GetTransformedSolutionAsync(Document documen
5353
{
5454
var solution = document.Project.Solution;
5555
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
56-
5756
var expectedFileName = diagnostic.Properties[SA1649FileNameMustMatchTypeName.ExpectedFileNameKey];
58-
var newPath = document.FilePath != null ? Path.Combine(Path.GetDirectoryName(document.FilePath), expectedFileName) : null;
59-
60-
var newDocumentId = DocumentId.CreateNewId(document.Id.ProjectId);
6157

62-
var newSolution = solution
63-
.RemoveDocument(document.Id)
64-
.AddDocument(newDocumentId, expectedFileName, syntaxRoot, document.Folders, newPath);
58+
var newSolution = ReplaceDocument(solution, document, document.Id, syntaxRoot, expectedFileName);
6559

66-
// Make sure to also add the file to linked projects
60+
// Make sure to also replace the file in linked projects
6761
foreach (var linkedDocumentId in document.GetLinkedDocumentIds())
6862
{
69-
DocumentId linkedExtractedDocumentId = DocumentId.CreateNewId(linkedDocumentId.ProjectId);
70-
newSolution = newSolution.AddDocument(linkedExtractedDocumentId, expectedFileName, syntaxRoot, document.Folders);
63+
newSolution = ReplaceDocument(newSolution, null, linkedDocumentId, syntaxRoot, expectedFileName);
7164
}
7265

7366
return newSolution;
7467
}
68+
69+
private static Solution ReplaceDocument(Solution solution, Document document, DocumentId documentId, SyntaxNode syntaxRoot, string expectedFileName)
70+
{
71+
document ??= solution.GetDocument(documentId);
72+
73+
var newDocumentFilePath = document.FilePath != null ? Path.Combine(Path.GetDirectoryName(document.FilePath), expectedFileName) : null;
74+
var newDocumentId = DocumentId.CreateNewId(documentId.ProjectId);
75+
76+
var newSolution = solution
77+
.RemoveDocument(documentId)
78+
.AddDocument(newDocumentId, expectedFileName, syntaxRoot, document.Folders, newDocumentFilePath);
79+
return newSolution;
80+
}
7581
}
7682
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1649UnitTests.cs

+63
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace StyleCop.Analyzers.Test.DocumentationRules
77
{
8+
using System.IO;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Microsoft.CodeAnalysis.Testing;
@@ -487,6 +488,59 @@ public class Class2
487488
await VerifyCSharpDiagnosticAsync("Class1.cs", testCode, testSettings: null, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
488489
}
489490

491+
[Fact]
492+
[WorkItem(1693, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1693")]
493+
[WorkItem(1665, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1665")]
494+
public async Task VerifyWithLinkedFileAsync()
495+
{
496+
var dirName = "0";
497+
var testCode = "public class [|Type1|] { }";
498+
499+
await new StyleCopCodeFixVerifier<SA1649FileNameMustMatchTypeName, SA1649CodeFixProvider>.CSharpTest()
500+
{
501+
TestState =
502+
{
503+
Sources =
504+
{
505+
(BuildPath(dirName, "TestFile.cs"), testCode),
506+
},
507+
AdditionalProjects =
508+
{
509+
["Project2"] =
510+
{
511+
Sources =
512+
{
513+
(BuildPath(dirName, "TestFile.cs"), testCode),
514+
},
515+
},
516+
},
517+
},
518+
FixedState =
519+
{
520+
Sources =
521+
{
522+
(BuildPath(dirName, "Type1.cs"), testCode),
523+
},
524+
AdditionalProjects =
525+
{
526+
["Project2"] =
527+
{
528+
Sources =
529+
{
530+
(BuildPath(dirName, "Type1.cs"), testCode),
531+
},
532+
},
533+
},
534+
},
535+
536+
// Fails without this. Hard to be sure why this is needed, but one guess could be
537+
// that the test framework does not respect the fact that both projects point to
538+
// the same file, and only inserts '#pragma warning disable' in the primary project's file.
539+
// Then we would still get a diagnostic in the additional project.
540+
TestBehaviors = TestBehaviors.SkipSuppressionCheck,
541+
}.RunAsync().ConfigureAwait(false);
542+
}
543+
490544
protected static string GetTypeDeclaration(string typeKind, string typeName, int? diagnosticKey = null)
491545
{
492546
if (diagnosticKey is not null)
@@ -550,5 +604,14 @@ protected static Task VerifyCSharpFixAsync(string oldFileName, string source, st
550604
test.ExpectedDiagnostics.AddRange(expected);
551605
return test.RunAsync(cancellationToken);
552606
}
607+
608+
// NOTE: Added to simplify the tests. After the fix has executed,
609+
// the file paths will contain backslashes when running tests on Windows.
610+
// Not really needed when setting up the test state, but handy in the fixed state.
611+
// Might make tests pass on Linux if anyone is developing there.
612+
private static string BuildPath(string part1, string part2)
613+
{
614+
return Path.Combine(part1, part2);
615+
}
553616
}
554617
}

0 commit comments

Comments
 (0)