-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EditorConfig comments cannot follow a property value #51625
base: main
Are you sure you want to change the base?
Changes from all commits
3b98df1
1b6c125
ea18e2f
d797c32
bf03a75
be49e52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -270,15 +270,18 @@ public void SpacesInProperties() | |||||||||||
properties); | ||||||||||||
} | ||||||||||||
|
||||||||||||
[Fact] | ||||||||||||
public void EndOfLineComments() | ||||||||||||
[Theory] | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please document the breaking change in https://github.com/dotnet/roslyn/blob/main/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20post%20DotNet%205.md #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ➡️ Documented for .NET 9 |
||||||||||||
[WorkItem(44596, "https://github.com/dotnet/roslyn/issues/44596")] | ||||||||||||
[InlineData(";")] | ||||||||||||
[InlineData("#")] | ||||||||||||
public void EndOfLineComments(string commentStartCharacter) | ||||||||||||
{ | ||||||||||||
var config = ParseConfigFile(@" | ||||||||||||
my_prop2 = my val2 # Comment"); | ||||||||||||
var config = ParseConfigFile($@" | ||||||||||||
my_prop2 = my val2 {commentStartCharacter} Not A Comment"); | ||||||||||||
|
||||||||||||
var properties = config.GlobalSection.Properties; | ||||||||||||
AssertEx.SetEqual( | ||||||||||||
new[] { KeyValuePair.Create("my_prop2", "my val2") }, | ||||||||||||
new[] { KeyValuePair.Create("my_prop2", $"my val2 {commentStartCharacter} Not A Comment") }, | ||||||||||||
properties); | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
@@ -994,6 +997,82 @@ public void EditorConfigToDiagnostics_DoubleSlash(string prefixEditorConfig, str | |||||||||||
], options.Select(o => o.TreeOptions).ToArray()); | ||||||||||||
} | ||||||||||||
|
||||||||||||
[Theory] | ||||||||||||
[InlineData("default")] | ||||||||||||
[InlineData("none")] | ||||||||||||
[InlineData("silent")] | ||||||||||||
[InlineData("refactoring")] | ||||||||||||
[InlineData("suggestion")] | ||||||||||||
[InlineData("warning")] | ||||||||||||
[InlineData("error")] | ||||||||||||
public void EditorConfigToDiagnosticsValidSeverity(string severity) | ||||||||||||
{ | ||||||||||||
var expected = severity switch | ||||||||||||
{ | ||||||||||||
"default" => ReportDiagnostic.Default, | ||||||||||||
"none" => ReportDiagnostic.Suppress, | ||||||||||||
"silent" => ReportDiagnostic.Hidden, | ||||||||||||
"refactoring" => ReportDiagnostic.Hidden, | ||||||||||||
"suggestion" => ReportDiagnostic.Info, | ||||||||||||
"warning" => ReportDiagnostic.Warn, | ||||||||||||
"error" => ReportDiagnostic.Error, | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would prefer these just be arguments to this test method, so the switch isn't needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
➡️ The current form is intentional. Arguments to the test method are input state and are used to define the unique ID of the test itself. Expected results should not be inputs, and changing an expected outcome should not be part of the test identity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd agree with Cyrus here -- we normally put the expected as an input too... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
➡️ I try to fix these when they occur in a method I'm working on. Test parameterization should only ever exist on the inputs, not the outputs. The entire test identity and reporting strategy (e.g. test historical pass rates, red/green developer iterations in TDD) is built around this premise. |
||||||||||||
_ => throw ExceptionUtilities.UnexpectedValue(severity), | ||||||||||||
}; | ||||||||||||
|
||||||||||||
var configs = ArrayBuilder<AnalyzerConfig>.GetInstance(); | ||||||||||||
configs.Add(Parse($@" | ||||||||||||
[*.cs] | ||||||||||||
dotnet_diagnostic.cs000.severity = {severity} | ||||||||||||
", "/.editorconfig")); | ||||||||||||
|
||||||||||||
var options = GetAnalyzerConfigOptions(["/test.cs"], configs); | ||||||||||||
configs.Free(); | ||||||||||||
|
||||||||||||
Assert.Equal([CreateImmutableDictionary(("cs000", expected))], Array.ConvertAll(options, o => o.TreeOptions)); | ||||||||||||
} | ||||||||||||
|
||||||||||||
[Theory] | ||||||||||||
[InlineData("unset")] | ||||||||||||
[InlineData("warn")] | ||||||||||||
[InlineData("")] | ||||||||||||
public void EditorConfigToDiagnosticsInvalidSeverity(string severity) | ||||||||||||
{ | ||||||||||||
var configs = ArrayBuilder<AnalyzerConfig>.GetInstance(); | ||||||||||||
configs.Add(Parse($@" | ||||||||||||
[*.cs] | ||||||||||||
dotnet_diagnostic.cs000.severity = {severity} | ||||||||||||
", "/.editorconfig")); | ||||||||||||
|
||||||||||||
var options = GetAnalyzerConfigOptions(["/test.cs"], configs); | ||||||||||||
configs.Free(); | ||||||||||||
|
||||||||||||
Assert.Equal([ImmutableDictionary<string, ReportDiagnostic>.Empty], Array.ConvertAll(options, o => o.TreeOptions)); | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// <summary> | ||||||||||||
/// Verifies that the AnalyzerConfig parser ignores comment-like trailing text when parsing severity values. | ||||||||||||
/// This ensures the change from https://github.com/dotnet/roslyn/pull/51625 does not affect the parsing of | ||||||||||||
/// diagnostic severities. | ||||||||||||
/// </summary> | ||||||||||||
[Theory] | ||||||||||||
[InlineData("#")] | ||||||||||||
[InlineData(";")] | ||||||||||||
Comment on lines
+1058
to
+1059
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd consider tweaking the inline data to add another case like:
Suggested change
As otherwise this test could incorrectly pass if all parse errors were simply converted to ReportDiagnostic.Error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ➡️ Added a second line inside the test itself. |
||||||||||||
[WorkItem("https://github.com/dotnet/roslyn/pull/51625")] | ||||||||||||
public void EditorConfigToDiagnosticsIgnoresCommentLikeText(string delimiter) | ||||||||||||
{ | ||||||||||||
var configs = ArrayBuilder<AnalyzerConfig>.GetInstance(); | ||||||||||||
configs.Add(Parse($@" | ||||||||||||
[*.cs] | ||||||||||||
dotnet_diagnostic.cs000.severity = error {delimiter} ignored text | ||||||||||||
dotnet_diagnostic.cs001.severity = warning {delimiter} ignored text | ||||||||||||
", "/.editorconfig")); | ||||||||||||
|
||||||||||||
var options = GetAnalyzerConfigOptions(["/test.cs"], configs); | ||||||||||||
configs.Free(); | ||||||||||||
|
||||||||||||
Assert.Equal([CreateImmutableDictionary(("cs000", ReportDiagnostic.Error), ("cs001", ReportDiagnostic.Warn))], Array.ConvertAll(options, o => o.TreeOptions)); | ||||||||||||
} | ||||||||||||
|
||||||||||||
[Fact] | ||||||||||||
public void LaterSectionOverrides() | ||||||||||||
{ | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -330,33 +330,40 @@ static void freeKey(List<Section> sectionKey, ObjectPool<List<Section>> pool) | |
|
||
internal static bool TryParseSeverity(string value, out ReportDiagnostic severity) | ||
{ | ||
ReadOnlySpan<char> commentStartCharacters = stackalloc char[] { ';', '#' }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: consider leaving a comment ("for backcompat, ...") |
||
var trimmed = value.AsSpan(); | ||
var commentStartIndex = trimmed.IndexOfAny(commentStartCharacters); | ||
if (commentStartIndex >= 0) | ||
trimmed = trimmed[0..commentStartIndex].TrimEnd(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understand, this change is to maintain the existing behavior and I assume we already have tests covering this. Is that right? #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ➡️ Yes, and tests have now been added. |
||
|
||
var comparer = StringComparer.OrdinalIgnoreCase; | ||
if (comparer.Equals(value, "default")) | ||
if (trimmed.Equals("default".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
{ | ||
severity = ReportDiagnostic.Default; | ||
return true; | ||
} | ||
else if (comparer.Equals(value, "error")) | ||
else if (trimmed.Equals("error".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
severity = ReportDiagnostic.Error; | ||
return true; | ||
} | ||
else if (comparer.Equals(value, "warning")) | ||
else if (trimmed.Equals("warning".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
severity = ReportDiagnostic.Warn; | ||
return true; | ||
} | ||
else if (comparer.Equals(value, "suggestion")) | ||
else if (trimmed.Equals("suggestion".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
severity = ReportDiagnostic.Info; | ||
return true; | ||
} | ||
else if (comparer.Equals(value, "silent") || comparer.Equals(value, "refactoring")) | ||
else if (trimmed.Equals("silent".AsSpan(), StringComparison.OrdinalIgnoreCase) | ||
|| trimmed.Equals("refactoring".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
severity = ReportDiagnostic.Hidden; | ||
return true; | ||
} | ||
else if (comparer.Equals(value, "none")) | ||
else if (trimmed.Equals("none".AsSpan(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
severity = ReportDiagnostic.Suppress; | ||
return true; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tagging @jaredpar to advise on which release to pull this change into, since it involves a compat break. Feels like we should hold this off rather than squeeze it late in the release cycle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should either update this before merging, or open an issue to track updating it once we know which release it landed in.