Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Extract common method to check if any ancestor belongs to SyntaxKind #9202

Merged
merged 8 commits into from May 2, 2024
Expand Up @@ -93,7 +93,7 @@ public override void VisitIdentifierName(IdentifierNameSyntax node)
bool IsUsedInLocalFunction(ISymbol symbol) =>
// We don't mute it if it's declared and used in local function
!(symbol.ContainingSymbol is IMethodSymbol containingSymbol && containingSymbol.MethodKind == MethodKindEx.LocalFunction)
&& HasAncestor(node, SyntaxKindEx.LocalFunctionStatement);
&& node.HasAncestorOfKind(SyntaxKindEx.LocalFunctionStatement);

bool IsInUnsupportedExpression() =>
node.FirstAncestorOrSelf<SyntaxNode>(x => x.IsAnyKind(SyntaxKindEx.IndexExpression, SyntaxKindEx.RangeExpression)) != null;
Expand All @@ -113,18 +113,15 @@ public override void VisitVariableDeclarator(VariableDeclaratorSyntax node)

private void InspectTryCatch(SyntaxNode node)
{
if (HasAncestor(node, SyntaxKind.TryStatement))
if (node.HasAncestorOfKind(SyntaxKind.TryStatement))
{
// We're only interested in "try" and "catch" blocks. Don't count "finally" block
isInTryOrCatch = isInTryOrCatch || !HasAncestor(node, SyntaxKind.FinallyClause);
isInTryOrCatch = isInTryOrCatch || !node.HasAncestorOfKind(SyntaxKind.FinallyClause);
}
else
{
isOutsideTryCatch = true;
}
}

private static bool HasAncestor(SyntaxNode node, SyntaxKind kind) =>
node.FirstAncestorOrSelf<SyntaxNode>(x => x.IsKind(kind)) != null;
}
}
46 changes: 15 additions & 31 deletions analyzers/src/SonarAnalyzer.CSharp/Helpers/CSharpSyntaxHelper.cs
Expand Up @@ -35,15 +35,14 @@ public static class CSharpSyntaxHelper
SyntaxFacts.GetText(SyntaxKind.NameOfKeyword);

private static readonly SyntaxKind[] LiteralSyntaxKinds =
new[]
{
[
SyntaxKind.CharacterLiteralExpression,
SyntaxKind.FalseLiteralExpression,
SyntaxKind.NullLiteralExpression,
SyntaxKind.NumericLiteralExpression,
SyntaxKind.StringLiteralExpression,
SyntaxKind.TrueLiteralExpression,
};
SyntaxKind.TrueLiteralExpression
];

public static bool AnyOfKind(this IEnumerable<SyntaxNode> nodes, SyntaxKind kind) =>
nodes.Any(n => n.RawKind == (int)kind);
Expand All @@ -70,37 +69,22 @@ public static SyntaxNode GetSelfOrTopParenthesizedExpression(this SyntaxNode nod
public static SyntaxNode GetFirstNonParenthesizedParent(this SyntaxNode node) =>
node.GetSelfOrTopParenthesizedExpression().Parent;

public static bool HasAncestorOfKind(this SyntaxNode syntaxNode, params SyntaxKind[] syntaxKinds) =>
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
syntaxNode.Ancestors().Any(x => x.IsAnyKind(syntaxKinds));

public static bool IsOnThis(this ExpressionSyntax expression) =>
IsOn(expression, SyntaxKind.ThisExpression);

public static bool IsOnBase(this ExpressionSyntax expression) =>
IsOn(expression, SyntaxKind.BaseExpression);
mary-georgiou-sonarsource marked this conversation as resolved.
Show resolved Hide resolved

private static bool IsOn(this ExpressionSyntax expression, SyntaxKind onKind)
{
switch (expression.Kind())
private static bool IsOn(this ExpressionSyntax expression, SyntaxKind onKind) =>
expression.Kind() switch
{
case SyntaxKind.InvocationExpression:
return IsOn(((InvocationExpressionSyntax)expression).Expression, onKind);

case SyntaxKind.AliasQualifiedName:
case SyntaxKind.GenericName:
case SyntaxKind.IdentifierName:
case SyntaxKind.QualifiedName:
// This is a simplification as we don't check where the method is defined (so this could be this or base)
return true;

case SyntaxKind.PointerMemberAccessExpression:
case SyntaxKind.SimpleMemberAccessExpression:
return ((MemberAccessExpressionSyntax)expression).Expression.RemoveParentheses().IsKind(onKind);

case SyntaxKind.ConditionalAccessExpression:
return ((ConditionalAccessExpressionSyntax)expression).Expression.RemoveParentheses().IsKind(onKind);

default:
return false;
}
}
SyntaxKind.InvocationExpression => IsOn(((InvocationExpressionSyntax)expression).Expression, onKind),
pavel-mikula-sonarsource marked this conversation as resolved.
Show resolved Hide resolved
// Following statement is a simplification as we don't check where the method is defined (so this could be this or base)
SyntaxKind.AliasQualifiedName or SyntaxKind.GenericName or SyntaxKind.IdentifierName or SyntaxKind.QualifiedName => true,
SyntaxKind.PointerMemberAccessExpression or SyntaxKind.SimpleMemberAccessExpression => ((MemberAccessExpressionSyntax)expression).Expression.RemoveParentheses().IsKind(onKind),
SyntaxKind.ConditionalAccessExpression => ((ConditionalAccessExpressionSyntax)expression).Expression.RemoveParentheses().IsKind(onKind),
_ => false,
};

