Skip to content

Commit

Permalink
Attribute.IsDefined check that second arg is attribute type. Fix DotN…
Browse files Browse the repository at this point in the history
  • Loading branch information
JohanLarsson committed Nov 3, 2018
1 parent be33eb7 commit 14b30ff
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 52 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace ReflectionAnalyzers.Tests.REFL044ExpectedAttributeTypeTests
{
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

public partial class Diagnostics
{
public class GetCustomAttribute
{
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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace ReflectionAnalyzers.Tests.REFL044ExpectedAttributeTypeTests
{
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

public partial class Diagnostics
{
public class IsDefined
{
private static readonly DiagnosticAnalyzer Analyzer = new IsDefinedAnalyzer();
private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(REFL044ExpectedAttributeType.DiagnosticId);

[Test]
public void AttributeIsDefined()
{
var code = @"
namespace RoslynSandbox
{
using System;
class C
{
public static bool M() => Attribute.IsDefined(typeof(C), ↓typeof(string));
}
}";

var message = "Expected attribute type.";
AnalyzerAssert.Diagnostics(Analyzer, ExpectedDiagnostic.WithMessage(message), code);
}

[Test]
public void IsDefinedExtensionMethod()
{
var code = @"
namespace RoslynSandbox
{
using System.Reflection;
class C
{
public static bool M() => typeof(C).IsDefined(↓typeof(string));
}
}";

var message = "Expected attribute type.";
AnalyzerAssert.Diagnostics(Analyzer, ExpectedDiagnostic.WithMessage(message), code);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ namespace ReflectionAnalyzers.Tests.REFL044ExpectedAttributeTypeTests
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Framework;

internal class ValidCode
public class ValidCode
{
private static readonly DiagnosticAnalyzer Analyzer = new GetCustomAttributeAnalyzer();
private static readonly DiagnosticDescriptor Descriptor = REFL044ExpectedAttributeType.Descriptor;

[Test]
public void WhenUsingGeneric()
public class GetCustomAttribute
{
var code = @"
private static readonly DiagnosticAnalyzer Analyzer = new GetCustomAttributeAnalyzer();
private static readonly DiagnosticDescriptor Descriptor = REFL044ExpectedAttributeType.Descriptor;

[Test]
public void WhenUsingGeneric()
{
var code = @"
namespace RoslynSandbox
{
using System;
Expand All @@ -27,13 +29,13 @@ public C()
}
}
}";
AnalyzerAssert.Valid(Analyzer, Descriptor, code);
}
AnalyzerAssert.Valid(Analyzer, Descriptor, code);
}

[Test]
public void WhenGetCustomAttributeCast()
{
var code = @"
[Test]
public void WhenGetCustomAttributeCast()
{
var code = @"
namespace RoslynSandbox
{
using System;
Expand All @@ -46,13 +48,13 @@ public C()
}
}
}";
AnalyzerAssert.Valid(Analyzer, Descriptor, code);
}
AnalyzerAssert.Valid(Analyzer, Descriptor, code);
}

[Test]
public void WhenGetCustomAttributeAs()
{
var code = @"
[Test]
public void WhenGetCustomAttributeAs()
{
var code = @"
namespace RoslynSandbox
{
using System;
Expand All @@ -66,7 +68,8 @@ public C()
}
}";

AnalyzerAssert.Valid(Analyzer, Descriptor, code);
AnalyzerAssert.Valid(Analyzer, Descriptor, code);
}
}
}
}
2 changes: 2 additions & 0 deletions ReflectionAnalyzers/Helpers/KnownSymbols/AttributeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ namespace ReflectionAnalyzers
internal class AttributeType : QualifiedType
{
internal readonly QualifiedMethod GetCustomAttribute;
internal readonly QualifiedMethod IsDefined;

internal AttributeType()
: base("System.Attribute")
{
this.GetCustomAttribute = new QualifiedMethod(this, nameof(this.GetCustomAttribute));
this.IsDefined = new QualifiedMethod(this, nameof(this.IsDefined));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ namespace ReflectionAnalyzers
internal class CustomAttributeExtensionsType : QualifiedType
{
internal readonly QualifiedMethod GetCustomAttribute;
internal readonly QualifiedMethod IsDefined;

internal CustomAttributeExtensionsType()
: base("System.Reflection.CustomAttributeExtensions")
{
this.GetCustomAttribute = new QualifiedMethod(this, nameof(this.GetCustomAttribute));
this.IsDefined = new QualifiedMethod(this, nameof(this.IsDefined));
}
}
}
}
77 changes: 77 additions & 0 deletions ReflectionAnalyzers/NodeAnalzers/IsDefinedAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
namespace ReflectionAnalyzers
{
using System.Collections.Immutable;
using Gu.Roslyn.AnalyzerExtensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

internal class IsDefinedAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
REFL044ExpectedAttributeType.Descriptor);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(Handle, SyntaxKind.InvocationExpression);
}

