// We have a lambda; we want to find a syntax node or statement which can be bound such that // we can get the type of the lambda, if there is one. For example if given // // A().B(x=>M(x)).C(); then we want to find A().B(x=>M()) // object d = (D)(x=>M(x)); then we want to find (D)(x=>M(x)) // D d = x=>M(x); then we want to find the whole thing. // protected virtual SyntaxNode GetBindableSyntaxNodeOfLambdaOrQuery(SyntaxNode node) { Debug.Assert(node != null); Debug.Assert(node != this.Root); Debug.Assert(node.IsAnonymousFunction() || node.IsQuery()); SyntaxNode current = node.Parent; for (; current != this.Root; current = current.Parent) { Debug.Assert(current != null, "How did we get outside the root?"); if (current is StatementSyntax) { return(current); } if (current.Kind == SyntaxKind.ParenthesizedExpression) { continue; } if (current is ExpressionSyntax) { return(GetBindableSyntaxNode(current)); } } // We made it up to the root without finding a viable expression or statement. Just bind // the lambda and hope for the best. return(node); }
/// <summary> /// Performs the same function as GetEnclosingBinder, but is known to take place within a /// specified lambda. Walks up the syntax hierarchy until a node with an associated binder /// is found. /// </summary> /// <remarks> /// CONSIDER: can this share code with MemberSemanticModel.GetEnclosingBinder? /// </remarks> private Binder GetLambdaEnclosingBinder(int position, SyntaxNode startingNode, SyntaxNode containingLambda, ExecutableCodeBinder lambdaBinder) { AssertPositionAdjusted(position); Debug.Assert(containingLambda.IsAnonymousFunction()); Debug.Assert(LookupPosition.IsInAnonymousFunctionOrQuery(position, containingLambda)); var current = startingNode; while (current != containingLambda) { Debug.Assert(current != null); StatementSyntax stmt = current as StatementSyntax; if (stmt != null) { if (LookupPosition.IsInStatementScope(position, stmt)) { Binder binder = lambdaBinder.GetBinder(current); if (binder != null) { return(binder); } } } else if (current.Kind == SyntaxKind.CatchClause) { if (LookupPosition.IsInCatchClauseScope(position, (CatchClauseSyntax)current)) { Binder binder = lambdaBinder.GetBinder(current); if (binder != null) { return(binder); } } } else if (current.IsAnonymousFunction()) { if (LookupPosition.IsInAnonymousFunctionOrQuery(position, current)) { Binder binder = lambdaBinder.GetBinder(current); if (binder != null) { return(binder); } } } else { // If this ever breaks, make sure that all callers of // CanHaveAssociatedLocalBinder are in sync. Debug.Assert(!current.CanHaveAssociatedLocalBinder()); } current = current.Parent; } return(lambdaBinder); }
private static bool NodeIsExplicitType(SyntaxNode node, SyntaxNode lambda) { Debug.Assert(node != null); Debug.Assert(lambda != null); Debug.Assert(lambda.IsAnonymousFunction() || lambda.IsQuery()); // UNDONE; return(false); }
internal override BoundNode GetBoundNode(SyntaxNode node) { // If this method is called with a null parameter, that implies that the Root should be // bound, but make sure that the Root is bindable. if (node == null) { node = GetBindableSyntaxNode(Root); } Debug.Assert(node == GetBindableSyntaxNode(node)); // We have one SemanticModel for each method. // // The SemanticModel contains a lazily-built immutable map from scope-introducing // syntactic statements (such as blocks) to binders, but not from lambdas to binders. // // The SemanticModel also contains a mutable map from syntax to bound nodes; that is // declared here. Since the map is not thread-safe we ensure that it is guarded with a // reader-writer lock. // // Have we already got the desired bound node in the mutable map? If so, return it. BoundNode result = GetBoundNodeFromMap(node); if (result != null) { return(result); } // We didn't have it in the map. Obtain a binder suitable for obtaining the answer. Binder binder = GetEnclosingBinder(GetAdjustedNodePosition(node)); Debug.Assert(binder != null); // If the binder we just obtained is for a child lambda then by obtaining the binder we // might have as a side effect caused the map to be populated with the answer we seek. // Check again. result = GetBoundNodeFromMap(node); if (result != null) { return(result); } // We might not actually have been given an expression or statement even though we were // allegedly given something that is "bindable". SyntaxNode nodeToBind = GetExpressionOrStatement(node); // We are in one of the scenarios where binding the lambda does not populate the map // with the desired node. Bind the node in the binder, and then add to the map every // syntax/bound node pair in the resulting bound node and its children. // // It is possible that we're being asked to bind an entire outermost lambda. For example: // // void M() { Func<int, int> f = x=>x+1; } // bind this ^----^ // // In that case the enclosing binder is the body binder for M, but we do not simply want // to bind the expression "x=>x+1". Rather, we want to bind the whole statement // enclosing it, so that we have the right type for x. if (nodeToBind.IsAnonymousFunction() && GetInnermostLambdaOrQuery(nodeToBind, node.Span.Start) == null) { nodeToBind = GetBindableSyntaxNodeOfLambdaOrQuery(nodeToBind); } BoundNode boundRoot = this.Bind(binder, nodeToBind, this.diagnostics); Debug.Assert(boundRoot != null); result = AddBoundTreeAndGetBoundNodeFromMap(node, boundRoot); // Special case: // // We might be in a "Color Color" scenario. That is, suppose we have been asked to bind // "Color" in "x = Color.Red;" or in "x = Color.ToString();" The former could be the // type "Color" and the latter could be the property "this.Color" or a local variable // named Color of type Color. // // We've just bound "Color"; perhaps it resolved to be the property. Now bind // "Color.Red" and re-do the mapping. That might replace the node in the map with the // right value, or it might leave it the same, or it might leave it untouched, if the // original syntax node does not appear in the new bound tree. Either way, we'll have // more accurate information in the tree for this case. if (node != this.Root && node.Parent != null && node.Parent.Kind == SyntaxKind.MemberAccessExpression && node.Kind == SyntaxKind.IdentifierName) { var memberAccess = (MemberAccessExpressionSyntax)node.Parent; if (memberAccess.Name.Kind == SyntaxKind.IdentifierName) { BoundNode boundMemberAccess = this.Bind(binder, memberAccess, this.diagnostics); result = AddBoundTreeAndGetBoundNodeFromMap(node, boundMemberAccess); } } // It is *still* possible that we haven't gotten the result. For example, consider // something like "int x = (a + b) * c;" If we ask for binding information on the // parenthetical expression then we'll get back a bound node for the syntax "a + b", not // the syntax "(a + b)"; the binder generates no bound state for the parenthetical. The // best we can do in this bad situation is to use the expression we just bound. Add it // to the map in case we're asked again. if (result == null) { AddBoundNodeToMap(node, boundRoot); result = boundRoot; } Debug.Assert(result != null); return(result); }