in src/Microsoft.VisualStudio.SDK.Analyzers/VSSDK006CheckServicesExistAnalyzer.cs [87:172]
internal void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context)
{
var invocationExpression = (InvocationExpressionSyntax)context.Node;
var invokedMethod = context.SemanticModel.GetSymbolInfo(invocationExpression.Expression, context.CancellationToken).Symbol as IMethodSymbol;
if (invokedMethod != null && this.getServiceMethods.Contains(invokedMethod.ReducedFrom ?? invokedMethod))
{
bool isTask = Utils.IsTask(invokedMethod.ReturnType);
SyntaxNode? startWalkFrom = isTask
? (SyntaxNode?)Utils.FindAncestor<AwaitExpressionSyntax>(invocationExpression, n => n is MemberAccessExpressionSyntax || n is InvocationExpressionSyntax, (aes, child) => aes.Expression == child)
: invocationExpression;
if (startWalkFrom == null)
{
return;
}
AssignmentExpressionSyntax? assignment;
VariableDeclaratorSyntax? variableDeclarator;
if (Utils.FindAncestor<MemberAccessExpressionSyntax>(
startWalkFrom,
n => n is CastExpressionSyntax || n is ParenthesizedExpressionSyntax || n is AwaitExpressionSyntax,
(mae, child) => mae.Expression == child) != null)
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, invocationExpression.Expression.GetLocation()));
}
else if ((assignment = Utils.FindAncestor<AssignmentExpressionSyntax>(
startWalkFrom,
n => n is CastExpressionSyntax || n is EqualsValueClauseSyntax || n is AwaitExpressionSyntax || (n is BinaryExpressionSyntax be && be.OperatorToken.IsKind(SyntaxKind.AsKeyword)),
(aes, child) => aes.Right == child)) != null)
{
ISymbol leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left, context.CancellationToken).Symbol;
if (leftSymbol is object)
{
// If the assigned variable is actually a field, scan this block for Assumes.Present
SyntaxNode? parentBlock = Utils.FindFirstAncestorOfTypes(invocationExpression, typeof(BlockSyntax), typeof(ArrowExpressionClauseSyntax));
if (!parentBlock?.DescendantNodes().Any(n => this.IsThrowingNullCheck(n, leftSymbol, context)) ?? true)
{
// Since we didn't find an Assumes.Present call for this symbol,
// if this is a field or property, scan all blocks and expression bodies within this type.
// otherwise just scan the blocks under this one.
System.Collections.Generic.IEnumerable<Location> derefs;
if (leftSymbol is IFieldSymbol || leftSymbol is IPropertySymbol)
{
derefs = from member in leftSymbol.ContainingType.GetMembers().OfType<IMethodSymbol>()
from syntaxRef in member.DeclaringSyntaxReferences
let methodSyntax = syntaxRef.GetSyntax(context.CancellationToken) as MethodDeclarationSyntax
where methodSyntax != null
let bodyOrExpression = (SyntaxNode)methodSyntax.Body ?? methodSyntax.ExpressionBody
where bodyOrExpression != null
from dref in this.ScanBlockForDereferencesWithoutNullCheck(context, leftSymbol, bodyOrExpression)
select dref;
}
else if (parentBlock is object)
{
derefs = this.ScanBlockForDereferencesWithoutNullCheck(context, leftSymbol, parentBlock);
}
else
{
derefs = Enumerable.Empty<Location>();
}
if (derefs.Any())
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, assignment.Left.GetLocation(), derefs));
}
}
}
}
else if ((variableDeclarator = Utils.FindAncestor<VariableDeclaratorSyntax>(
startWalkFrom,
n => n is CastExpressionSyntax || n is EqualsValueClauseSyntax || n is AwaitExpressionSyntax || (n is BinaryExpressionSyntax be && be.OperatorToken.IsKind(SyntaxKind.AsKeyword)),
(vds, child) => vds.Initializer == child)) != null)
{
// The GetService call was assigned via an initializer to a new local variable. Search the code block for uses and null checks.
var leftSymbol = context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken) as ILocalSymbol;
if (leftSymbol != null)
{
BlockSyntax containingBlock = context.Node.FirstAncestorOrSelf<BlockSyntax>();
ImmutableArray<Location> derefs = this.ScanBlockForDereferencesWithoutNullCheck(context, leftSymbol, containingBlock);
if (derefs.Any())
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, variableDeclarator.Identifier.GetLocation(), derefs));
}
}
}
}
}