From be33eb7e219c916c435b069e115d9bf39ae3ab2e Mon Sep 17 00:00:00 2001 From: JohanLarsson Date: Sat, 3 Nov 2018 15:52:58 +0100 Subject: [PATCH] REFL044 Expected attribute type. #182. --- README.md | 4 ++ .../CodeFix.cs | 30 ++++++++ .../ValidCode.cs | 72 +++++++++++++++++++ ReflectionAnalyzers.sln | 1 + .../GetCustomAttributeAnalyzer.cs | 11 ++- .../REFL044ExpectedAttributeType.cs | 19 +++++ documentation/REFL044.md | 67 +++++++++++++++++ 7 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/CodeFix.cs create mode 100644 ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/ValidCode.cs create mode 100644 ReflectionAnalyzers/REFL044ExpectedAttributeType.cs create mode 100644 documentation/REFL044.md diff --git a/README.md b/README.md index 13517494..1277d4a7 100644 --- a/README.md +++ b/README.md @@ -176,5 +176,9 @@ Analyzers checking System.Reflection REFL043 First argument must match type. + + REFL044 + Expected attribute type. + diff --git a/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/CodeFix.cs b/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/CodeFix.cs new file mode 100644 index 00000000..c449e8f5 --- /dev/null +++ b/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/CodeFix.cs @@ -0,0 +1,30 @@ +namespace ReflectionAnalyzers.Tests.REFL044ExpectedAttributeTypeTests +{ + using Gu.Roslyn.Asserts; + using Microsoft.CodeAnalysis.Diagnostics; + using NUnit.Framework; + + internal class CodeFix + { + private static readonly DiagnosticAnalyzer Analyzer = new GetCustomAttributeAnalyzer(); + private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(REFL044ExpectedAttributeType.DiagnosticId); + + [Test] + public void AttributeGetCustomAttribute() + { + var code = @" +namespace RoslynSandbox +{ + using System; + + class C + { + public static bool Bar() => Attribute.GetCustomAttribute(typeof(C), ↓typeof(string)) == null; + } +}"; + + var message = "Expected attribute type."; + AnalyzerAssert.Diagnostics(Analyzer, ExpectedDiagnostic.WithMessage(message), code); + } + } +} diff --git a/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/ValidCode.cs b/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/ValidCode.cs new file mode 100644 index 00000000..a3ccb4dc --- /dev/null +++ b/ReflectionAnalyzers.Tests/REFL044ExpectedAttributeTypeTests/ValidCode.cs @@ -0,0 +1,72 @@ +namespace ReflectionAnalyzers.Tests.REFL044ExpectedAttributeTypeTests +{ + using Gu.Roslyn.Asserts; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + using NUnit.Framework; + + internal class ValidCode + { + private static readonly DiagnosticAnalyzer Analyzer = new GetCustomAttributeAnalyzer(); + private static readonly DiagnosticDescriptor Descriptor = REFL044ExpectedAttributeType.Descriptor; + + [Test] + public void WhenUsingGeneric() + { + var code = @" +namespace RoslynSandbox +{ + using System; + using System.Reflection; + + class C + { + public C() + { + var attribute = typeof(C).GetCustomAttribute(); + } + } +}"; + AnalyzerAssert.Valid(Analyzer, Descriptor, code); + } + + [Test] + public void WhenGetCustomAttributeCast() + { + var code = @" +namespace RoslynSandbox +{ + using System; + + class C + { + public C() + { + var attribute = (ObsoleteAttribute)Attribute.GetCustomAttribute(typeof(C), typeof(ObsoleteAttribute)); + } + } +}"; + AnalyzerAssert.Valid(Analyzer, Descriptor, code); + } + + [Test] + public void WhenGetCustomAttributeAs() + { + var code = @" +namespace RoslynSandbox +{ + using System; + + class C + { + public C() + { + var attribute = Attribute.GetCustomAttribute(typeof(C), typeof(ObsoleteAttribute)) as ObsoleteAttribute; + } + } +}"; + + AnalyzerAssert.Valid(Analyzer, Descriptor, code); + } + } +} diff --git a/ReflectionAnalyzers.sln b/ReflectionAnalyzers.sln index 52567709..e6420fd7 100644 --- a/ReflectionAnalyzers.sln +++ b/ReflectionAnalyzers.sln @@ -74,6 +74,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docs", ".docs", "{1C271AF2 documentation\REFL041.md = documentation\REFL041.md documentation\REFL042.md = documentation\REFL042.md documentation\REFL043.md = documentation\REFL043.md + documentation\REFL044.md = documentation\REFL044.md RELEASE_NOTES.md = RELEASE_NOTES.md EndProjectSection EndProject diff --git a/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs b/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs index acf593a1..8a6f1799 100644 --- a/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs +++ b/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs @@ -13,7 +13,8 @@ internal class GetCustomAttributeAnalyzer : DiagnosticAnalyzer /// public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( REFL010PreferGenericGetCustomAttribute.Descriptor, - REFL012PreferIsDefined.Descriptor); + REFL012PreferIsDefined.Descriptor, + REFL044ExpectedAttributeType.Descriptor); /// public override void Initialize(AnalysisContext context) @@ -51,6 +52,14 @@ context.Node is InvocationExpressionSyntax invocation && nameof(InvocationExpressionSyntax), invocationText))); } + + if (!attributeType.Value.IsAssignableTo(context.Compilation.GetTypeByMetadataName("System.Attribute"), context.Compilation)) + { + context.ReportDiagnostic( + Diagnostic.Create( + REFL044ExpectedAttributeType.Descriptor, + attributeType.Argument.GetLocation())); + } } } diff --git a/ReflectionAnalyzers/REFL044ExpectedAttributeType.cs b/ReflectionAnalyzers/REFL044ExpectedAttributeType.cs new file mode 100644 index 00000000..a3bfe8d1 --- /dev/null +++ b/ReflectionAnalyzers/REFL044ExpectedAttributeType.cs @@ -0,0 +1,19 @@ +namespace ReflectionAnalyzers +{ + using Microsoft.CodeAnalysis; + + internal static class REFL044ExpectedAttributeType + { + internal const string DiagnosticId = "REFL044"; + + internal static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + id: DiagnosticId, + title: "Expected attribute type.", + messageFormat: "Expected attribute type.", + category: AnalyzerCategory.SystemReflection, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "Expected attribute type.", + helpLinkUri: HelpLink.ForId(DiagnosticId)); + } +} diff --git a/documentation/REFL044.md b/documentation/REFL044.md new file mode 100644 index 00000000..bbc43f02 --- /dev/null +++ b/documentation/REFL044.md @@ -0,0 +1,67 @@ +# REFL044 +## Expected attribute type. + + +
+ + + + + + + + + + + + + + + + + + + + +
CheckIdREFL044
SeverityWarning
Enabledtrue
CategoryReflectionAnalyzers.SystemReflection
CodeGetCustomAttributeAnalyzer
+ + +## Description + +Expected attribute type. + +## Motivation + +ADD MOTIVATION HERE + +## How to fix violations + +ADD HOW TO FIX VIOLATIONS HERE + + +## Configure severity + +### Via ruleset file. + +Configure the severity per project, for more info see [MSDN](https://msdn.microsoft.com/en-us/library/dd264949.aspx). + +### Via #pragma directive. +```C# +#pragma warning disable REFL044 // Expected attribute type. +Code violating the rule here +#pragma warning restore REFL044 // Expected attribute type. +``` + +Or put this at the top of the file to disable all instances. +```C# +#pragma warning disable REFL044 // Expected attribute type. +``` + +### Via attribute `[SuppressMessage]`. + +```C# +[System.Diagnostics.CodeAnalysis.SuppressMessage("ReflectionAnalyzers.SystemReflection", + "REFL044:Expected attribute type.", + Justification = "Reason...")] +``` + \ No newline at end of file