private static void Handle(SyntaxNodeAnalysisContext context)
{
if (!context.IsExcludedFromAnalysis() &&
context.Node is InvocationExpressionSyntax invocation &&
TryGetArgs(invocation, context, out _, out _, out var attributeType, out _))
{
if (!attributeType.Value.IsAssignableTo(context.Compilation.GetTypeByMetadataName("System.Attribute"), context.Compilation))
{
context.ReportDiagnostic(
Diagnostic.Create(
REFL044ExpectedAttributeType.Descriptor,
attributeType.Argument.GetLocation()));
}
}
}

private static bool TryGetArgs(InvocationExpressionSyntax invocation, SyntaxNodeAnalysisContext context, out IMethodSymbol target, out ExpressionSyntax member, out ArgumentAndValue<ITypeSymbol> attributeType, out ArgumentSyntax inheritsArg)
{
if ((invocation.TryGetTarget(KnownSymbol.Attribute.IsDefined, context.SemanticModel, context.CancellationToken, out target) ||
invocation.TryGetTarget(KnownSymbol.CustomAttributeExtensions.IsDefined, context.SemanticModel, context.CancellationToken, out target)) &&
target.TryFindParameter("attributeType", out var attributeTypeParameter) &&
invocation.TryFindArgument(attributeTypeParameter, out var attributeTypeArg) &&
attributeTypeArg.Expression is TypeOfExpressionSyntax typeOf &&
context.SemanticModel.TryGetType(typeOf.Type, context.CancellationToken, out var typeSymbol))
{
attributeType = new ArgumentAndValue<ITypeSymbol>(attributeTypeArg, typeSymbol);
if (!target.TryFindParameter("inherit", out var inheritParameter) ||
!invocation.TryFindArgument(inheritParameter, out inheritsArg))
{
inheritsArg = null;
}

if (target.IsExtensionMethod &&
invocation.Expression is MemberAccessExpressionSyntax memberAccess)
{
member = memberAccess.Expression;
return true;
}

if (target.TryFindParameter("element", out var elementParameter) &&
invocation.TryFindArgument(elementParameter, out var elementArg))
{
member = elementArg.Expression;
return true;
}
}

member = null;
attributeType = default(ArgumentAndValue<ITypeSymbol>);
inheritsArg = null;
return false;
}
}
}
6 changes: 5 additions & 1 deletion documentation/REFL044.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
</tr>
<tr>
<td>Code</td>
<td><a href="https://github.com/DotNetAnalyzers/ReflectionAnalyzers/blob/master/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs">GetCustomAttributeAnalyzer</a></td>
<td><a href="https://github.com/DotNetAnalyzers/ReflectionAnalyzers/blob/master/ReflectionAnalyzers/NodeAnalzers/GetCustomAttributeAnalyzer.cs">GetCustomAttributeAnalyzer</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/DotNetAnalyzers/ReflectionAnalyzers/blob/master/ReflectionAnalyzers/NodeAnalzers/IsDefinedAnalyzer.cs">IsDefinedAnalyzer</a></td>
</tr>
</table>
<!-- end generated table -->
Expand Down

0 comments on commit 14b30ff

Please sign in to comment.