diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/CollectionsShouldImplementGenericInterface.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/CollectionsShouldImplementGenericInterface.cs index 424e83c0106..0bbd0d71fc7 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/CollectionsShouldImplementGenericInterface.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/CollectionsShouldImplementGenericInterface.cs @@ -49,28 +49,24 @@ protected override void Initialize(SonarAnalysisContext context) => { var typeDeclaration = (BaseTypeDeclarationSyntax)c.Node; var implementedTypes = typeDeclaration.BaseList?.Types; - if (implementedTypes == null || c.IsRedundantPositionalRecordContext()) + if (implementedTypes is null || c.IsRedundantPositionalRecordContext()) { return; } - List issues = null; var containingType = (INamedTypeSymbol)c.ContainingSymbol; - foreach (var typeSymbol in containingType.Interfaces.Concat(new[] { containingType.BaseType }).WhereNotNull()) + var typeSymbols = containingType.Interfaces.Concat([containingType.BaseType]).WhereNotNull().ToImmutableArray(); + if (typeSymbols.Any(x => x.OriginalDefinition.IsAny(GenericTypes))) + { + return; + } + foreach (var typeSymbol in typeSymbols) { - if (typeSymbol.OriginalDefinition.IsAny(GenericTypes)) - { - return; - } - if (SuggestGenericCollectionType(typeSymbol) is { } suggestedGenericType) { - issues ??= new(); - issues.Add(Diagnostic.Create(Rule, typeDeclaration.Identifier.GetLocation(), suggestedGenericType)); + c.ReportIssue(Rule, typeDeclaration.Identifier, suggestedGenericType); } } - - issues?.ForEach(d => c.ReportIssue(d)); }, SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotDecreaseMemberVisibility.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotDecreaseMemberVisibility.cs index ce2eeaa2386..3f2bc83e6ba 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotDecreaseMemberVisibility.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotDecreaseMemberVisibility.cs @@ -27,6 +27,7 @@ public sealed class DoNotDecreaseMemberVisibility : SonarDiagnosticAnalyzer private const string MessageFormat = "This member hides '{0}'. Make it non-private or seal the class."; private static readonly DiagnosticDescriptor Rule = DescriptorFactory.Create(DiagnosticId, MessageFormat); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); protected override void Initialize(SonarAnalysisContext context) => @@ -40,24 +41,24 @@ protected override void Initialize(SonarAnalysisContext context) => return; } - var issueFinder = new IssueFinder(classSymbol, c.SemanticModel); - foreach (var diagnostic in classDeclaration.Members.Select(issueFinder.FindIssue).WhereNotNull()) + var issueReporter = new IssueReporter(classSymbol, c); + foreach (var member in classDeclaration.Members) { - c.ReportIssue(diagnostic); + issueReporter.ReportIssue(member); } }, SyntaxKind.ClassDeclaration, SyntaxKindEx.RecordClassDeclaration); - private sealed class IssueFinder + private sealed class IssueReporter { private readonly IList allBaseClassMethods; private readonly IList allBaseClassProperties; - private readonly SemanticModel semanticModel; + private readonly SonarSyntaxNodeReportingContext context; - public IssueFinder(ITypeSymbol classSymbol, SemanticModel semanticModel) + public IssueReporter(ITypeSymbol classSymbol, SonarSyntaxNodeReportingContext context) { - this.semanticModel = semanticModel; + this.context = context; var allBaseClassMembers = classSymbol.BaseType .GetSelfAndBaseTypes() .SelectMany(t => t.GetMembers()) @@ -68,57 +69,39 @@ public IssueFinder(ITypeSymbol classSymbol, SemanticModel semanticModel) allBaseClassProperties = allBaseClassMembers.OfType().ToList(); } - public Diagnostic FindIssue(MemberDeclarationSyntax memberDeclaration) + public void ReportIssue(MemberDeclarationSyntax memberDeclaration) { - var memberSymbol = semanticModel.GetDeclaredSymbol(memberDeclaration); - - if (memberSymbol is IMethodSymbol methodSymbol) + switch (context.SemanticModel.GetDeclaredSymbol(memberDeclaration)) { - return FindMethodIssue(memberDeclaration, methodSymbol); + case IMethodSymbol methodSymbol: + ReportMethodIssue(memberDeclaration, methodSymbol); + break; + case IPropertySymbol propertySymbol: + ReportPropertyIssue(memberDeclaration, propertySymbol); + break; } - - return memberSymbol is IPropertySymbol propertySymbol ? FindPropertyIssue(memberDeclaration, propertySymbol) : null; } - private Diagnostic FindMethodIssue(MemberDeclarationSyntax memberDeclaration, IMethodSymbol methodSymbol) + private void ReportMethodIssue(MemberDeclarationSyntax memberDeclaration, IMethodSymbol methodSymbol) { - if (memberDeclaration is not MethodDeclarationSyntax methodDeclaration - || methodDeclaration.Modifiers.Any(SyntaxKind.NewKeyword)) - { - return null; - } - - var hidingMethod = allBaseClassMethods.FirstOrDefault(m => IsDecreasingAccess(m.DeclaredAccessibility, methodSymbol.DeclaredAccessibility, false) - && IsMatchingSignature(m, methodSymbol)); - - if (hidingMethod != null) + if (memberDeclaration is MethodDeclarationSyntax methodDeclaration + && !methodDeclaration.Modifiers.Any(SyntaxKind.NewKeyword) + && allBaseClassMethods.FirstOrDefault(x => + IsDecreasingAccess(x.DeclaredAccessibility, methodSymbol.DeclaredAccessibility, false) + && IsMatchingSignature(x, methodSymbol)) is { } hidingMethod) { - var location = methodDeclaration.Identifier.GetLocation(); - if (location != null) - { - return Diagnostic.Create(Rule, location, hidingMethod); - } + context.ReportIssue(Rule, methodDeclaration.Identifier, hidingMethod.ToDisplayString()); } - - return null; } - private Diagnostic FindPropertyIssue(MemberDeclarationSyntax memberDeclaration, IPropertySymbol propertySymbol) + private void ReportPropertyIssue(MemberDeclarationSyntax memberDeclaration, IPropertySymbol propertySymbol) { - if (memberDeclaration is not PropertyDeclarationSyntax propertyDeclaration - || propertyDeclaration.Modifiers.Any(SyntaxKind.NewKeyword)) + if (memberDeclaration is PropertyDeclarationSyntax propertyDeclaration + && !propertyDeclaration.Modifiers.Any(SyntaxKind.NewKeyword) + && allBaseClassProperties.FirstOrDefault(x => IsDecreasingPropertyAccess(x, propertySymbol, propertySymbol.IsOverride)) is { } hidingProperty) { - return null; + context.ReportIssue(Rule, propertyDeclaration.Identifier, hidingProperty.ToDisplayString()); } - - var hidingProperty = allBaseClassProperties.FirstOrDefault(p => IsDecreasingPropertyAccess(p, propertySymbol, propertySymbol.IsOverride)); - if (hidingProperty != null) - { - var location = propertyDeclaration.Identifier.GetLocation(); - return Diagnostic.Create(Rule, location, hidingProperty); - } - - return null; } private static bool IsSymbolVisibleFromNamespace(ISymbol symbol, INamespaceSymbol ns) => diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotHideBaseClassMethods.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotHideBaseClassMethods.cs index 52edb94ade7..9ab881a319d 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotHideBaseClassMethods.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotHideBaseClassMethods.cs @@ -35,21 +35,21 @@ protected override void Initialize(SonarAnalysisContext context) => var declarationSyntax = (TypeDeclarationSyntax)c.Node; if (declarationSyntax.Identifier.IsMissing || c.IsRedundantPositionalRecordContext() - || !(c.SemanticModel.GetDeclaredSymbol(declarationSyntax) is {} declaredSymbol)) + || !(c.SemanticModel.GetDeclaredSymbol(declarationSyntax) is { } declaredSymbol)) { return; } - var issueFinder = new IssueFinder(declaredSymbol, c.SemanticModel); - foreach (var diagnostic in declarationSyntax.Members.Select(issueFinder.FindIssue).WhereNotNull()) + var issueReporter = new IssueReporter(declaredSymbol, c); + foreach (var member in declarationSyntax.Members) { - c.ReportIssue(diagnostic); + issueReporter.ReportIssue(member); } }, SyntaxKind.ClassDeclaration, SyntaxKindEx.RecordClassDeclaration); - private sealed class IssueFinder + private sealed class IssueReporter { private enum Match { @@ -59,25 +59,22 @@ private enum Match } private readonly IList allBaseTypeMethods; - private readonly SemanticModel semanticModel; + private readonly SonarSyntaxNodeReportingContext context; - public IssueFinder(ITypeSymbol typeSymbol, SemanticModel semanticModel) + public IssueReporter(ITypeSymbol typeSymbol, SonarSyntaxNodeReportingContext context) { - this.semanticModel = semanticModel; + this.context = context; allBaseTypeMethods = GetAllBaseMethods(typeSymbol); } - public Diagnostic FindIssue(MemberDeclarationSyntax memberDeclaration) + public void ReportIssue(MemberDeclarationSyntax memberDeclaration) { - var issueLocation = (memberDeclaration as MethodDeclarationSyntax)?.Identifier.GetLocation(); - - if (semanticModel.GetDeclaredSymbol(memberDeclaration) is not IMethodSymbol methodSymbol || issueLocation == null) + if (memberDeclaration is MethodDeclarationSyntax { Identifier: { } identifier } + && context.SemanticModel.GetDeclaredSymbol(memberDeclaration) is IMethodSymbol methodSymbol + && FindBaseMethodHiddenByMethod(methodSymbol) is { } baseMethodHidden) { - return null; + context.ReportIssue(Rule, identifier, baseMethodHidden.ToDisplayString()); } - - var baseMethodHidden = FindBaseMethodHiddenByMethod(methodSymbol); - return baseMethodHidden != null ? Diagnostic.Create(Rule, issueLocation, baseMethodHidden) : null; } private static List GetAllBaseMethods(ITypeSymbol typeSymbol) => diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotShiftByZeroOrIntSize.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotShiftByZeroOrIntSize.cs index 282556e495a..69a48d8027d 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotShiftByZeroOrIntSize.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/DoNotShiftByZeroOrIntSize.cs @@ -76,16 +76,16 @@ protected override void Initialize(SonarAnalysisContext context) => } linesWithShiftOperations.Add(shift.Line); - if (shift.Diagnostic != null) + if (shift.Node is not null) { - c.ReportIssue(shift.Diagnostic); + c.ReportIssue(Rule, shift.Node, shift.Description); } } - zeroShiftIssues - .Where(sh => !ContainsShiftExpressionWithinTwoLines(linesWithShiftOperations, sh.Line)) - .ToList() - .ForEach(sh => c.ReportIssue(sh.Diagnostic)); + foreach (var shift in zeroShiftIssues.Where(x => !ContainsShiftExpressionWithinTwoLines(linesWithShiftOperations, x.Line))) + { + c.ReportIssue(Rule, shift.Node, shift.Description); + } }, SyntaxKind.MethodDeclaration, SyntaxKind.PropertyDeclaration); @@ -196,18 +196,20 @@ private static string FindProblemDescription(int typeSizeInBits, int shiftBy, Sh private sealed class ShiftInstance { - public Diagnostic Diagnostic { get; } + public string Description { get; } + public SyntaxNode Node { get; } public bool IsLiteralZero { get; } public int Line { get; } public ShiftInstance(SyntaxNode node) => Line = node.GetLineNumberToReport(); - public ShiftInstance(string description, bool isLieralZero, SyntaxNode node) + public ShiftInstance(string description, bool isLiteralZero, SyntaxNode node) : this(node) { - Diagnostic = Diagnostic.Create(Rule, node.GetLocation(), description); - IsLiteralZero = isLieralZero; + Description = description; + IsLiteralZero = isLiteralZero; + Node = node; } } }