Open meziantou opened 4 years ago
This is same concern mentioned at https://github.com/dotnet/roslyn/issues/44330#issuecomment-630395417. As mentioned in https://github.com/dotnet/roslyn/issues/44330#issuecomment-631487553 the analyzer should not handle this but user should use a source suppression to document this.
Oh actually I think @Evangelink might have already fixed this?
@mavasani I can reproduce the issue but I am not entirely sure how to fix it. The first thing that comes to my mind is to say that if a type is used as a generic type then we don't report on it but that's killing too many valid cases. The other option would be to have a list of methods (maybe configurable) that are considered as creating instance of types.
3 options here:
I think 1. or 3. would be fine.
@genlu suggested a potential 4th option: Use a special attribute that can be applied to type/method to mark this semantics.
Would it make sense to use the DynamicallyAccessedMembers
attribute? Most methods from the framework are already annotated. For instance, Deserialize
is annotated with PublicProperties | PublicFields | PublicConstructors
(source). This means the method looks for the constructors of T. This doesn't guarantee to call it, but this is a good indication it will try to create an instance of the object.
That's being said, I'm not sure this will help for the reported code as the T
parameter is actually a Dictionary<string, CustomType>
, so the attribute doesn't explain what to do with CustomType
. But it will help for Deserialize<CustomType>(...)
.
@mavasani I can reproduce the issue but I am not entirely sure how to fix it. The first thing that comes to my mind is to say that if a type is used as a generic type then we don't report on it but that's killing too many valid cases. The other option would be to have a list of methods (maybe configurable) that are considered as creating instance of types.
Hi there. I'm curious why not systematically suppressing any cases where the given type is used as a generic is not the right answer? The rule is really trying to say "hey, you've got a type here that nobody is using". When it's used a generic, the type is in fact being used, so no diagnostic should be produced.
The other option would be to have a list of methods (maybe configurable) that are considered as creating instance of types.
If we go with this approach, we should consider AddHostedService
as one of the methods in the list. See https://github.com/dotnet/roslyn-analyzers/issues/6086
Alternatively, https://github.com/dotnet/roslyn-analyzers/issues/3545 suggests that libraries that have methods which instantiate generic types should write their own DiagnosticSuppressor
What if you put a new()
constraint on JsonSerializer.Deserialize<T>()
? Or would that break?
Otherwise, a workaround is to have a helper method with the constraint:
public static class MySerializer
{
public static TValue? Deserialize<TValue>(string json, JsonSerializerOptions? options = null)
where TValue : new()
{
return JsonSerializer.Deserialize<TValue>(json, options);
}
}
Obviously, this will only work for types with public parameterless constructors.
I am going to move this issue to dotnet/runtime for them to help us with the best option. Let me list down all the options we have identified in this issue:
Issue: CA1812 false positives from non-explicit type instantiations
Potential solutions:
DynamicallyAccessedMembers
attribute as explained in https://github.com/dotnet/roslyn-analyzers/issues/6561I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
@stephentoub @bartonjs @buyaa-n @carlossanlop to help triage the options available in https://github.com/dotnet/roslyn-analyzers/issues/6561
Tagging subscribers to this area: @dotnet/area-system-runtime See info in area-owners.md if you want to be subscribed.
Author: | meziantou |
---|---|
Assignees: | - |
Labels: | `area-System.Runtime`, `untriaged`, `code-analyzer` |
Milestone: | - |
Here's my case:
internal sealed class ServiceFabricHealthLoggerProvider : ILoggerProvider { }
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ServiceFabricHealthLoggerProvider>());
LoggerProviderOptions.RegisterProviderOptions<ServiceFabricHealthLoggerOptions, ServiceFabricHealthLoggerProvider>(builder.Services);
Neither option to suppress seems to be working to me:
dotnet_diagnostic.CA1812.severity = none
dotnet_code_quality.CA1812.excluded_symbol_names = ServiceFabricHealthLoggerProvider
Context:
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
@abatishchev I was able to confirm that dotnet_diagnostic.CA1812.severity = none
does indeed work fine and suppresses the warning. Please find a sample project showing the same: ClassLibrary7.zip. If you remove the editorconfig entry, CA1812
does fire as expected.
Regarding dotnet_code_quality.CA1812.excluded_symbol_names
, that option is not yet supported. It is one of the proposed suggestions in this issue to allow such custom suppressions.
hi @mavasani, I've got it reproducing when replaced
<AnalysisLevel>7-all</AnalysisLevel>
with
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
Even without updating the package version to:
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0-preview1.22631.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Which in my case is 7.0.0
.
Tagging subscribers to this area: @dotnet/area-meta See info in area-owners.md if you want to be subscribed.
Author: | meziantou |
---|---|
Assignees: | - |
Labels: | `area-Meta`, `untriaged`, `code-analyzer` |
Milestone: | - |
- Options based Configuration: Add an editorconfig option to specify special methods or types whose generic type arguments are instantiated.
Options based Configuration
sounds best to me, probably should be at type level, specifying at method level might be too granular
Regarding
dotnet_code_quality.CA1812.excluded_symbol_names
, that option is not yet supported. It is one of the proposed suggestions in this issue to allow such custom suppressions.
👍 I would vote for this config
I raised this with @stephentoub who recommended option 2, which is where I would lean as well. This would make the rule stay useful in most cases and the small sacrifice for a potential false negative. This would make things work without having the need for folks (or API authors) to configure the rule.
You could reduce the false negatives with a slightly better heuristic. Instead of making it "used in any generic parameter" you could make it "used in a generic parameter which is also used as a return value".
This would make it flag a case where someone was calling Serialize<T>(null)
but not flag a case where someone was calling Deserialize<T>(string)
(since it would return T).
Transferred back to analyzers repo as question answered
You could reduce the false negatives with a slightly better heuristic. Instead of making it "used in any generic parameter" you could make it "used in a generic parameter which is also used as a return value".
Unless I'm missing something, it seems like that would still leave us with a sea of CA1812 false positives for common M.E.DI usage, e.g. services.AddSingleton<T>()
and then taking T
as a constructor parameter somewhere.
I had CA1812 come up when using CommandLine in a command line project.
internal static class Program
{
public class Options //CA1812
{
[Option('p', "verbose", Required = false, Default = 5, HelpText = "")]
public int Verbose { get; set; }
}
private static void Main(string[] args)
{
_ = Parser.Default.ParseArguments<Options>(args)
.WithParsed(opts =>
{
The only reason I'm adding a comment here is is that I noticed the error no longer came up when I made internals visible to my test project by adding this to the command line project. I'm not pointing this out as a fix, just making note of the scenario.
<ItemGroup>
<InternalsVisibleTo Include="MyCommandLineProject.Tests" />
</ItemGroup>
These are the command project settings.
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>MyCommandLineProject</AssemblyName>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest-all</AnalysisLevel>
</PropertyGroup>
Describe the bug
CA1812 false-positive
Steps To Reproduce
Expected behavior
No diagnostic
Actual behavior
It reports a diagnostic that is not appliable in the context
Analyzer
Package: Microsoft.CodeAnalysis.FxCopAnalyzers
Version: v3.3.0 (Latest)
Diagnostic ID: CA1812