Skip to content

Commit 29bedf0

Browse files
committed
support .stylecop.json configuration file name
1 parent c725402 commit 29bedf0

5 files changed

Lines changed: 84 additions & 16 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Settings/SettingsFileCodeFixProvider.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
6363
var workspace = project.Solution.Workspace;
6464

6565
// check if the settings file already exists
66-
if (project.AdditionalDocuments.Any(IsStyleCopSettingsDocument))
66+
if (project.AdditionalDocuments.Any(document => SettingsHelper.IsStyleCopSettingsFile(document.Name)))
6767
{
6868
return SpecializedTasks.CompletedTask;
6969
}
@@ -94,11 +94,6 @@ public override FixAllProvider GetFixAllProvider()
9494
return null;
9595
}
9696

97-
private static bool IsStyleCopSettingsDocument(TextDocument document)
98-
{
99-
return string.Equals(document.Name, SettingsHelper.SettingsFileName, StringComparison.OrdinalIgnoreCase);
100-
}
101-
10297
private static Task<Solution> GetTransformedSolutionAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
10398
{
10499
var project = document.Project;

StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/DiagnosticVerifier.Helper.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ protected virtual Solution CreateSolution(ProjectId projectId, string language)
179179
if (!string.IsNullOrEmpty(settings))
180180
{
181181
var documentId = DocumentId.CreateNewId(projectId);
182-
solution = solution.AddAdditionalDocument(documentId, SettingsHelper.SettingsFileName, settings);
182+
solution = solution.AddAdditionalDocument(documentId, this.GetSettingsFileName(), settings);
183183
}
184184

185185
ParseOptions parseOptions = solution.GetProject(projectId).ParseOptions;
@@ -204,6 +204,15 @@ protected virtual string GetSettings()
204204
return null;
205205
}
206206

207+
/// <summary>
208+
/// Gets the name of the settings file to use.
209+
/// </summary>
210+
/// <returns>The name of the settings file to use.</returns>
211+
protected virtual string GetSettingsFileName()
212+
{
213+
return SettingsHelper.SettingsFileName;
214+
}
215+
207216
protected DiagnosticResult CSharpDiagnostic(string diagnosticId = null)
208217
{
209218
var analyzers = this.GetCSharpDiagnosticAnalyzers();

StyleCop.Analyzers/StyleCop.Analyzers.Test/Settings/SettingsFileCodeFixProviderUnitTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace NamespaceName
2626
";
2727

2828
private bool createSettingsFile;
29+
private string settingsFileName = SettingsHelper.SettingsFileName;
2930

3031
/// <summary>
3132
/// Verifies that a file without a header, but with leading trivia will produce the correct diagnostic message.
@@ -64,6 +65,21 @@ public async Task TestSettingsFileDoesNotExistAsync()
6465
public async Task TestSettingsFileAlreadyExistsAsync()
6566
{
6667
this.createSettingsFile = true;
68+
this.settingsFileName = SettingsHelper.SettingsFileName;
69+
70+
var offeredFixes = await this.GetOfferedCSharpFixesAsync(TestCode).ConfigureAwait(false);
71+
Assert.Empty(offeredFixes);
72+
}
73+
74+
/// <summary>
75+
/// Verifies that a code fix will not be offered if the settings file is already present.
76+
/// </summary>
77+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
78+
[Fact]
79+
public async Task TestDotPrefixedSettingsFileAlreadyExistsAsync()
80+
{
81+
this.createSettingsFile = true;
82+
this.settingsFileName = $".{SettingsHelper.SettingsFileName}";
6783

6884
var offeredFixes = await this.GetOfferedCSharpFixesAsync(TestCode).ConfigureAwait(false);
6985
Assert.Empty(offeredFixes);
@@ -80,6 +96,12 @@ protected override string GetSettings()
8096
return null;
8197
}
8298

99+
/// <inheritdoc/>
100+
protected override string GetSettingsFileName()
101+
{
102+
return this.settingsFileName;
103+
}
104+
83105
/// <inheritdoc/>
84106
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
85107
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/Settings/SettingsUnitTests.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,25 @@ public async Task VerifySettingsSupportsTrailingCommasAsync()
308308
Assert.Equal("a", styleCopSettings.NamingRules.AllowedHungarianPrefixes[0]);
309309
}
310310

311+
[Fact]
312+
public async Task VerifySettingsFileNameSupportsDotPrefixAsync()
313+
{
314+
var settings = @"
315+
{
316+
""settings"": {
317+
""documentationRules"": {
318+
""companyName"": ""TestCompany"",
319+
},
320+
}
321+
}
322+
";
323+
var context = await CreateAnalysisContextAsync(settings, ".stylecop.json").ConfigureAwait(false);
324+
325+
var styleCopSettings = context.GetStyleCopSettings(CancellationToken.None);
326+
327+
Assert.Equal("TestCompany", styleCopSettings.DocumentationRules.CompanyName);
328+
}
329+
311330
[Fact]
312331
public async Task VerifyInvalidJsonBehaviorAsync()
313332
{
@@ -334,7 +353,7 @@ public async Task VerifyEmptyOrMissingFileAsync()
334353
Assert.Equal("Copyright (c) PlaceholderCompany. All rights reserved.", styleCopSettings.DocumentationRules.GetCopyrightText("unused"));
335354
}
336355

337-
private static async Task<SyntaxTreeAnalysisContext> CreateAnalysisContextAsync(string stylecopJSON)
356+
private static async Task<SyntaxTreeAnalysisContext> CreateAnalysisContextAsync(string stylecopJSON, string settingsFileName = SettingsHelper.SettingsFileName)
338357
{
339358
var projectId = ProjectId.CreateNewId();
340359
var documentId = DocumentId.CreateNewId(projectId);
@@ -347,7 +366,7 @@ private static async Task<SyntaxTreeAnalysisContext> CreateAnalysisContextAsync(
347366
var document = solution.GetDocument(documentId);
348367
var syntaxTree = await document.GetSyntaxTreeAsync().ConfigureAwait(false);
349368

350-
var stylecopJSONFile = new AdditionalTextHelper(SettingsHelper.SettingsFileName, stylecopJSON);
369+
var stylecopJSONFile = new AdditionalTextHelper(settingsFileName, stylecopJSON);
351370
var additionalFiles = ImmutableArray.Create<AdditionalText>(stylecopJSONFile);
352371
var analyzerOptions = new AnalyzerOptions(additionalFiles);
353372

StyleCop.Analyzers/StyleCop.Analyzers/Settings/SettingsHelper.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,32 @@ internal static StyleCopSettings GetStyleCopSettings(this AnalyzerOptions option
6666
return GetStyleCopSettings(options != null ? options.AdditionalFiles : ImmutableArray.Create<AdditionalText>(), failureBehavior, cancellationToken);
6767
}
6868

69+
/// <summary>
70+
/// Gets a value indicating whether the specified path points to a StyleCop settings file (stylecop.json or .stylecop.json).
71+
/// </summary>
72+
/// <param name="path">The path to test.</param>
73+
/// <returns><c>true</c> if <paramref name="path"/> points to a StyleCop settings file; otherwise, <c>false</c>.</returns>
74+
internal static bool IsStyleCopSettingsFile(string path)
75+
{
76+
var fileName = Path.GetFileName(path).ToLowerInvariant();
77+
78+
if (fileName.StartsWith("."))
79+
{
80+
fileName = fileName.Substring(1);
81+
}
82+
83+
return fileName == SettingsFileName;
84+
}
85+
6986
internal static StyleCopSettings GetStyleCopSettings(this AnalysisContext context, AnalyzerOptions options, CancellationToken cancellationToken)
7087
{
7188
return GetStyleCopSettings(context, options, DeserializationFailureBehavior.ReturnDefaultSettings, cancellationToken);
7289
}
7390

7491
internal static StyleCopSettings GetStyleCopSettings(this AnalysisContext context, AnalyzerOptions options, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
7592
{
76-
SourceText text = TryGetStyleCopSettingsText(options, cancellationToken);
93+
string settingsFileName;
94+
SourceText text = TryGetStyleCopSettingsText(options, cancellationToken, out settingsFileName);
7795
if (text == null)
7896
{
7997
return new StyleCopSettings();
@@ -90,7 +108,7 @@ internal static StyleCopSettings GetStyleCopSettings(this AnalysisContext contex
90108
return settings;
91109
}
92110

93-
return GetStyleCopSettings(SettingsFileName, text, failureBehavior);
111+
return GetStyleCopSettings(settingsFileName, text, failureBehavior);
94112
}
95113

96114
internal static StyleCopSettings GetStyleCopSettings(this CompilationStartAnalysisContext context, AnalyzerOptions options, CancellationToken cancellationToken)
@@ -102,7 +120,8 @@ internal static StyleCopSettings GetStyleCopSettings(this CompilationStartAnalys
102120
internal static StyleCopSettings GetStyleCopSettings(this CompilationStartAnalysisContext context, AnalyzerOptions options, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
103121
#pragma warning restore RS1012 // Start action has no registered actions.
104122
{
105-
SourceText text = TryGetStyleCopSettingsText(options, cancellationToken);
123+
string settingsFileName;
124+
SourceText text = TryGetStyleCopSettingsText(options, cancellationToken, out settingsFileName);
106125
if (text == null)
107126
{
108127
return new StyleCopSettings();
@@ -119,7 +138,7 @@ internal static StyleCopSettings GetStyleCopSettings(this CompilationStartAnalys
119138
return settings;
120139
}
121140

122-
return GetStyleCopSettings(SettingsFileName, text, failureBehavior);
141+
return GetStyleCopSettings(settingsFileName, text, failureBehavior);
123142
}
124143

125144
private static StyleCopSettings GetStyleCopSettings(string path, SourceText text, DeserializationFailureBehavior failureBehavior)
@@ -159,24 +178,28 @@ private static StyleCopSettings GetStyleCopSettings(string path, SourceText text
159178
return new StyleCopSettings();
160179
}
161180

162-
private static SourceText TryGetStyleCopSettingsText(this AnalyzerOptions options, CancellationToken cancellationToken)
181+
private static SourceText TryGetStyleCopSettingsText(this AnalyzerOptions options, CancellationToken cancellationToken, out string settingsFileName)
163182
{
164183
foreach (var additionalFile in options.AdditionalFiles)
165184
{
166-
if (Path.GetFileName(additionalFile.Path).ToLowerInvariant() == SettingsFileName)
185+
if (IsStyleCopSettingsFile(additionalFile.Path))
167186
{
187+
settingsFileName = additionalFile.Path;
188+
168189
return additionalFile.GetText(cancellationToken);
169190
}
170191
}
171192

193+
settingsFileName = null;
194+
172195
return null;
173196
}
174197

175198
private static StyleCopSettings GetStyleCopSettings(ImmutableArray<AdditionalText> additionalFiles, DeserializationFailureBehavior failureBehavior, CancellationToken cancellationToken)
176199
{
177200
foreach (var additionalFile in additionalFiles)
178201
{
179-
if (Path.GetFileName(additionalFile.Path).ToLowerInvariant() == SettingsFileName)
202+
if (IsStyleCopSettingsFile(additionalFile.Path))
180203
{
181204
SourceText additionalTextContent = additionalFile.GetText(cancellationToken);
182205
return GetStyleCopSettings(additionalFile.Path, additionalTextContent, failureBehavior);

0 commit comments

Comments
 (0)