Skip to content

Commit 282c5e1

Browse files
Update SA1649CodeFixProvider to first remove the file in other projects which reference the same file before adding them there (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)
#1693 #3866
1 parent ff5c432 commit 282c5e1

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 update other projects which reference the same file
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(3866, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3866")]
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, since the error message is not so good,
537+
// but one guess could be that the test framework does not respect the fact that both projects
538+
// point to 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)