Skip to content

Commit

Permalink
Rule S2930: Remove duplicate issues when in top-level statements. (#5125
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mary-georgiou-sonarsource authored Dec 1, 2021
1 parent 7c84e50 commit bb5703b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 34 deletions.
74 changes: 49 additions & 25 deletions analyzers/src/SonarAnalyzer.CSharp/Rules/DisposableNotDisposed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,30 +69,45 @@ protected override void Initialize(SonarAnalysisContext context) =>
context.RegisterSymbolAction(
c =>
{
var namedType = (INamedTypeSymbol)c.Symbol;
if (namedType.ContainingType != null || !namedType.IsClassOrStruct())
var namedTypeSymbol = (INamedTypeSymbol)c.Symbol;
if (namedTypeSymbol.ContainingType != null || !namedTypeSymbol.IsClassOrStruct())
{
return;
}

var typesDeclarationsAndSemanticModels = namedType.DeclaringSyntaxReferences
.Select(x => new NodeAndSemanticModel<SyntaxNode>(c.Compilation.GetSemanticModel(x.SyntaxTree), x.GetSyntax()))
.ToList();
var typesDeclarationsAndSemanticModels = namedTypeSymbol.DeclaringSyntaxReferences
.Select(x => CreateNodeAndSemanticModelObject(x, c))
.ToList();

var trackedNodesAndSymbols = new HashSet<NodeAndSymbol>();
foreach (var typeDeclarationAndSemanticModel in typesDeclarationsAndSemanticModels)
{
TrackInitializedLocalsAndPrivateFields(typeDeclarationAndSemanticModel.Node, typeDeclarationAndSemanticModel.SemanticModel, trackedNodesAndSymbols);
TrackAssignmentsToLocalsAndPrivateFields(typeDeclarationAndSemanticModel.Node, typeDeclarationAndSemanticModel.SemanticModel, trackedNodesAndSymbols);
TrackInitializedLocalsAndPrivateFields(
namedTypeSymbol,
typeDeclarationAndSemanticModel.Node,
typeDeclarationAndSemanticModel.SemanticModel,
trackedNodesAndSymbols);

TrackAssignmentsToLocalsAndPrivateFields(
namedTypeSymbol,
typeDeclarationAndSemanticModel.Node,
typeDeclarationAndSemanticModel.SemanticModel,
trackedNodesAndSymbols);
}

if (trackedNodesAndSymbols.Any())
{
var excludedSymbols = new HashSet<ISymbol>();
foreach (var typeDeclarationAndSemanticModel in typesDeclarationsAndSemanticModels)
{
ExcludeDisposedAndClosedLocalsAndPrivateFields(typeDeclarationAndSemanticModel.Node, typeDeclarationAndSemanticModel.SemanticModel, excludedSymbols);
ExcludeReturnedPassedAndAliasedLocalsAndPrivateFields(typeDeclarationAndSemanticModel.Node, typeDeclarationAndSemanticModel.SemanticModel, excludedSymbols);
ExcludeDisposedAndClosedLocalsAndPrivateFields(
typeDeclarationAndSemanticModel.Node,
typeDeclarationAndSemanticModel.SemanticModel,
excludedSymbols);
ExcludeReturnedPassedAndAliasedLocalsAndPrivateFields(
typeDeclarationAndSemanticModel.Node,
typeDeclarationAndSemanticModel.SemanticModel,
excludedSymbols);
}

foreach (var trackedNodeAndSymbol in trackedNodesAndSymbols.Where(x => !excludedSymbols.Contains(x.Symbol)))
Expand All @@ -103,19 +118,22 @@ protected override void Initialize(SonarAnalysisContext context) =>
},
SymbolKind.NamedType);

private static void TrackInitializedLocalsAndPrivateFields(SyntaxNode typeDeclaration, SemanticModel semanticModel, ISet<NodeAndSymbol> trackedNodesAndSymbols)
private static NodeAndSemanticModel<SyntaxNode> CreateNodeAndSemanticModelObject(SyntaxReference syntaxReference, SymbolAnalysisContext c) =>
new (c.Compilation.GetSemanticModel(syntaxReference.SyntaxTree), syntaxReference.GetSyntax());

private static void TrackInitializedLocalsAndPrivateFields(INamedTypeSymbol namedType,
SyntaxNode typeDeclaration,
SemanticModel semanticModel,
ISet<NodeAndSymbol> trackedNodesAndSymbols)
{
var localVariableDeclarations = typeDeclaration
.DescendantNodes()
.OfType<LocalDeclarationStatementSyntax>()
.Where(x => !x.UsingKeyword().IsKind(SyntaxKind.UsingKeyword))
.Select(x => x.Declaration);
var descendantNodes = GetDescendantNodes(namedType, typeDeclaration).ToList();
var localVariableDeclarations = descendantNodes.OfType<LocalDeclarationStatementSyntax>()
.Where(x => !x.UsingKeyword().IsKind(SyntaxKind.UsingKeyword))
.Select(x => x.Declaration);

var fieldVariableDeclarations = typeDeclaration
.DescendantNodes()
.OfType<FieldDeclarationSyntax>()
.Where(x => !x.Modifiers.Any() || x.Modifiers.Any(SyntaxKind.PrivateKeyword))
.Select(x => x.Declaration);
var fieldVariableDeclarations = descendantNodes.OfType<FieldDeclarationSyntax>()
.Where(x => !x.Modifiers.Any() || x.Modifiers.Any(SyntaxKind.PrivateKeyword))
.Select(x => x.Declaration);

foreach (var declaration in localVariableDeclarations.Concat(fieldVariableDeclarations))
{
Expand All @@ -127,12 +145,13 @@ private static void TrackInitializedLocalsAndPrivateFields(SyntaxNode typeDeclar
}
}

private static void TrackAssignmentsToLocalsAndPrivateFields(SyntaxNode typeDeclaration, SemanticModel semanticModel, ISet<NodeAndSymbol> trackedNodesAndSymbols)
private static void TrackAssignmentsToLocalsAndPrivateFields(INamedTypeSymbol namedType,
SyntaxNode typeDeclaration,
SemanticModel semanticModel,
ISet<NodeAndSymbol> trackedNodesAndSymbols)
{
var simpleAssignments = typeDeclaration
.DescendantNodes()
.Where(n => n.IsKind(SyntaxKind.SimpleAssignmentExpression))
.Cast<AssignmentExpressionSyntax>();
var simpleAssignments = GetDescendantNodes(namedType, typeDeclaration).Where(n => n.IsKind(SyntaxKind.SimpleAssignmentExpression))
.Cast<AssignmentExpressionSyntax>();

foreach (var simpleAssignment in simpleAssignments)
{
Expand All @@ -146,6 +165,11 @@ private static void TrackAssignmentsToLocalsAndPrivateFields(SyntaxNode typeDecl
}
}

private static IEnumerable<SyntaxNode> GetDescendantNodes(INamedTypeSymbol namedType, SyntaxNode typeDeclaration) =>
namedType.IsTopLevelProgram()
? typeDeclaration.ChildNodes().OfType<GlobalStatementSyntax>().Select(x => x.ChildNodes().First())
: typeDeclaration.DescendantNodes();

private static bool IsLocalOrPrivateField(ISymbol symbol) =>
symbol.Kind == SymbolKind.Local
|| (symbol.Kind == SymbolKind.Field && symbol.DeclaredAccessibility == Accessibility.Private);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,13 @@ public void Lambdas()
{
Action<int, int> a = static (int v, int w) => {
var fs = new FileStream("", FileMode.Open); // Noncompliant
// Noncompliant@-1 - duplicate
};
Action<int, int> b = (_, _) => {
var fs = new FileStream("", FileMode.Open);
fs.Dispose();
};
Action<int, int> с = static (int v, int w) => {
FileStream fs = new("", FileMode.Open); // Noncompliant
// Noncompliant@-1 - duplicate
};
Action<int, int> в = (_, _) => {
FileStream fs = new("", FileMode.Open);
Expand All @@ -56,18 +54,15 @@ record MyRecord
private FileStream field_fs1; // Compliant - not instantiated
public FileStream field_fs2 = new FileStream(@"c:\foo.txt", FileMode.Open); // Compliant - public
private FileStream field_fs3 = new FileStream(@"c:\foo.txt", FileMode.Open); // Noncompliant {{Dispose 'field_fs3' when it is no longer needed.}}
// Noncompliant@-1 - duplicate
private FileStream field_fs4 = new FileStream(@"c:\foo.txt", FileMode.Open); // Compliant - disposed
private FileStream field_fs5 = new(@"c:\foo.txt", FileMode.Open); // Noncompliant {{Dispose 'field_fs5' when it is no longer needed.}}
// Noncompliant@-1 - duplicate

private FileStream backing_field1;
public FileStream Prop1
{
init
{
backing_field1 = new FileStream("", FileMode.Open); // Noncompliant
// Noncompliant@-1 - duplicate
}
}

Expand All @@ -77,7 +72,6 @@ public FileStream Prop2
init
{
backing_field2 = new ("", FileMode.Open); // Noncompliant
// Noncompliant@-1 - duplicate
}
}

Expand All @@ -97,10 +91,7 @@ public void Foo()
}

FileStream fs1 = new(@"c:\foo.txt", FileMode.Open); // Noncompliant
// Noncompliant@-1 - duplicate
var fs2 = File.Open(@"c:\foo.txt", FileMode.Open); // Noncompliant - instantiated with factory method
// Noncompliant@-1 - duplicate
var s = new WebClient(); // Noncompliant - another tracked type
// Noncompliant@-1 - duplicate
}
}

0 comments on commit bb5703b

Please sign in to comment.