public void GetBoundNodes(LanguageSyntaxNode node, out LanguageSyntaxNode bindableNode, out BoundNode lowestBoundNode, out BoundNode highestBoundNode, out BoundNode boundParent) { bindableNode = this.GetBindableSyntaxNode(node); LanguageSyntaxNode bindableParent = this.GetBindableParentNode(bindableNode); /* TODO:MetaDslx * // Special handling for the Color Color case. * // * // Suppose we have: * // public class Color { * // public void M(int x) {} * // public static void M(params int[] x) {} * // } * // public class C { * // public void Test() { * // Color Color = new Color(); * // System.Action<int> d = Color.M; * // } * // } * // * // We actually don't know how to interpret the "Color" in "Color.M" until we * // perform overload resolution on the method group. Now, if we were getting * // the semantic info for the method group, then bindableParent would be the * // variable declarator "d = Color.M" and so we would be able to pull the result * // of overload resolution out of the bound (method group) conversion. However, * // if we are getting the semantic info for just the "Color" part, then * // bindableParent will be the member access, which doesn't have enough information * // to determine which "Color" to use (since no overload resolution has been * // performed). We resolve this problem by detecting the case where we're looking * // up the LHS of a member access and calling GetBindableParentNode one more time. * // This gets us up to the level where the method group conversion occurs. * if (bindableParent != null && bindableParent.Kind() == SyntaxKind.SimpleMemberAccessExpression && ((MemberAccessExpressionSyntax)bindableParent).Expression == bindableNode) * { * bindableParent = this.GetBindableParentNode(bindableParent); * }*/ boundParent = bindableParent == null ? null : this.GetLowerBoundNode(bindableParent); lowestBoundNode = this.GetLowerBoundNode(bindableNode); highestBoundNode = this.GetUpperBoundNode(bindableNode); }
/// <summary> /// Get all bounds nodes associated with a node, ordered from highest to lowest in the bound tree. /// Strictly speaking, the order is that of a pre-order traversal of the bound tree. /// </summary> public ImmutableArray <BoundNode> GetBoundNodes(LanguageSyntaxNode 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. ImmutableArray <BoundNode> results = GetBoundNodesFromMap(node); if (!results.IsDefaultOrEmpty) { return(results); } // We might not actually have been given an expression or statement even though we were // allegedly given something that is "bindable". // If we didn't find in the cached bound nodes, find a binding root and bind it. // This will cache bound nodes under the binding root. LanguageSyntaxNode nodeToBind = GetBindingRoot(node); var nodeBinder = GetEnclosingBinder(GetAdjustedNodePosition(nodeToBind)); BoundNode boundNode = nodeBinder.CreateBoundNodeForBoundTree(nodeToBind, this); results = AddBoundTreeAndGetBoundNodeFromMap(node, boundNode); if (!results.IsDefaultOrEmpty) { return(results); } // If we still didn't find it, its still possible we could bind it directly. // For example, types are usually not represented by bound nodes, and some error conditions and // not yet implemented features do not create bound nodes for everything underneath them. // // In this case, however, we only add the single bound node we found to the map, not any child bound nodes, // to avoid duplicates in the map if a parent of this node comes through this code path also. var binder = GetEnclosingBinder(GetAdjustedNodePosition(node)); results = GetBoundNodesFromMap(node); if (!results.IsDefaultOrEmpty) { return(results); } /*else * { * var directlyBoundNode = binder.CreateBoundNodeForBoundTree(node, this); * AddBoundTreeForStandaloneSyntax(node, directlyBoundNode); * results = GetBoundNodesFromMap(node); * * if (!results.IsDefaultOrEmpty) * { * return results; * } * }*/ return(ImmutableArray <BoundNode> .Empty); }
private static void AddNode(OrderPreservingMultiDictionary <SyntaxNode, BoundNode> map, SyntaxNode thisSyntaxNodeOnly, BoundNode node) { if (node == null) { return; } // It is possible for there to be multiple bound nodes with the same syntax tree, // and that is by design. For example, in // // byte b = 3; // // there is a bound node for the implicit conversion to byte and a bound node for the // literal, an int. Sometimes we want the inner one (to state the type of the expression) // and sometimes we want the "parent's" view of things (for extract method, for instance.) // // We want to add all bound nodes associated with the same syntax node to the cache, so we first add the // bound node, then we dive deeper into the bound tree. if (ShouldAddNode(node, thisSyntaxNodeOnly)) { map.Add(node.Syntax, node); } }
// Adds every syntax/bound pair in a tree rooted at the given bound node to the map, and the // performs a lookup of the given syntax node in the map. private ImmutableArray <BoundNode> AddBoundTreeAndGetBoundNodeFromMap(LanguageSyntaxNode syntax, BoundNode bound) { bool alreadyInTree = false; if (bound != null) { alreadyInTree = _map.ContainsKey(bound.Syntax); } // check if we already have node in the cache. // this may happen if we have races and in such case we are no longer interested in adding if (!alreadyInTree) { BoundNodeMapBuilder.AddToMap(bound, _map); } ImmutableArray <BoundNode> result; return(_map.TryGetValue(syntax, out result) ? result : default(ImmutableArray <BoundNode>)); }
private static void CacheSubTree(OrderPreservingMultiDictionary <SyntaxNode, BoundNode> map, SyntaxNode thisSyntaxNodeOnly, BoundNode node) { if (node == null) { return; } Stack <BoundNode> nodeStack = new Stack <BoundNode>(); nodeStack.Push(node); while (nodeStack.Count > 0) { BoundNode current = nodeStack.Pop(); AddNode(map, thisSyntaxNodeOnly, current); var currentChildren = current.GetChildBoundNodes(thisSyntaxNodeOnly); for (int i = currentChildren.Length - 1; i >= 0; --i) { nodeStack.Push(currentChildren[i]); } } }