public static bool IsInNameOfArgument(this ExpressionSyntax expression, SemanticModel semanticModel)
{
Expand Down
9 changes: 4 additions & 5 deletions analyzers/src/SonarAnalyzer.CSharp/ReuseClientBase.cs
Expand Up @@ -38,17 +38,16 @@ protected bool IsReusableClient(SonarSyntaxNodeReportingContext context)
node.Parent is EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: LocalDeclarationStatementSyntax or UsingStatementSyntax } } };

private static bool IsInFieldOrPropertyInitializer(SyntaxNode node) =>
node.Ancestors().Any(x => x.IsAnyKind(SyntaxKind.FieldDeclaration, SyntaxKind.PropertyDeclaration))
&& !node.Ancestors().Any(x => x.IsAnyKind(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration))
node.HasAncestorOfKind(SyntaxKind.FieldDeclaration, SyntaxKind.PropertyDeclaration)
&& !node.HasAncestorOfKind(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration)
&& !node.Parent.IsKind(SyntaxKind.ArrowExpressionClause);

private static bool IsInConditionalCode(SyntaxNode node) =>
node.Ancestors().Any(x => x.IsAnyKind(
SyntaxKind.IfStatement,
node.HasAncestorOfKind(SyntaxKind.IfStatement,
SyntaxKind.SwitchStatement,
SyntaxKindEx.SwitchExpression,
SyntaxKind.ConditionalExpression,
SyntaxKindEx.CoalesceAssignmentExpression));
SyntaxKindEx.CoalesceAssignmentExpression);

private static bool IsAssignedToStaticFieldOrProperty(SonarSyntaxNodeReportingContext context) =>
context.Node.Parent.WalkUpParentheses() is AssignmentExpressionSyntax assignment
Expand Down
Expand Up @@ -67,5 +67,5 @@ public sealed class ControllersReuseClient : ReuseClientBase
&& method.Modifiers.Any(x => x.IsKind(SyntaxKind.PublicKeyword)));

private static bool IsInsideConstructor(SyntaxNode node) =>
node.Ancestors().Any(x => x.IsAnyKind(SyntaxKind.ConstructorDeclaration, SyntaxKindEx.PrimaryConstructorBaseType));
node.HasAncestorOfKind(SyntaxKind.ConstructorDeclaration, SyntaxKindEx.PrimaryConstructorBaseType);
}
Expand Up @@ -85,7 +85,7 @@ private static void ReportOnViolation(SonarSyntaxNodeReportingContext context)
|| IsResultInContinueWithCall(memberAccessNameName, simpleMemberAccess)
|| IsChainedAfterThreadPoolCall(context.SemanticModel, simpleMemberAccess)
|| simpleMemberAccess.IsInNameOfArgument(context.SemanticModel)
|| simpleMemberAccess.Ancestors().Any(x => x is GlobalStatementSyntax))
|| simpleMemberAccess.HasAncestorOfKind(SyntaxKind.GlobalStatement))
{
return;
}
Expand Down
Expand Up @@ -30,7 +30,7 @@ public sealed class HardcodedIpAddress : HardcodedIpAddressBase<SyntaxKind, Lite
public HardcodedIpAddress(IAnalyzerConfiguration analyzerConfiguration) : base(analyzerConfiguration) { }

protected override bool HasAttributes(SyntaxNode literalExpression) =>
literalExpression.Ancestors().AnyOfKind(SyntaxKind.Attribute);
literalExpression.HasAncestorOfKind(SyntaxKind.Attribute);

