Skip to content

Commit

Permalink
UtilityAnalyzer: Optimize SimpleMemberAccessExpression handling for t…
Browse files Browse the repository at this point in the history
…he TokenTypeAnalyzer (#7810)
  • Loading branch information
martin-strecker-sonarsource authored Aug 18, 2023
1 parent 287d36a commit 3d0f443
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -173,17 +173,29 @@ private bool IsValueParameterOfSetter(SimpleNameSyntax simpleName)

[PerformanceSensitive("https://github.com/SonarSource/sonar-dotnet/issues/7805", AllowCaptures = false, AllowGenericEnumeration = false, AllowImplicitBoxing = false)]
private TokenType? ClassifyMemberAccess(SimpleNameSyntax name) =>
// Most right hand side of a member access?
name is
name switch
{
Parent: MemberAccessExpressionSyntax
{
Parent: not MemberAccessExpressionSyntax, // Topmost in a memberaccess tree
Name: { } parentName // Right hand side
} parent
} && parentName == name
? ClassifySimpleNameExpressionSpecialContext(parent, name)
: ClassifyIdentifierByModel(name);
Parent: MemberAccessExpressionSyntax // Most right hand side of a member access?
{
Parent: not MemberAccessExpressionSyntax, // Topmost in a memberaccess tree
Name: { } parentName // Right hand side
} parent
} when parentName == name => ClassifySimpleNameExpressionSpecialContext(parent, name),
// 'name' can not be a nested type, if there is an expression to the left of the member access,
// that can not bind to a type. The only things that can bind to a type are SimpleNames (Identifier or GenericName)
// or pre-defined types. None of the pre-defined types have a nested type, so we can exclude these as well.
{ Parent: MemberAccessExpressionSyntax x } when AnyMemberAccessLeftIsNotASimpleName(x) => TokenType.UnknownTokentype,
_ => ClassifyIdentifierByModel(name),
};

private static bool AnyMemberAccessLeftIsNotASimpleName(MemberAccessExpressionSyntax memberAccess) =>
memberAccess switch
{
{ Expression: not SimpleNameSyntax and not MemberAccessExpressionSyntax } => true,
{ Expression: MemberAccessExpressionSyntax left } => AnyMemberAccessLeftIsNotASimpleName(left),
_ => false,
};

private TokenType ClassifyIdentifierByModel(SimpleNameSyntax x) =>
SemanticModel.GetSymbolInfo(x).Symbol is INamedTypeSymbol or ITypeParameterSymbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,16 +871,17 @@ public class Test
[DataRow("[u:Prop]?.[u:InstanceProp]?.[u:InstanceProp]", false)] // Can not be a type on the left side of a ?. or on the right
[DataRow("global::[t:A].StaticProp", true)] // Can be a namespace or type
[DataRow("this.[u:Prop]", false)] // Right of this: must be a property/field
[DataRow("this.[u:Prop].[u:InstanceProp].[u:InstanceProp]", true)] // TODO: false, Right of this: must be properties or fields
[DataRow("(true ? Prop : Prop).[u:InstanceProp].[u:InstanceProp]", true)] // TODO: false, Right of some expression: must be properties or fields
[DataRow("int.[u:MaxValue]", false)] // Right of pre-defined type: must be a property/field because pre-defined types do not have nested types
[DataRow("this.[u:Prop].[u:InstanceProp].[u:InstanceProp]", false)] // must be properties or fields
[DataRow("(true ? Prop : Prop).[u:InstanceProp].[u:InstanceProp]", false)] // Right of some expression: must be properties or fields
[DataRow("[t:A]<int>.StaticProp", true)] // TODO: false, Generic name. Must be a type because not in an invocation context, like A<int>()
[DataRow("A<int>.[u:StaticProp]", false)] // Most right hand side
[DataRow("A<int>.[u:StaticProp].InstanceProp", true)] // Not the right hand side, could be a nested type
[DataRow("A<int>.[t:B].StaticProp", true)] // Not the right hand side, is a nested type
[DataRow("[t:A]<int>.[u:StaticProp]?.[u:InstanceProp]", true)] // TODO: false, Can all be infered from the positions
[DataRow("[t:A]<int>.[t:B]<int>.[u:StaticProp]", true)] // TODO: false, Generic names must be types and StaticProp is most right hand side
[DataRow("[t:A]<int>.[u:StaticM]<int>().[u:InstanceProp]", true)] // TODO: false, A must be a type StaticM is invoked and InstanceProp is after the invocation
[DataRow("A<int>.StaticM<int>().[u:InstanceProp].InstanceProp", true)] // TODO: false, Is right from invocation
[DataRow("A<int>.StaticM<int>().[u:InstanceProp].InstanceProp", false)] // Is right from invocation
public void IdentifierToken_SimpleMemberAccess_InOrdinaryExpression(string memberAccessExpression, bool allowSemanticModel) =>
ClassifierTestHarness.AssertTokenTypes($$"""
public class A
Expand Down Expand Up @@ -923,6 +924,29 @@ public void M()
}
""", allowSemanticModel);

[DataTestMethod]
[DataRow("M([u:MethodGroup]<int>);", false)]
[DataRow("M([u:MethodGroup]<T>);", false)]
[DataRow("M(C<T>.[u:StaticMethodGroup]<T>);", false)]
[DataRow("M([t:C]<T>.StaticMethodGroup<T>);", true)] // TODO false, must be a type
public void IdentifierToken_SimpleMemberAccess_GenericMethodGroup(string invocation, bool allowSemanticModel)
{
ClassifierTestHarness.AssertTokenTypes($$"""
using System;

public class C<T> {
public void M(Action a)
{
{{invocation}}
}

public void MethodGroup<TM>() { }

public static void StaticMethodGroup<TM>() { }
}
""", allowSemanticModel);
}

[DataTestMethod]
[DataRow("_ = [u:i];")]
[DataRow("[u:i] = [u:i] + [u:i];")]
Expand Down Expand Up @@ -991,7 +1015,7 @@ public void M()
[DataRow("_ = [u:i] > [u:i];")]
[DataRow("_ = [u:i] >= [u:i];")]
[DataRow("_ = [u:i] is iConst;")] // iConst could be a type
[DataRow("_ = [u:ex] as [t:ArgumentException];", true)] // TODO: false. Is known to be a type
[DataRow("_ = [u:ex] as [t:ArgumentException];")]
[DataRow("_ = [u:ex] ?? [u:ex];")]
[DataRow("i += [u:i];")]
[DataRow("i -= [u:i];")]
Expand Down

0 comments on commit 3d0f443

Please sign in to comment.