Skip to content

Commit

Permalink
Suppress uninitialized DbSet warnings when a ctor is present
Browse files Browse the repository at this point in the history
Fixes #26879
  • Loading branch information
roji committed Apr 22, 2022
1 parent 1b9becc commit 4f326a5
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 5 deletions.
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
<MicrosoftExtensionsLoggingVersion>7.0.0-preview.4.22217.5</MicrosoftExtensionsLoggingVersion>
</PropertyGroup>
<PropertyGroup Label="Other dependencies">
<MicrosoftCodeAnalysisVersion>4.0.1</MicrosoftCodeAnalysisVersion>
<MicrosoftCodeAnalysisVersion>4.1.0</MicrosoftCodeAnalysisVersion>
</PropertyGroup>
</Project>
17 changes: 13 additions & 4 deletions src/EFCore.Analyzers/UninitializedDbSetDiagnosticSuppressor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
foreach (var diagnostic in context.ReportedDiagnostics)
{
// We have an warning about an uninitialized non-nullable property.
// Get the node, and make sure it's a property who's type syntactically contains DbSet (fast check before getting the
// semantic model, which is heavier).
if (diagnostic.Location.SourceTree is not { } sourceTree
|| sourceTree.GetRoot().FindNode(diagnostic.Location.SourceSpan) is not PropertyDeclarationSyntax propertyDeclarationSyntax

// CS8618 contains the location of the uninitialized property in AdditionalLocations; note that if the class has a constructor,
// the diagnostic main Location points to the constructor rather than to the uninitialized property.
// The AdditionalLocations was added in 7.0.0-preview.3, fall back to the main location just in case and older compiler is
// being used (the check below for PropertyDeclarationSyntax will filter out the diagnostic if it's pointing to a constructor).
var location = diagnostic.AdditionalLocations.Count > 0
? diagnostic.AdditionalLocations[0]
: diagnostic.Location;

// Get the node, and make sure it's a property whose type syntactically contains DbSet (fast check before getting the semantic
// model, which is heavier).
if (location.SourceTree is not { } sourceTree
|| sourceTree.GetRoot().FindNode(location.SourceSpan) is not PropertyDeclarationSyntax propertyDeclarationSyntax
|| !propertyDeclarationSyntax.Type.ToString().Contains("DbSet"))
{
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,57 @@ public class Blog
Assert.False(diagnostic.IsSuppressed);
}

[ConditionalFact]
public async Task DbSet_property_on_DbContext_with_ctor_is_suppressed()
{
var source = @"
public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public MyDbContext() {}
public Microsoft.EntityFrameworkCore.DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int Id { get; set; }
}";

var diagnostic = Assert.Single(await GetDiagnosticsFullSourceAsync(source));

Assert.Equal("CS8618", diagnostic.Id);
Assert.True(diagnostic.IsSuppressed);
}

[ConditionalFact]
public async Task DbSet_property_on_DbContext_with_ctors_is_suppressed()
{
var source = @"
public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public MyDbContext() {}
public MyDbContext(int foo) {}
public Microsoft.EntityFrameworkCore.DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int Id { get; set; }
}";

var diagnostics = await GetDiagnosticsFullSourceAsync(source);

Assert.All(
diagnostics,
diagnostic =>
{
Assert.Equal("CS8618", diagnostic.Id);
Assert.True(diagnostic.IsSuppressed);
});
}

protected Task<Diagnostic[]> GetDiagnosticsFullSourceAsync(string source)
=> base.GetDiagnosticsFullSourceAsync(source, analyzerDiagnosticsOnly: false);

Expand Down

0 comments on commit 4f326a5

Please sign in to comment.