protected override string GetAssignedVariableName(SyntaxNode stringLiteral) =>
stringLiteral.FirstAncestorOrSelf<SyntaxNode>(IsVariableIdentifier)?.ToString();
Expand Down
Expand Up @@ -307,7 +307,7 @@ public override void VisitCastExpression(CastExpressionSyntax node)
}

public static bool IsTopLevel(SyntaxNode node) =>
!node.Ancestors().Any(x => x is CheckedStatementSyntax || x is CheckedExpressionSyntax);
!node.HasAncestorOfKind(SyntaxKind.CheckedStatement, SyntaxKind.CheckedExpression);

private void VisitChecked<T>(T node, SyntaxKind checkedKind, SyntaxToken tokenToReport, Action<T> baseCall)
where T : SyntaxNode
Expand Down
Expand Up @@ -85,11 +85,10 @@ public override void Visit(SyntaxNode node)
public override void VisitIdentifierName(IdentifierNameSyntax node) =>
DereferencesMethodArguments |=
argumentNames.Contains(node.GetName())
&& node.Ancestors().Any(x => x.IsAnyKind(
AwaitExpression,
&& node.HasAncestorOfKind(AwaitExpression,
ElementAccessExpression,
ForEachStatement,
ThrowStatement,
SimpleMemberAccessExpression));
SimpleMemberAccessExpression);
}
}
Expand Up @@ -23,15 +23,14 @@ namespace SonarAnalyzer.Helpers;
internal static class VisualBasicSyntaxHelper
{
private static readonly SyntaxKind[] LiteralSyntaxKinds =
new[]
{
[
SyntaxKind.CharacterLiteralExpression,
SyntaxKind.FalseLiteralExpression,
SyntaxKind.NothingLiteralExpression,
SyntaxKind.NumericLiteralExpression,
SyntaxKind.StringLiteralExpression,
SyntaxKind.TrueLiteralExpression,
};
];

public static SyntaxNode GetTopMostContainingMethod(this SyntaxNode node) =>
node.AncestorsAndSelf().LastOrDefault(ancestor => ancestor is MethodBaseSyntax || ancestor is PropertyBlockSyntax);
Expand Down Expand Up @@ -83,6 +82,9 @@ public static StatementSyntax GetSucceedingStatement(this StatementSyntax curren

#endregion Statement

public static bool HasAncestorOfKind(this SyntaxNode syntaxNode, params SyntaxKind[] syntaxKinds) =>
syntaxNode.Ancestors().Any(x => x.IsAnyKind(syntaxKinds));

public static bool IsNothingLiteral(this SyntaxNode syntaxNode) =>
syntaxNode != null && syntaxNode.IsKind(SyntaxKind.NothingLiteralExpression);

Expand Down
Expand Up @@ -30,7 +30,7 @@ public sealed class HardcodedIpAddress : HardcodedIpAddressBase<SyntaxKind, Lite
public HardcodedIpAddress(IAnalyzerConfiguration analyzerConfiguration) : base(analyzerConfiguration) { }

protected override bool HasAttributes(SyntaxNode literalExpression) =>
literalExpression.Ancestors().AnyOfKind(SyntaxKind.Attribute);
literalExpression.HasAncestorOfKind(SyntaxKind.Attribute);

protected override string GetAssignedVariableName(SyntaxNode stringLiteral) =>
stringLiteral.FirstAncestorOrSelf<SyntaxNode>(IsVariableIdentifier)?.ToString();
Expand Down
Expand Up @@ -39,7 +39,7 @@ public sealed class StringLiteralShouldNotBeDuplicated : StringLiteralShouldNotB
?? false;

protected override bool IsInnerInstance(SonarSyntaxNodeReportingContext context) =>
context.Node.Ancestors().Any(x => x is ClassBlockSyntax || x is StructureBlockSyntax);
context.Node.HasAncestorOfKind(SyntaxKind.ClassBlock, SyntaxKind.StructureBlock);

protected override IEnumerable<LiteralExpressionSyntax> FindLiteralExpressions(SyntaxNode node) =>
node.DescendantNodes(n => !n.IsKind(SyntaxKind.AttributeList))
Expand Down
Expand Up @@ -85,11 +85,10 @@ public override void Visit(SyntaxNode node)
public override void VisitIdentifierName(IdentifierNameSyntax node) =>
DereferencesMethodArguments |=
argumentNames.Contains(node.GetName())
&& node.Ancestors().Any(x => x.IsAnyKind(
AwaitExpression,
&& node.HasAncestorOfKind(AwaitExpression,
InvocationExpression, // For array access
ForEachStatement,
ThrowStatement,
SimpleMemberAccessExpression));
SimpleMemberAccessExpression);
}
}