-
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
Wire through editorconfig #27232
Wire through editorconfig #27232
Conversation
90a2ddd
to
d09083f
Compare
@@ -244,6 +246,231 @@ internal SourceText TryReadFileContent(CommandLineSourceFile file, IList<Diagnos | |||
} | |||
} | |||
|
|||
internal static ImmutableArray<ImmutableDictionary<string, ReportDiagnostic>> GetAnalyzerConfigOptions( | |||
IReadOnlyList<string> sourcePaths, |
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.
Why IReadOnlyList<string>
here? Next method takes an ImmutableArray<string>
for a paths parameter. #Resolved
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.
This one is used for testing and I pass in an array instead of an ImmutableArray because the syntax is shorter and clearer. #Resolved
diagId = diagId.Substring(0, diagId.Length - DiagnosticOptionSuffix.Length); | ||
|
||
ReportDiagnostic? severity = null; | ||
switch (CaseInsensitiveComparison.ToLower(value)) |
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.
switch (CaseInsensitiveComparison.ToLower(value)) [](start = 23, length = 50)
How often are we going to be parsing this? Seems like we're creating an extra allocation per property here. Know it's annoying but may want to consider wiriting out this if by hand.
key.EndsWith(DiagnosticOptionSuffix, StringComparison.Ordinal)) | ||
{ | ||
string diagId = key.Substring(DiagnosticOptionPrefix.Length); | ||
diagId = diagId.Substring(0, diagId.Length - DiagnosticOptionSuffix.Length); |
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.
Can't this be done as a single Substring operation?
|
||
readEditorConfigFiles(analyzerConfigPaths, analyzerConfigs); | ||
|
||
if (diagnostics.HasAnyErrors()) |
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.
Shouldn't this check be done first? The code above has no impact on the content of this DiagnosticBag
instance.
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.
Ah I see now. That's really subtle as you capture some of the context (diagnostics) but pass some explicitly (analyzerConfigs). I think you should be consistent here and do one or the other.
In reply to: 191616102 [](ancestors = 191616102)
string fileContent = TryReadFileContent(configPath, diagnostics, out string normalizedPath); | ||
if (fileContent is null) | ||
{ | ||
// Error reading a file. Bail out and report error. |
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.
Don't see how an error gets reported here.
|
||
processedDirs.Add(directory); | ||
var forwardPath = PathWithForwardSlashSeparators(directory); | ||
var editorConfig = EditorConfig.Parse(fileContent, forwardPath); |
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.
How do errors get reported on this path?
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.
There are never any parse errors for EditorConfig files. Any invalid sections are skipped and no errors are reported.
test windows_debug_spanish_unit32_prtest please |
Provides a new command line compiler option: "/analyzerconfig:" that takes a set of "analyzer config" files (supersets of EditorConfig files) that the compiler will consider for diagnostic configuration.
47750e8
to
8df6338
Compare
I think this is no longer true? #Resolved |
Yup! Merged commits have been removed to assist reviewing |
@jaredpar Any more comments here? |
(e1, e2) => e1.Directory.Length.CompareTo(e2.Directory.Length))); | ||
|
||
var allDiagnosticOptions = | ||
ArrayBuilder<ImmutableDictionary<string, ReportDiagnostic>>.GetInstance(sourcePaths.Count); |
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.
Even if the intent is ordinal comparison consider using an explicit comparer here. This makes it clear to later developers that the scenario was considered.
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.
I think you're thinking of the ImmutableDictionary. This is an ImmutableArray of ImmutableDictionaries, so the only comparer I expect to work here is reference comparison. The later creation of the ImmutableDictionaries should have explicit comparers.
@@ -5339,4 +5339,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="ERR_OutVariableCannotBeByRef" xml:space="preserve"> | |||
<value>An out variable cannot be declared as a ref local</value> | |||
</data> | |||
<data name="WRN_InvalidSeverityInAnalyzerConfig" xml:space="preserve"> | |||
<value>The diagnostic '{0}' was given an invalid severity '{1}' in analyzer config file.</value> |
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.
in analyzer config file [](start = 68, length = 23)
"in analyzer config file"? In THE analyzer config file? In AN analyzer config file? Can we identify the offending config file? I presume this message identifies the offending file and line.
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.
Unfortunately the parser does not hold enough information to produce an exact location. I've changed the method to include the directory of the editorconfig file, which should be unambiguous since there is only more than one in a rare error scenario. However, I think producing the full path wouldn't be too hard, it's just that I've refactored this API slightly in a later PR so I'd prefer to fix it later. I've filed https://github.com/dotnet/roslyn/projects/18#card-10413696 to track this issue.
<value>Invalid severity in analyzer config file.</value> | ||
</data> | ||
<data name="ERR_MultipleAnalyzerConfigsInSameDir" xml:space="preserve"> | ||
<value>Multiple analyzer configs cannot be in the same directory ('{0}').</value> |
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.
analyzer configs [](start = 20, length = 16)
... analyzer config files cannot ...
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.
/// A set of paths to EditorConfig-compatible analyzer config files. | ||
/// </summary> | ||
public ImmutableArray<string> AnalyzerConfigPaths { get; internal set; } | ||
|
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.
This is confusing. Why do we call them "paths" instead of a filenames? Why do we use string
instead of CommandLineSourceFile
like we do for AdditionalFiles
and EmbeddedFiles
?
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.
Perhaps this is a set of paths to directories containing editor config files?
In reply to: 193898640 [](ancestors = 193898640)
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.
A file name usually just refers to the name of the file, i.e. for a file /a/b/c.cs
the file name would be c.cs
, while the path would be /a/b/c.cs
. Right now these happen to be absolute paths, but I didn't want to guarantee that in the API.
As to why I didn't choose to use CommandLineSourceFile
-- I'm not sure why we did choose that for AdditionalFiles
and EmbeddedFiles
. CommandLineSourceFile
is a struct with two entries: Path
, which is the same as the string here, and IsScript
which is a bool that is always false, since by definition only source files can be script files. I have a feeling that the helper for parsing separated paths happened to return CommandLineSourceFiles
and these additions couldn't be bothered to convert. Otherwise I see no reason for them just to be paths.
|
||
ReportDiagnostic? severity; | ||
if (CaseInsensitiveComparison.Equals(value, "default")) | ||
{ |
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.
This can be done using a switch on value.ToLowerInvariant(). Since we're looking at ASCII, no fancy Unicode-correct case insensitivity is needed.
Why do we require an exact match for the prefix and suffix of the key, but compare the middle part using relaxed case?
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.
I've changed this to use OrdinalIgnoreCase
. Jared suggested not calling ToLower or ToUpper because it will allocate an additional string for each property. The if
is more verbose, but avoids that.
As to the mixed casing -- the key is always lower-cased by the EditorConfig parser, while the value is case-preserving, so we compare the value case-insensitively, but the key case-sensitively.
@@ -244,6 +246,233 @@ internal SourceText TryReadFileContent(CommandLineSourceFile file, IList<Diagnos | |||
} | |||
} | |||
|
|||
internal static ImmutableArray<ImmutableDictionary<string, ReportDiagnostic>> GetAnalyzerConfigOptions( |
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.
GetAnalyzerConfigOptions [](start = 86, length = 24)
I lack context to understand what this method is supposed to do. What are the arguments? Can you add a doc comment please?
Is there a design doc that will help me know what this PR is trying to do? |
@gafter Great point. I will write up a doc to accompany the feature. It will follow closely what's described on this page: https://editorconfig.org/ |
@@ -65,8 +71,7 @@ private EditorConfig(Section globalSection, ImmutableArray<Section> namedSection | |||
/// <summary> | |||
/// Gets whether this editorconfig is a the topmost editorconfig. | |||
/// </summary> | |||
public bool IsRoot | |||
=> GlobalSection.Properties.TryGetValue("root", out string val) && val == "true"; | |||
public bool IsRoot => GlobalSection.Properties.TryGetValue("root", out string val) && val == "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.
If the topmost editorconfig file in the project lacks this property, it should still be considered the root. How do we handle that?
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.
This comment is bad. Root
should mean that no properties from higher editorconfigs should apply, not necessarily that there couldn't be editorconfigs higher in the directory tree. This work is tracked at #27644
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.
This PR provides a new command line compiler option: "/analyzerconfig:" that
takes a set of "analyzer config" files (supersets of EditorConfig files)
that the compiler will consider for diagnostic configuration.