-
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
Global analyzer config precedence #48634
Comments
//cc @mavasani @jasonmalinowski @jaredpar for review / feedback. |
Thanks @chsienki - the problem is indeed real and I agree it needs a solution. The proposal of using a We currently recommend end users to name their hierarchical global config files as
As an aside, I think we should also implicitly assume We should also consider updating the documentation about global config naming to recommend following naming for global config files:
|
Interesting. I'm not against the idea of making more decisions about levels implicitly, but I think we have to be very careful in designing the rules around how we do it. In the original design, we went for In terms of assuming I'm also wary of the hierarchy based decision making, just from a user understanding point of view. Imagine the following layout:
Any generated files would get the values set in Like I said I'm not against making it more implicit, but there is an obvious tension between ease of use and accidental opt-in that I'd like to be very careful with. I guess I wonder if any of the implicit suggestions become breaking changes. If not I wonder if it makes sense to start with everything being explicit, then add implicit options as users request them / encounter issues? |
Sounds good. We can leave that part out for now, it is more than likely that users will have a single .globalconfig at repo root, so this feature might not be that essential after all.
My core concern is users will not really understand the concept of
Note that this was a feedback from our doc expert, @gewarren, when we were writing up the documentation for global config files and I agree with her. The fact that we require |
So we do detect it via the project system, but note that the compiler doesn't have any notion of what is or isn't a global config until we actually parse it. We look for the In an ideal world we'd just use a different argument, but as discussed before this isn't practical. @jaredpar to review this, because we're really creating workaround's here for this specific problem. If we absolutely can't revisit this decision, then I would argue in favor of using the filename as a signal so its more obvious for a user. I guess I'm still a little concerned that we'd require NuGet authors to call their config file something different (and include the |
FYI @chsienki - We have now received 3 separate customer requests for this feature. The .NET SDK analyzers ship with global configs which are conflicting with user's global configs. We need this feature to resolve the precedence. |
@mavasani yup, let's get this prioritized. I'll get a PR up with the above design and we can make any iterations there as needed. |
Implements category specific AnalysisLevel properties as per dotnet/roslyn#49250 (comment). Users would now be able to specify the core AnalysisLevel and override category specific AnalysisLevel for each category. ```xml <AnalysisLevel>5-minimum</AnalysisLevel> <AnalysisLevelSecurity>latest-all</ AnalysisLevelSecurity > <AnalysisLevelPerformance>preview-recommended</AnalysisLevelPerformance> <AnalysisLevelGlobalization>5-none</AnalysisLevelGlobalization> ``` 1. The post-build tool now generates category-specific global configs for each supported analysis mode 2. The tool also generates category-specific `AddGlobalAnalyzerConfigForPackage` targets so that if user overrides a category-specific AnalysisLevel property NOTE: This PR depends on feature dotnet/roslyn#48634, which is currently in code review: dotnet/roslyn#49834
…otnet/roslyn#48634 and wanting AnalysisMode AllEnabledByDefault
It looks like PR #49834 will solve my specific scenario, but since my scenario is a little bit different to the other scenarios mentioned here, I thought I'd describe it. At my company we use an internal NuGet package to keep our build settings consistent across the many repositories and solutions that we have. The NuGet package contains a We would like to start with all analyzer rules enabled, and only disable a few that we don't want to use. To do this, we set <Project>
<PropertyGroup>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
</PropertyGroup>
<ItemGroup>
<GlobalAnalyzerConfigFiles Include="$(MSBuildThisFileDirectory).globalconfig" />
</ItemGroup>
</Project> Unfortunately, that results in conflicts because using Once a config level can be specified, then we can set the level of the |
This was fixed with #49834 |
Global analyzer config precedence
We recently introduced a new feature, known as 'global analyzer config'. This was designed to solve two, somewhat orthogonal, problems:
.editorconfig
hierarchy, without the file itself being within the source hierarchy (such as from a NuGet package)The design generally satisfies both problems, but runs into issue when you combine both problems together and still wish to have hierarchical config settings.
Issue
This is easiest explained by way of example. Consider the following scenarios:
Scenario 1
<ID>
towarning
.none
.Solution: The user adds a regular
.editorconfig
that changes the severity of<ID>
tonone
within their source tree.Scenario 2
<ID>
tonone
in source generated files.Solution: The user adds a global analyzer config to their source directory, which sets the severity of
<ID>
tonone
.Scenario 3
<ID>
towarning
.<ID>
tonone
in source generated files.Problem: The user cannot create a global analyzer config with
<ID>
set tonone
. Because global configs are combined into a single virtual config, we unset the value and issue a warning. The user has no way to override the value from the NuGet package.Proposed Solution
The proposed solution is to introduce an optional notion of precedence to global analyzer configs.
A straw-man syntax looks something like
Any colliding keys within the same level will be unset and a warning issued. When keys from a higher level collide with those of a lower level, the higher level wins, and if there is only a single winning key, no warning is issued. By default, a global editor config without an explicit level would have a default level of
0
.Thus in scenario 3, a user could create a global config with a higher precedence than the one specified in the NuGet package, allowing them to override the severity of
<ID>
in generated files.Multiple keys with the same value
The existing behavior today is to issue a warning whenever we see duplicate keys. However, it seems intuitive that if all the keys map to the same value, we should be able to treat it as unambiguous and use the value. As part of this proposal we should change the behavior seen today to allow unambiguous duplicates. This won't be a breaking change as we'll be removing a subset of warnings.
Why isn't this a problem for
.editorconfig
Standard
.editorconfig
s can resolve conflicts via their natural filesystem hierarchy. Anything 'further from the root' is higher in precedence than the closer ones. Global analyzer configs can exist in completely different hierarchies, so a NuGet provided config inc:\users\user\.nuget\cache\MyPackage\1.0.0\.globalconfig
could be 'further away' thanc:\myproject\.globalconfig
but should clearly not take precedence.Further, because global configs are designed to provide values for files without a source path, even if we could resolve the 'logical depth' of the config, there is no correct way to determine which one should apply to the file itself. One option could be to use the project file path as a proxy, but the compiler itself doesn't currently have any notion of project file. Therefore not only would it be an only somewhat inaccurate heuristic, but we would have to do work anyway to enable it.
Notes
Should we allow negative levels?: Ultimately the level just needs to be an opaque sortable key. Limiting it to integers makes sense as the ordering is obvious, but having negative levels wouldn't necessarily be a problem. A user could create a config with a level of
-1
which would mean 'Apply this config key, unless a NuGet package has already provided it'. This seems like kind of a niche case, but falls out for free from the above design, so seems worth including.Could a 'malicious' NuGet package provide an ID of
Int32.Max
?: Yes, but this is no worse a situation than exists currently.Can it get complicated figuring out where a value is set: Yes, with multiple hierarchical configs and levels it could be non-obvious exactly which rule is being applied and why. One option could be to issue a hidden diagnostic when we select an override, that would allow e.g. an analyzer to surface the information to the user.
Is this just a problem for NuGet: No, for sufficiently advanced solutions (such as Roslyn itself) it seems likely that different parts of the source tree (such as a Compilers and Workspaces) might want to have differing global values. This proposal enables that too.
The text was updated successfully, but these errors were encountered: