// // LocalReferenceFinder.cs // // Author: // Simon Lindgren // // Copyright (c) 2012 Simon Lindgren // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp.Refactoring { /// /// Finds references to IVariables. /// /// /// This class is more efficient than /// if there is already a resolved tree or if multiple searches needs /// to be performed. /// public class LocalReferenceFinder { LocalReferenceLocator locator; MultiDictionary references = new MultiDictionary(); HashSet visitedRoots = new HashSet(); public LocalReferenceFinder(CSharpAstResolver resolver) { locator = new LocalReferenceLocator(resolver, this); } public LocalReferenceFinder(BaseRefactoringContext context) : this(context.Resolver) { } void VisitIfNeccessary(AstNode rootNode) { // If any of the parent nodes are recorded as visited, // we don't need to traverse rootNode this time. var tmpRoot = rootNode; while(tmpRoot != null){ if (visitedRoots.Contains(tmpRoot)) return; tmpRoot = tmpRoot.Parent; } locator.ProccessRoot (rootNode); } /// /// Finds the references to . /// /// /// Root node for the search. /// /// /// The variable to find references for. /// /// /// When a single is reused for multiple /// searches, which references outside of are /// or are not reported is undefined. /// public IList FindReferences(AstNode rootNode, IVariable variable) { lock (locator) { VisitIfNeccessary(rootNode); var lookup = (ILookup)references; if (!lookup.Contains(variable)) return new List(); // Clone the list for thread safety return references[variable].ToList(); } } class LocalReferenceLocator : DepthFirstAstVisitor { CSharpAstResolver resolver; LocalReferenceFinder referenceFinder; public LocalReferenceLocator(CSharpAstResolver resolver, LocalReferenceFinder referenceFinder) { this.resolver = resolver; this.referenceFinder = referenceFinder; } IList processedVariables = new List(); public void ProccessRoot (AstNode rootNode) { rootNode.AcceptVisitor(this); referenceFinder.visitedRoots.Add(rootNode); } public override void VisitCSharpTokenNode(CSharpTokenNode token) { // Nothing } protected override void VisitChildren(AstNode node) { if (referenceFinder.visitedRoots.Contains(node)) return; var localResolveResult = resolver.Resolve(node) as LocalResolveResult; if (localResolveResult != null && !processedVariables.Contains(localResolveResult.Variable)) { referenceFinder.references.Add(localResolveResult.Variable, new ReferenceResult(node, localResolveResult)); processedVariables.Add(localResolveResult.Variable); base.VisitChildren(node); Debug.Assert(processedVariables.Contains(localResolveResult.Variable), "Variable should still be in the list of processed variables."); processedVariables.Remove(localResolveResult.Variable); } else { base.VisitChildren(node); } } } } public class ReferenceResult { public ReferenceResult (AstNode node, LocalResolveResult resolveResult) { Node = node; ResolveResult = resolveResult; } public AstNode Node { get; private set; } public LocalResolveResult ResolveResult { get; private set; } } }