private static void UpdateInvocationLocation( SyntaxNodeAnalysisContext context, ISymbol symbol, SimpleNameSyntax currentAccessNode, GetAccessInfoDelegate getAccessInfo) { var symbolInfo = _applicationBuilderSymbolInfos.GetOrCreateValue(symbol); var scopeEnclosingNode = currentAccessNode.FirstAncestorOrSelf <CSharpSyntaxNode>(IsScopeEnclosingNode); lock (symbolInfo) { if (!symbolInfo.Scopes.TryGetValue(scopeEnclosingNode, out var scopeInfo)) { symbolInfo.Scopes[scopeEnclosingNode] = scopeInfo = new ScopeInfo(); } if (scopeInfo.DiagnosticReported) { return; } ref var existingAccessInfo = ref getAccessInfo(scopeInfo); if (existingAccessInfo.location == null || currentAccessNode.GetLocation().SourceSpan.Start < existingAccessInfo.location.SourceSpan.Start) { existingAccessInfo = (currentAccessNode.GetLocation(), currentAccessNode.ToString()); VerifyInvocationOrder(context, scopeInfo); } }
private static void AnalyzeSyntaxNode(SyntaxNodeAnalysisContext context) { // Skip invocations that aren't made on member access expressions. // We want something like app.Xyz() var invocationNode = (InvocationExpressionSyntax)context.Node; if (!(invocationNode.Expression is MemberAccessExpressionSyntax memberAccess)) { return; } var name = memberAccess.Name.ToString(); GetAccessInfoDelegate getAccessInfo = name == UseGoogleTrace ? scopeInfo => ref scopeInfo.UseGoogleTraceAccessInfo : name.StartsWith(UseMvc) ? scopeInfo => ref scopeInfo.UseMvcAccessInfo : default(GetAccessInfoDelegate); if (getAccessInfo == null) { return; } // Walk up a chain of method calls on IApplicationBuilder (if there is one), and see if // there is a local/parameter/field/property symbol at the end of the chain. If so, record // the name access for that symbol. var nameAccessNode = memberAccess.Name; var semanticModel = context.SemanticModel; do { var symbol = semanticModel.GetSymbolInfo(memberAccess.Expression).Symbol; // TODO: Do we want to support things that are reference convertible to IApplicationBuilder as well? // All receivers of the method invocations must be of type IApplicationBuilder. if (symbol?.Type()?.ToDisplayString() != IApplicationBuilderFullName) { break; } switch (symbol.Kind) { case SymbolKind.Local: case SymbolKind.Field: case SymbolKind.Parameter: case SymbolKind.Property: UpdateInvocationLocation(context, symbol, nameAccessNode, getAccessInfo); return; case SymbolKind.Method: // Walk up the chain... memberAccess = (memberAccess.Expression as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax; break; } }while (memberAccess != null); }