diff --git a/analyzers/src/SonarAnalyzer.CSharp/Rules/RequireAttributeUsageAttribute.cs b/analyzers/src/SonarAnalyzer.CSharp/Rules/RequireAttributeUsageAttribute.cs index 09ea59a4130..5a701a882b8 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/Rules/RequireAttributeUsageAttribute.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/Rules/RequireAttributeUsageAttribute.cs @@ -18,47 +18,41 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -namespace SonarAnalyzer.Rules.CSharp +namespace SonarAnalyzer.Rules.CSharp; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class RequireAttributeUsageAttribute : SonarDiagnosticAnalyzer { - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public sealed class RequireAttributeUsageAttribute : SonarDiagnosticAnalyzer - { - internal const string DiagnosticId = "S3993"; - private const string MessageFormat = "Specify AttributeUsage on '{0}'{1}."; + internal const string DiagnosticId = "S3993"; + private const string MessageFormat = "Specify AttributeUsage on '{0}'{1}."; - private static readonly DiagnosticDescriptor rule = - DescriptorFactory.Create(DiagnosticId, MessageFormat); + private static readonly DiagnosticDescriptor Rule = + DescriptorFactory.Create(DiagnosticId, MessageFormat); - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(rule); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); - protected override void Initialize(SonarAnalysisContext context) + protected override void Initialize(SonarAnalysisContext context) => + context.RegisterNodeAction(c => { - context.RegisterNodeAction(c => - { - var classDeclaration = (ClassDeclarationSyntax)c.Node; - var classSymbol = c.SemanticModel.GetDeclaredSymbol(classDeclaration); - - if (classSymbol == null || - !classSymbol.DerivesFrom(KnownType.System_Attribute) || - classSymbol.HasAttribute(KnownType.System_AttributeUsageAttribute)) - { - return; - } + var classDeclaration = (ClassDeclarationSyntax)c.Node; + if (c.SemanticModel.GetDeclaredSymbol(classDeclaration) is { IsAbstract: false } classSymbol + && classSymbol.DerivesFrom(KnownType.System_Attribute) + && !classSymbol.HasAttribute(KnownType.System_AttributeUsageAttribute)) + { var additionalText = InheritsAttributeUsage(classSymbol) ? " to improve readability, even though it inherits it from its base type" : string.Empty; - c.ReportIssue(Diagnostic.Create(rule, classDeclaration.Identifier.GetLocation(), + c.ReportIssue(Diagnostic.Create(Rule, classDeclaration.Identifier.GetLocation(), classSymbol.Name, additionalText)); - }, - SyntaxKind.ClassDeclaration); - } - - private static bool InheritsAttributeUsage(INamedTypeSymbol classSymbol) => - classSymbol.GetSelfAndBaseTypes() - // System.Attribute already has AttributeUsage, we don't want to report it - .TakeWhile(t => !t.Is(KnownType.System_Attribute)) - .Any(t => t.HasAttribute(KnownType.System_AttributeUsageAttribute)); - } + } + }, + SyntaxKind.ClassDeclaration); + + private static bool InheritsAttributeUsage(INamedTypeSymbol classSymbol) => + classSymbol.GetSelfAndBaseTypes() + // System.Attribute already has AttributeUsage, we don't want to report it + .TakeWhile(x => !x.Is(KnownType.System_Attribute)) + .Any(x => x.HasAttribute(KnownType.System_AttributeUsageAttribute)); } diff --git a/analyzers/tests/SonarAnalyzer.Test/Rules/MarkAssemblyWithAttributeUsageAttributeTest.cs b/analyzers/tests/SonarAnalyzer.Test/Rules/MarkAssemblyWithAttributeUsageAttributeTest.cs index 98c99cfd699..958d1fddf28 100644 --- a/analyzers/tests/SonarAnalyzer.Test/Rules/MarkAssemblyWithAttributeUsageAttributeTest.cs +++ b/analyzers/tests/SonarAnalyzer.Test/Rules/MarkAssemblyWithAttributeUsageAttributeTest.cs @@ -20,13 +20,12 @@ using SonarAnalyzer.Rules.CSharp; -namespace SonarAnalyzer.Test.Rules +namespace SonarAnalyzer.Test.Rules; + +[TestClass] +public class MarkAssemblyWithAttributeUsageAttributeTest { - [TestClass] - public class MarkAssemblyWithAttributeUsageAttributeTest - { - [TestMethod] - public void RequireAttributeUsageAttribute() => - new VerifierBuilder().AddPaths(@"RequireAttributeUsageAttribute.cs").Verify(); - } + [TestMethod] + public void RequireAttributeUsageAttribute() => + new VerifierBuilder().AddPaths(@"RequireAttributeUsageAttribute.cs").Verify(); } diff --git a/analyzers/tests/SonarAnalyzer.Test/Rules/RequireAttributeUsageAttributeTest.cs b/analyzers/tests/SonarAnalyzer.Test/Rules/RequireAttributeUsageAttributeTest.cs index a8cd70f4763..bae8cb91e00 100644 --- a/analyzers/tests/SonarAnalyzer.Test/Rules/RequireAttributeUsageAttributeTest.cs +++ b/analyzers/tests/SonarAnalyzer.Test/Rules/RequireAttributeUsageAttributeTest.cs @@ -20,26 +20,25 @@ using SonarAnalyzer.Rules.CSharp; -namespace SonarAnalyzer.Test.Rules +namespace SonarAnalyzer.Test.Rules; + +[TestClass] +public class RequireAttributeUsageAttributeTest { - [TestClass] - public class RequireAttributeUsageAttributeTest - { - private readonly VerifierBuilder builder = new VerifierBuilder(); + private readonly VerifierBuilder builder = new VerifierBuilder(); - [TestMethod] - public void RequireAttributeUsageAttribute() => - builder.AddPaths("RequireAttributeUsageAttribute.cs").Verify(); + [TestMethod] + public void RequireAttributeUsageAttribute() => + builder.AddPaths("RequireAttributeUsageAttribute.cs").Verify(); #if NET - [TestMethod] - public void RequireAttributeUsageAttribute_CSharp11() => - builder.AddPaths("RequireAttributeUsageAttribute.CSharp11.cs") - .WithOptions(ParseOptionsHelper.FromCSharp11) - .Verify(); + [TestMethod] + public void RequireAttributeUsageAttribute_CSharp11() => + builder.AddPaths("RequireAttributeUsageAttribute.CSharp11.cs") + .WithOptions(ParseOptionsHelper.FromCSharp11) + .Verify(); #endif - } } diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/RequireAttributeUsageAttribute.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/RequireAttributeUsageAttribute.cs index 7a31635c03e..dc3ad38dca6 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/RequireAttributeUsageAttribute.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/RequireAttributeUsageAttribute.cs @@ -1,24 +1,25 @@ using System; -namespace Tests.Diagnostics +public class MyInvalidAttribute : Attribute +// ^^^^^^^^^^^^^^^^^^ {{Specify AttributeUsage on 'MyInvalidAttribute'.}} { - public class MyInvalidAttribute : Attribute -// ^^^^^^^^^^^^^^^^^^ {{Specify AttributeUsage on 'MyInvalidAttribute'.}} - { - } +} - [AttributeUsage(AttributeTargets.Class)] - public class MyCompliantAttribute : Attribute - { - } +[AttributeUsage(AttributeTargets.Class)] +public class MyCompliantAttribute : Attribute +{ +} - public class MyInvalidInheritedAttribute : MyCompliantAttribute -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Specify AttributeUsage on 'MyInvalidInheritedAttribute' to improve readability, even though it inherits it from its base type.}} - { - } +public class MyInvalidInheritedAttribute : MyCompliantAttribute +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ {{Specify AttributeUsage on 'MyInvalidInheritedAttribute' to improve readability, even though it inherits it from its base type.}} +{ +} - [AttributeUsage(AttributeTargets.Class)] - public class MyInheritedAttribute : MyCompliantAttribute - { - } +[AttributeUsage(AttributeTargets.Class)] +public class MyInheritedAttribute : MyCompliantAttribute +{ +} + +public abstract class AbstractAttribute : Attribute // Compliant +{ }