Skip to content

Commit dfeab26

Browse files
committed
GetConstructor
1 parent dffcf6e commit dfeab26

4 files changed

Lines changed: 82 additions & 19 deletions

File tree

ReflectionAnalyzers.Tests/Helpers/Reflection/GetXTests.GetMethodOverloadResolution.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace ReflectionAnalyzers;
2+
3+
using System.Threading;
4+
5+
using Gu.Roslyn.AnalyzerExtensions;
6+
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.CSharp.Syntax;
10+
11+
internal readonly struct GetConstructor
12+
{
13+
internal readonly InvocationExpressionSyntax Invocation;
14+
internal readonly IMethodSymbol Target;
15+
internal readonly ReflectedMember Member;
16+
internal readonly Flags Flags;
17+
internal readonly Types Types;
18+
19+
private GetConstructor(InvocationExpressionSyntax invocation, IMethodSymbol target, ReflectedMember member, Flags flags, Types types)
20+
{
21+
this.Invocation = invocation;
22+
this.Target = target;
23+
this.Member = member;
24+
this.Flags = flags;
25+
this.Types = types;
26+
}
27+
28+
internal IMethodSymbol? Single => this.Member.Match == FilterMatch.Single ? (IMethodSymbol)this.Member.Symbol! : null;
29+
30+
/// <summary>
31+
/// Check if <paramref name="candidate"/> is a call to Type.GetConstructor.
32+
/// </summary>
33+
internal static GetConstructor? Match(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
34+
{
35+
return candidate switch
36+
{
37+
InvocationExpressionSyntax invocation
38+
=> Match(invocation, semanticModel, cancellationToken),
39+
PostfixUnaryExpressionSyntax { RawKind: (int)SyntaxKind.SuppressNullableWarningExpression, Operand: InvocationExpressionSyntax invocation }
40+
=> Match(invocation, semanticModel, cancellationToken),
41+
MemberAccessExpressionSyntax { Expression: { } inner }
42+
=> Match(inner, semanticModel, cancellationToken),
43+
IdentifierNameSyntax identifierName
44+
when semanticModel.TryGetSymbol(identifierName, cancellationToken, out ILocalSymbol? local) &&
45+
AssignedValue.FindSingle(local, semanticModel, cancellationToken) is { } value
46+
=> Match(value, semanticModel, cancellationToken),
47+
_ => null,
48+
};
49+
}
50+
51+
/// <summary>
52+
/// Check if <paramref name="candidate"/> is a call to Type.GetConstructor.
53+
/// </summary>
54+
internal static GetConstructor? Match(InvocationExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
55+
{
56+
if (candidate.TryGetTarget(KnownSymbol.Type.GetConstructor, semanticModel, cancellationToken, out var target))
57+
{
58+
if (ReflectedMember.TryGetType(candidate, semanticModel, cancellationToken, out var type, out var typeSource) &&
59+
Flags.TryCreate(candidate, target, semanticModel, cancellationToken, out var flags) &&
60+
Types.TryCreate(candidate, target, semanticModel, cancellationToken, out var types))
61+
{
62+
return ReflectedMember.TryCreate(target, candidate, type, typeSource, Name.Ctor, flags.Effective, types, semanticModel.Compilation, out var member)
63+
? new GetConstructor(candidate, target, member, flags, types)
64+
: null;
65+
}
66+
67+
if (Flags.TryCreate(candidate, target, semanticModel, cancellationToken, out flags) &&
68+
flags.AreInSufficient)
69+
{
70+
var member = new ReflectedMember(type, typeSource, null, target, candidate, FilterMatch.InSufficientFlags);
71+
_ = Types.TryCreate(candidate, target, semanticModel, cancellationToken, out types);
72+
return new GetConstructor(candidate, target, member, flags, types);
73+
}
74+
}
75+
76+
return null;
77+
}
78+
}

ReflectionAnalyzers/NodeAnalzers/GetXAnalyzer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ private static bool ShouldUseSameTypeAsParameter(ReflectedMember member, Types t
675675

676676
for (var i = 0; i < method.Parameters.Length; i++)
677677
{
678-
if (!TypeSymbolComparer.Equal(types.Symbols[i], Effective(method.Parameters[i].Type)) &&
678+
if (!TypeSymbolComparer.Equal(types.Symbols[i], EffectiveType(method.Parameters[i].Type)) &&
679679
context.SemanticModel.IsAccessible(context.Node.SpanStart, method.Parameters[i].Type))
680680
{
681681
typeText = method.Parameters[i].Type.ToString(context);
@@ -688,7 +688,7 @@ private static bool ShouldUseSameTypeAsParameter(ReflectedMember member, Types t
688688
return true;
689689
}
690690

691-
ITypeSymbol Effective(ITypeSymbol t) =>
691+
static ITypeSymbol EffectiveType(ITypeSymbol t) =>
692692
t.NullableAnnotation == NullableAnnotation.Annotated
693693
? t.WithNullableAnnotation(NullableAnnotation.None)
694694
: t;

ReflectionAnalyzers/NodeAnalzers/InvokeAnalyzer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public override void Initialize(AnalysisContext context)
3333
private static void Handle(SyntaxNodeAnalysisContext context)
3434
{
3535
if (!context.IsExcludedFromAnalysis() &&
36-
context.Node is InvocationExpressionSyntax { ArgumentList: { }, Expression: MemberAccessExpressionSyntax memberAccess } invocation &&
36+
context.Node is InvocationExpressionSyntax { ArgumentList: { } } invocation &&
3737
invocation.TryGetMethodName(out var name) &&
3838
name == "Invoke" &&
3939
context.SemanticModel.TryGetSymbol(invocation, context.CancellationToken, out var invoke) &&
@@ -140,7 +140,7 @@ type is IArrayTypeSymbol arrayType &&
140140
}
141141
}
142142
}
143-
else if (GetX.TryGetConstructorInfo(memberAccess, context.SemanticModel, context.CancellationToken, out var ctor))
143+
else if (GetConstructor.Match(invocation.Expression, context.SemanticModel, context.CancellationToken) is { Single: { } ctor })
144144
{
145145
if (ReturnValue.ShouldCast(invocation, ctor.ReturnType, context.SemanticModel))
146146
{

0 commit comments

Comments
 (0)