// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team // // 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; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.Resolver { public delegate void FoundReferenceCallback(AstNode astNode, ResolveResult result); /// /// 'Find references' implementation. /// /// /// This class is thread-safe. /// The intended multi-threaded usage is to call GetSearchScopes() once, and then /// call FindReferencesInFile() concurrently on multiple threads (parallel foreach over all interesting files). /// public sealed class FindReferences { #region Properties /// /// Specifies whether to find type references even if an alias is being used. /// Aliases may be var or using Alias = ...;. /// public bool FindTypeReferencesEvenIfAliased { get; set; } /// /// Specifies whether find references should only look for specialized matches /// with equal type parameter substitution to the member we are searching for. /// public bool FindOnlySpecializedReferences { get; set; } /// /// If this option is enabled, find references on a overridden member /// will find calls to the base member. /// public bool FindCallsThroughVirtualBaseMethod { get; set; } /// /// If this option is enabled, find references on a member implementing /// an interface will also find calls to the interface. /// public bool FindCallsThroughInterface { get; set; } /// /// If this option is enabled, find references will look for all references /// to the virtual method slot. /// public bool WholeVirtualSlot { get; set; } //public bool FindAllOverloads { get; set; } /// /// Specifies whether to look for references in documentation comments. /// This will find entity references in cref attributes and /// parameter references in <param> and <paramref> tags. /// TODO: implement this feature. /// public bool SearchInDocumentationComments { get; set; } #endregion #region GetEffectiveAccessibility /// /// Gets the effective accessibility of the specified entity - /// that is, the accessibility viewed from the top level. /// /// /// internal member in public class -> internal /// public member in internal class -> internal /// protected member in public class -> protected /// protected member in internal class -> protected and internal /// public static Accessibility GetEffectiveAccessibility(IEntity entity) { if (entity == null) throw new ArgumentNullException("entity"); Accessibility a = entity.Accessibility; for (ITypeDefinition declType = entity.DeclaringTypeDefinition; declType != null; declType = declType.DeclaringTypeDefinition) { a = MergeAccessibility(declType.Accessibility, a); } return a; } static Accessibility MergeAccessibility(Accessibility outer, Accessibility inner) { if (outer == inner) return inner; if (outer == Accessibility.None || inner == Accessibility.None) return Accessibility.None; if (outer == Accessibility.Private || inner == Accessibility.Private) return Accessibility.Private; if (outer == Accessibility.Public) return inner; if (inner == Accessibility.Public) return outer; // Inner and outer are both in { protected, internal, protected and internal, protected or internal } // (but they aren't both the same) if (outer == Accessibility.ProtectedOrInternal) return inner; if (inner == Accessibility.ProtectedOrInternal) return outer; // Inner and outer are both in { protected, internal, protected and internal }, // but aren't both the same, so the result is protected and internal. return Accessibility.ProtectedAndInternal; } #endregion #region class SearchScope sealed class SearchScope : IFindReferenceSearchScope { readonly Func factory; public SearchScope(Func factory) { this.factory = factory; } public SearchScope(string searchTerm, Func factory) { this.searchTerm = searchTerm; this.factory = factory; } internal string searchTerm; internal FindReferences findReferences; internal ICompilation declarationCompilation; internal Accessibility accessibility; internal ITypeDefinition topLevelTypeDefinition; internal string fileName; IResolveVisitorNavigator IFindReferenceSearchScope.GetNavigator(ICompilation compilation, FoundReferenceCallback callback) { FindReferenceNavigator n = factory(compilation); if (n != null) { n.callback = callback; n.findReferences = findReferences; return n; } else { return new ConstantModeResolveVisitorNavigator(ResolveVisitorNavigationMode.Skip, null); } } ICompilation IFindReferenceSearchScope.Compilation { get { return declarationCompilation; } } string IFindReferenceSearchScope.SearchTerm { get { return searchTerm; } } Accessibility IFindReferenceSearchScope.Accessibility { get { return accessibility; } } ITypeDefinition IFindReferenceSearchScope.TopLevelTypeDefinition { get { return topLevelTypeDefinition; } } string IFindReferenceSearchScope.FileName { get { return fileName; } } } abstract class FindReferenceNavigator : IResolveVisitorNavigator { internal FoundReferenceCallback callback; internal FindReferences findReferences; internal abstract bool CanMatch(AstNode node); internal abstract bool IsMatch(ResolveResult rr); ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) { if (CanMatch(node)) return ResolveVisitorNavigationMode.Resolve; else return ResolveVisitorNavigationMode.Scan; } void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result) { if (CanMatch(node) && IsMatch(result)) { ReportMatch(node, result); } } public virtual void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) { } protected void ReportMatch(AstNode node, ResolveResult result) { if (callback != null) callback(node, result); } internal virtual void NavigatorDone(CSharpAstResolver resolver, CancellationToken cancellationToken) { } } #endregion #region GetSearchScopes public IList GetSearchScopes(ISymbol symbol) { if (symbol == null) throw new ArgumentNullException("symbol"); switch (symbol.SymbolKind) { case SymbolKind.Namespace: return new[] { GetSearchScopeForNamespace((INamespace)symbol) }; case SymbolKind.TypeParameter: return new[] { GetSearchScopeForTypeParameter((ITypeParameter)symbol) }; } SearchScope scope; SearchScope additionalScope = null; IEntity entity = null; if (symbol.SymbolKind == SymbolKind.Variable) { var variable = (IVariable) symbol; scope = GetSearchScopeForLocalVariable(variable); } else if (symbol.SymbolKind == SymbolKind.Parameter) { var par = (IParameter)symbol; scope = GetSearchScopeForParameter(par); entity = par.Owner; } else { entity = symbol as IEntity; if (entity == null) throw new NotSupportedException("Unsupported symbol type"); if (entity is IMember) entity = NormalizeMember((IMember)entity); switch (entity.SymbolKind) { case SymbolKind.TypeDefinition: scope = FindTypeDefinitionReferences((ITypeDefinition)entity, this.FindTypeReferencesEvenIfAliased, out additionalScope); break; case SymbolKind.Field: if (entity.DeclaringTypeDefinition != null && entity.DeclaringTypeDefinition.Kind == TypeKind.Enum) scope = FindMemberReferences(entity, m => new FindEnumMemberReferences((IField)m)); else scope = FindMemberReferences(entity, m => new FindFieldReferences((IField)m)); break; case SymbolKind.Property: scope = FindMemberReferences(entity, m => new FindPropertyReferences((IProperty)m)); if (entity.Name == "Current") additionalScope = FindEnumeratorCurrentReferences((IProperty)entity); else if (entity.Name == "IsCompleted") additionalScope = FindAwaiterIsCompletedReferences((IProperty)entity); break; case SymbolKind.Event: scope = FindMemberReferences(entity, m => new FindEventReferences((IEvent)m)); break; case SymbolKind.Method: scope = GetSearchScopeForMethod((IMethod)entity); break; case SymbolKind.Indexer: scope = FindIndexerReferences((IProperty)entity); break; case SymbolKind.Operator: scope = GetSearchScopeForOperator((IMethod)entity); break; case SymbolKind.Constructor: IMethod ctor = (IMethod)entity; scope = FindObjectCreateReferences(ctor); additionalScope = FindChainedConstructorReferences(ctor); break; case SymbolKind.Destructor: scope = GetSearchScopeForDestructor((IMethod)entity); break; default: throw new ArgumentException("Unknown entity type " + entity.SymbolKind); } } var effectiveAccessibility = entity != null ? GetEffectiveAccessibility(entity) : Accessibility.Private; var topLevelTypeDefinition = GetTopLevelTypeDefinition(entity); if (scope.accessibility == Accessibility.None) scope.accessibility = effectiveAccessibility; scope.declarationCompilation = entity != null ? entity.Compilation : null; scope.topLevelTypeDefinition = topLevelTypeDefinition; scope.findReferences = this; if (additionalScope != null) { if (additionalScope.accessibility == Accessibility.None) additionalScope.accessibility = effectiveAccessibility; additionalScope.declarationCompilation = scope.declarationCompilation; additionalScope.topLevelTypeDefinition = topLevelTypeDefinition; additionalScope.findReferences = this; return new[] { scope, additionalScope }; } else { return new[] { scope }; } } public IList GetSearchScopes(IEnumerable symbols) { if (symbols == null) throw new ArgumentNullException("symbols"); return symbols.SelectMany(GetSearchScopes).ToList(); } static ITypeDefinition GetTopLevelTypeDefinition(IEntity entity) { if (entity == null) return null; ITypeDefinition topLevelTypeDefinition = entity.DeclaringTypeDefinition; while (topLevelTypeDefinition != null && topLevelTypeDefinition.DeclaringTypeDefinition != null) topLevelTypeDefinition = topLevelTypeDefinition.DeclaringTypeDefinition; return topLevelTypeDefinition; } #endregion #region GetInterestingFileNames /// /// Gets the file names that possibly contain references to the element being searched for. /// public IEnumerable GetInterestingFiles(IFindReferenceSearchScope searchScope, ICompilation compilation) { if (searchScope == null) throw new ArgumentNullException("searchScope"); if (compilation == null) throw new ArgumentNullException("compilation"); var pc = compilation.MainAssembly.UnresolvedAssembly as IProjectContent; if (pc == null) throw new ArgumentException("Main assembly is not a project content"); if (searchScope.TopLevelTypeDefinition != null) { ITypeDefinition topLevelTypeDef = compilation.Import(searchScope.TopLevelTypeDefinition); if (topLevelTypeDef == null) { // This compilation cannot have references to the target entity. return EmptyList.Instance; } switch (searchScope.Accessibility) { case Accessibility.None: case Accessibility.Private: if (topLevelTypeDef.ParentAssembly == compilation.MainAssembly) return topLevelTypeDef.Parts.Select(p => p.UnresolvedFile).OfType().Distinct(); else return EmptyList.Instance; case Accessibility.Protected: return GetInterestingFilesProtected(topLevelTypeDef); case Accessibility.Internal: if (topLevelTypeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly)) return pc.Files.OfType(); else return EmptyList.Instance; case Accessibility.ProtectedAndInternal: if (topLevelTypeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly)) return GetInterestingFilesProtected(topLevelTypeDef); else return EmptyList.Instance; case Accessibility.ProtectedOrInternal: if (topLevelTypeDef.ParentAssembly.InternalsVisibleTo(compilation.MainAssembly)) return pc.Files.OfType(); else return GetInterestingFilesProtected(topLevelTypeDef); default: return pc.Files.OfType(); } } else { if (searchScope.FileName == null) return pc.Files.OfType(); else return pc.Files.OfType().Where(f => f.FileName == searchScope.FileName); } } IEnumerable GetInterestingFilesProtected(ITypeDefinition referencedTypeDefinition) { return (from typeDef in referencedTypeDefinition.Compilation.MainAssembly.GetAllTypeDefinitions() where typeDef.IsDerivedFrom(referencedTypeDefinition) from part in typeDef.Parts select part.UnresolvedFile ).OfType().Distinct(); } #endregion #region FindReferencesInFile /// /// Finds all references in the given file. /// /// The search scope for which to look. /// AST resolver for the file to search in. /// Callback used to report the references that were found. /// CancellationToken that may be used to cancel the operation. public void FindReferencesInFile(IFindReferenceSearchScope searchScope, CSharpAstResolver resolver, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (resolver == null) throw new ArgumentNullException("resolver"); FindReferencesInFile(searchScope, resolver.UnresolvedFile, (SyntaxTree)resolver.RootNode, resolver.Compilation, callback, cancellationToken); } /// /// Finds all references in the given file. /// /// The search scopes for which to look. /// AST resolver for the file to search in. /// Callback used to report the references that were found. /// CancellationToken that may be used to cancel the operation. public void FindReferencesInFile(IList searchScopes, CSharpAstResolver resolver, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (resolver == null) throw new ArgumentNullException("resolver"); FindReferencesInFile(searchScopes, resolver.UnresolvedFile, (SyntaxTree)resolver.RootNode, resolver.Compilation, callback, cancellationToken); } /// /// Finds all references in the given file. /// /// The search scope for which to look. /// The type system representation of the file being searched. /// The syntax tree of the file being searched. /// The compilation for the project that contains the file. /// Callback used to report the references that were found. /// CancellationToken that may be used to cancel the operation. public void FindReferencesInFile(IFindReferenceSearchScope searchScope, CSharpUnresolvedFile unresolvedFile, SyntaxTree syntaxTree, ICompilation compilation, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (searchScope == null) throw new ArgumentNullException("searchScope"); FindReferencesInFile(new[] { searchScope }, unresolvedFile, syntaxTree, compilation, callback, cancellationToken); } /// /// Finds all references in the given file. /// /// The search scopes for which to look. /// The type system representation of the file being searched. /// The syntax tree of the file being searched. /// The compilation for the project that contains the file. /// Callback used to report the references that were found. /// CancellationToken that may be used to cancel the operation. public void FindReferencesInFile(IList searchScopes, CSharpUnresolvedFile unresolvedFile, SyntaxTree syntaxTree, ICompilation compilation, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (searchScopes == null) throw new ArgumentNullException("searchScopes"); if (syntaxTree == null) throw new ArgumentNullException("syntaxTree"); if (compilation == null) throw new ArgumentNullException("compilation"); if (callback == null) throw new ArgumentNullException("callback"); if (searchScopes.Count == 0) return; var navigators = new IResolveVisitorNavigator[searchScopes.Count]; for (int i = 0; i < navigators.Length; i++) { navigators[i] = searchScopes[i].GetNavigator(compilation, callback); } IResolveVisitorNavigator combinedNavigator; if (searchScopes.Count == 1) { combinedNavigator = navigators[0]; } else { combinedNavigator = new CompositeResolveVisitorNavigator(navigators); } cancellationToken.ThrowIfCancellationRequested(); combinedNavigator = new DetectSkippableNodesNavigator(combinedNavigator, syntaxTree); cancellationToken.ThrowIfCancellationRequested(); CSharpAstResolver resolver = new CSharpAstResolver(compilation, syntaxTree, unresolvedFile); resolver.ApplyNavigator(combinedNavigator, cancellationToken); foreach (var n in navigators) { var frn = n as FindReferenceNavigator; if (frn != null) frn.NavigatorDone(resolver, cancellationToken); } } #endregion #region RenameReferencesInFile public static AstNode GetNodeToReplace(AstNode node) { if (node is ConstructorInitializer) return null; if (node is ObjectCreateExpression) node = ((ObjectCreateExpression)node).Type; if (node is InvocationExpression) node = ((InvocationExpression)node).Target; if (node is MemberReferenceExpression) node = ((MemberReferenceExpression)node).MemberNameToken; if (node is SimpleType) node = ((SimpleType)node).IdentifierToken; if (node is MemberType) node = ((MemberType)node).MemberNameToken; if (node is NamespaceDeclaration) { // var nsd = ((NamespaceDeclaration)node); // node = nsd.Identifiers.LastOrDefault (n => n.Name == memberName) ?? nsd.Identifiers.FirstOrDefault (); // if (node == null) return null; } if (node is TypeDeclaration) node = ((TypeDeclaration)node).NameToken; if (node is DelegateDeclaration) node = ((DelegateDeclaration)node).NameToken; if (node is EntityDeclaration) node = ((EntityDeclaration)node).NameToken; if (node is ParameterDeclaration) node = ((ParameterDeclaration)node).NameToken; if (node is ConstructorDeclaration) node = ((ConstructorDeclaration)node).NameToken; if (node is DestructorDeclaration) node = ((DestructorDeclaration)node).NameToken; if (node is NamedArgumentExpression) node = ((NamedArgumentExpression)node).NameToken; if (node is NamedExpression) node = ((NamedExpression)node).NameToken; if (node is VariableInitializer) node = ((VariableInitializer)node).NameToken; if (node is IdentifierExpression) { node = ((IdentifierExpression)node).IdentifierToken; } return node; } public void RenameReferencesInFile(IList searchScopes, string newName, CSharpAstResolver resolver, Action callback, Action errorCallback, CancellationToken cancellationToken = default (CancellationToken)) { FindReferencesInFile( searchScopes, resolver, delegate(AstNode astNode, ResolveResult result) { var nodeToReplace = GetNodeToReplace(astNode); if (nodeToReplace == null) { errorCallback (new Error (ErrorType.Error, "no node to replace found.")); return; } callback (new RenameCallbackArguments(nodeToReplace, Identifier.Create(newName))); }, cancellationToken); } #endregion #region Find TypeDefinition References SearchScope FindTypeDefinitionReferences(ITypeDefinition typeDefinition, bool findTypeReferencesEvenIfAliased, out SearchScope additionalScope) { string searchTerm = null; additionalScope = null; if (!findTypeReferencesEvenIfAliased && KnownTypeReference.GetCSharpNameByTypeCode(typeDefinition.KnownTypeCode) == null) { // We can optimize the search by looking only for the type references with the right identifier, // but only if it's not a primitive type and we're not looking for indirect references (through an alias) searchTerm = typeDefinition.Name; if (searchTerm.Length > 9 && searchTerm.EndsWith("Attribute", StringComparison.Ordinal)) { // The type might be an attribute, so we also need to look for the short form: string shortForm = searchTerm.Substring(0, searchTerm.Length - 9); additionalScope = FindTypeDefinitionReferences(typeDefinition, shortForm); } } return FindTypeDefinitionReferences(typeDefinition, searchTerm); } SearchScope FindTypeDefinitionReferences(ITypeDefinition typeDefinition, string searchTerm) { return new SearchScope( searchTerm, delegate (ICompilation compilation) { ITypeDefinition imported = compilation.Import(typeDefinition); if (imported != null) return new FindTypeDefinitionReferencesNavigator(imported, searchTerm); else return null; }); } sealed class FindTypeDefinitionReferencesNavigator : FindReferenceNavigator { readonly ITypeDefinition typeDefinition; readonly string searchTerm; public FindTypeDefinitionReferencesNavigator(ITypeDefinition typeDefinition, string searchTerm) { this.typeDefinition = typeDefinition; this.searchTerm = searchTerm; } internal override bool CanMatch(AstNode node) { IdentifierExpression ident = node as IdentifierExpression; if (ident != null) return searchTerm == null || ident.Identifier == searchTerm; MemberReferenceExpression mre = node as MemberReferenceExpression; if (mre != null) return searchTerm == null || mre.MemberName == searchTerm; SimpleType st = node as SimpleType; if (st != null) return searchTerm == null || st.Identifier == searchTerm; MemberType mt = node as MemberType; if (mt != null) return searchTerm == null || mt.MemberName == searchTerm; if (searchTerm == null && node is PrimitiveType) return true; TypeDeclaration typeDecl = node as TypeDeclaration; if (typeDecl != null) return searchTerm == null || typeDecl.Name == searchTerm; DelegateDeclaration delegateDecl = node as DelegateDeclaration; if (delegateDecl != null) return searchTerm == null || delegateDecl.Name == searchTerm; return false; } internal override bool IsMatch(ResolveResult rr) { TypeResolveResult trr = rr as TypeResolveResult; return trr != null && typeDefinition.Equals(trr.Type.GetDefinition()); } } #endregion #region Find Member References SearchScope FindMemberReferences(IEntity member, Func factory) { string searchTerm = member.Name; return new SearchScope( searchTerm, delegate(ICompilation compilation) { IMember imported = compilation.Import((IMember)member); return imported != null ? factory(imported) : null; }); } class FindMemberReferencesNavigator : FindReferenceNavigator { readonly IMember member; readonly string searchTerm; public FindMemberReferencesNavigator(IMember member) { this.member = member; this.searchTerm = member.Name; } internal override bool CanMatch(AstNode node) { IdentifierExpression ident = node as IdentifierExpression; if (ident != null) return ident.Identifier == searchTerm; MemberReferenceExpression mre = node as MemberReferenceExpression; if (mre != null) return mre.MemberName == searchTerm; PointerReferenceExpression pre = node as PointerReferenceExpression; if (pre != null) return pre.MemberName == searchTerm; NamedExpression ne = node as NamedExpression; if (ne != null) return ne.Name == searchTerm; return false; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(member, mrr.Member, mrr.IsVirtualCall); } } IMember NormalizeMember(IMember member) { if (WholeVirtualSlot && member.IsOverride) member = InheritanceHelper.GetBaseMembers(member, false).FirstOrDefault(m => !m.IsOverride) ?? member; if (!FindOnlySpecializedReferences) member = member.MemberDefinition; return member; } bool IsMemberMatch(IMember member, IMember referencedMember, bool isVirtualCall) { referencedMember = NormalizeMember(referencedMember); if (member.Equals(referencedMember)) return true; if (FindCallsThroughInterface && member.DeclaringTypeDefinition != null && member.DeclaringTypeDefinition.Kind == TypeKind.Interface) { if (FindOnlySpecializedReferences) { return referencedMember.ImplementedInterfaceMembers.Contains(member); } else { return referencedMember.ImplementedInterfaceMembers.Any(m => m.MemberDefinition.Equals(member)); } } if (!isVirtualCall) return false; bool isInterfaceCall = referencedMember.DeclaringTypeDefinition != null && referencedMember.DeclaringTypeDefinition.Kind == TypeKind.Interface; if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot && !isInterfaceCall) { // Test if 'member' overrides 'referencedMember': foreach (var baseMember in InheritanceHelper.GetBaseMembers(member, false)) { if (FindOnlySpecializedReferences) { if (baseMember.Equals(referencedMember)) return true; } else { if (baseMember.MemberDefinition.Equals(referencedMember)) return true; } if (!baseMember.IsOverride) break; } return false; } else if (FindCallsThroughInterface && isInterfaceCall) { // Test if 'member' implements 'referencedMember': if (FindOnlySpecializedReferences) { return member.ImplementedInterfaceMembers.Contains(referencedMember); } else { return member.ImplementedInterfaceMembers.Any(m => m.MemberDefinition.Equals(referencedMember)); } } return false; } /* bool PerformVirtualLookup(IMember member, IMember referencedMember) { if (FindCallsThroughVirtualBaseMethod && member.IsOverride && !WholeVirtualSlot) return true; var typeDef = referencedMember.DeclaringTypeDefinition; return FindCallsThroughInterface && typeDef != null && typeDef.Kind == TypeKind.Interface; }*/ sealed class FindFieldReferences : FindMemberReferencesNavigator { public FindFieldReferences(IField field) : base(field) { } internal override bool CanMatch(AstNode node) { if (node is VariableInitializer) { return node.Parent is FieldDeclaration; } return base.CanMatch(node); } } sealed class FindEnumMemberReferences : FindMemberReferencesNavigator { public FindEnumMemberReferences(IField field) : base(field) { } internal override bool CanMatch(AstNode node) { return node is EnumMemberDeclaration || base.CanMatch(node); } } sealed class FindPropertyReferences : FindMemberReferencesNavigator { public FindPropertyReferences(IProperty property) : base(property) { } internal override bool CanMatch(AstNode node) { return node is PropertyDeclaration || base.CanMatch(node); } } sealed class FindEventReferences : FindMemberReferencesNavigator { public FindEventReferences(IEvent ev) : base(ev) { } internal override bool CanMatch(AstNode node) { if (node is VariableInitializer) { return node.Parent is EventDeclaration; } return node is CustomEventDeclaration || base.CanMatch(node); } } #endregion #region Find References to IEnumerator.Current SearchScope FindEnumeratorCurrentReferences(IProperty property) { return new SearchScope( delegate(ICompilation compilation) { IProperty imported = compilation.Import(property); return imported != null ? new FindEnumeratorCurrentReferencesNavigator(imported) : null; }); } SearchScope FindAwaiterIsCompletedReferences(IProperty property) { return new SearchScope( delegate(ICompilation compilation) { IProperty imported = compilation.Import(property); return imported != null ? new FindAwaiterIsCompletedReferencesNavigator(imported) : null; }); } sealed class FindEnumeratorCurrentReferencesNavigator : FindReferenceNavigator { IProperty property; public FindEnumeratorCurrentReferencesNavigator(IProperty property) { this.property = property; } internal override bool CanMatch(AstNode node) { return node is ForeachStatement; } internal override bool IsMatch(ResolveResult rr) { ForEachResolveResult ferr = rr as ForEachResolveResult; return ferr != null && ferr.CurrentProperty != null && findReferences.IsMemberMatch(property, ferr.CurrentProperty, true); } } sealed class FindAwaiterIsCompletedReferencesNavigator : FindReferenceNavigator { IProperty property; public FindAwaiterIsCompletedReferencesNavigator(IProperty property) { this.property = property; } internal override bool CanMatch(AstNode node) { return node is UnaryOperatorExpression; } internal override bool IsMatch(ResolveResult rr) { AwaitResolveResult arr = rr as AwaitResolveResult; return arr != null && arr.IsCompletedProperty != null && findReferences.IsMemberMatch(property, arr.IsCompletedProperty, true); } } #endregion #region Find Method References SearchScope GetSearchScopeForMethod(IMethod method) { Type specialNodeType; switch (method.Name) { case "Add": specialNodeType = typeof(ArrayInitializerExpression); break; case "Where": specialNodeType = typeof(QueryWhereClause); break; case "Select": specialNodeType = typeof(QuerySelectClause); break; case "SelectMany": specialNodeType = typeof(QueryFromClause); break; case "Join": case "GroupJoin": specialNodeType = typeof(QueryJoinClause); break; case "OrderBy": case "OrderByDescending": case "ThenBy": case "ThenByDescending": specialNodeType = typeof(QueryOrdering); break; case "GroupBy": specialNodeType = typeof(QueryGroupClause); break; case "Invoke": if (method.DeclaringTypeDefinition != null && method.DeclaringTypeDefinition.Kind == TypeKind.Delegate) specialNodeType = typeof(InvocationExpression); else specialNodeType = null; break; case "GetEnumerator": case "MoveNext": specialNodeType = typeof(ForeachStatement); break; case "GetAwaiter": case "GetResult": case "OnCompleted": case "UnsafeOnCompleted": specialNodeType = typeof(UnaryOperatorExpression); break; default: specialNodeType = null; break; } // Use searchTerm only if specialNodeType==null string searchTerm = (specialNodeType == null) ? method.Name : null; return new SearchScope( searchTerm, delegate (ICompilation compilation) { IMethod imported = compilation.Import(method); if (imported != null) return new FindMethodReferences(imported, specialNodeType); else return null; }); } sealed class FindMethodReferences : FindReferenceNavigator { readonly IMethod method; readonly Type specialNodeType; HashSet potentialMethodGroupConversions = new HashSet(); public FindMethodReferences(IMethod method, Type specialNodeType) { this.method = method; this.specialNodeType = specialNodeType; } internal override bool CanMatch(AstNode node) { if (specialNodeType != null && node.GetType() == specialNodeType) return true; Expression expr = node as Expression; if (expr == null) return node is MethodDeclaration; InvocationExpression ie = node as InvocationExpression; if (ie != null) { Expression target = ParenthesizedExpression.UnpackParenthesizedExpression(ie.Target); IdentifierExpression ident = target as IdentifierExpression; if (ident != null) return ident.Identifier == method.Name; MemberReferenceExpression mre = target as MemberReferenceExpression; if (mre != null) return mre.MemberName == method.Name; PointerReferenceExpression pre = target as PointerReferenceExpression; if (pre != null) return pre.MemberName == method.Name; } else if (expr.Role != Roles.TargetExpression) { // MemberReferences & Identifiers that aren't used in an invocation can still match the method // as delegate name. if (expr.GetChildByRole(Roles.Identifier).Name == method.Name) potentialMethodGroupConversions.Add(expr); } return node is MethodDeclaration; } internal override bool IsMatch(ResolveResult rr) { if (specialNodeType != null) { var ferr = rr as ForEachResolveResult; if (ferr != null) { return IsMatch(ferr.GetEnumeratorCall) || (ferr.MoveNextMethod != null && findReferences.IsMemberMatch(method, ferr.MoveNextMethod, true)); } var arr = rr as AwaitResolveResult; if (arr != null) { return IsMatch(arr.GetAwaiterInvocation) || (arr.GetResultMethod != null && findReferences.IsMemberMatch(method, arr.GetResultMethod, true)) || (arr.OnCompletedMethod != null && findReferences.IsMemberMatch(method, arr.OnCompletedMethod, true)); } } var mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(method, mrr.Member, mrr.IsVirtualCall); } internal override void NavigatorDone(CSharpAstResolver resolver, CancellationToken cancellationToken) { foreach (var expr in potentialMethodGroupConversions) { var conversion = resolver.GetConversion(expr, cancellationToken); if (conversion.IsMethodGroupConversion && findReferences.IsMemberMatch(method, conversion.Method, conversion.IsVirtualMethodLookup)) { IType targetType = resolver.GetExpectedType(expr, cancellationToken); ResolveResult result = resolver.Resolve(expr, cancellationToken); ReportMatch(expr, new ConversionResolveResult(targetType, result, conversion)); } } base.NavigatorDone(resolver, cancellationToken); } } #endregion #region Find Indexer References SearchScope FindIndexerReferences(IProperty indexer) { return new SearchScope( delegate (ICompilation compilation) { IProperty imported = compilation.Import(indexer); if (imported != null) return new FindIndexerReferencesNavigator(imported); else return null; }); } sealed class FindIndexerReferencesNavigator : FindReferenceNavigator { readonly IProperty indexer; public FindIndexerReferencesNavigator(IProperty indexer) { this.indexer = indexer; } internal override bool CanMatch(AstNode node) { return node is IndexerExpression || node is IndexerDeclaration; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(indexer, mrr.Member, mrr.IsVirtualCall); } } #endregion #region Find Operator References SearchScope GetSearchScopeForOperator(IMethod op) { OperatorType? opType = OperatorDeclaration.GetOperatorType(op.Name); if (opType == null) return GetSearchScopeForMethod(op); switch (opType.Value) { case OperatorType.LogicalNot: return FindUnaryOperator(op, UnaryOperatorType.Not); case OperatorType.OnesComplement: return FindUnaryOperator(op, UnaryOperatorType.BitNot); case OperatorType.UnaryPlus: return FindUnaryOperator(op, UnaryOperatorType.Plus); case OperatorType.UnaryNegation: return FindUnaryOperator(op, UnaryOperatorType.Minus); case OperatorType.Increment: return FindUnaryOperator(op, UnaryOperatorType.Increment); case OperatorType.Decrement: return FindUnaryOperator(op, UnaryOperatorType.Decrement); case OperatorType.True: case OperatorType.False: // TODO: implement search for op_True/op_False correctly return GetSearchScopeForMethod(op); case OperatorType.Addition: return FindBinaryOperator(op, BinaryOperatorType.Add); case OperatorType.Subtraction: return FindBinaryOperator(op, BinaryOperatorType.Subtract); case OperatorType.Multiply: return FindBinaryOperator(op, BinaryOperatorType.Multiply); case OperatorType.Division: return FindBinaryOperator(op, BinaryOperatorType.Divide); case OperatorType.Modulus: return FindBinaryOperator(op, BinaryOperatorType.Modulus); case OperatorType.BitwiseAnd: // TODO: an overloaded bitwise operator can also be called using the corresponding logical operator // (if op_True/op_False is defined) return FindBinaryOperator(op, BinaryOperatorType.BitwiseAnd); case OperatorType.BitwiseOr: return FindBinaryOperator(op, BinaryOperatorType.BitwiseOr); case OperatorType.ExclusiveOr: return FindBinaryOperator(op, BinaryOperatorType.ExclusiveOr); case OperatorType.LeftShift: return FindBinaryOperator(op, BinaryOperatorType.ShiftLeft); case OperatorType.RightShift: return FindBinaryOperator(op, BinaryOperatorType.ShiftRight); case OperatorType.Equality: return FindBinaryOperator(op, BinaryOperatorType.Equality); case OperatorType.Inequality: return FindBinaryOperator(op, BinaryOperatorType.InEquality); case OperatorType.GreaterThan: return FindBinaryOperator(op, BinaryOperatorType.GreaterThan); case OperatorType.LessThan: return FindBinaryOperator(op, BinaryOperatorType.LessThan); case OperatorType.GreaterThanOrEqual: return FindBinaryOperator(op, BinaryOperatorType.GreaterThanOrEqual); case OperatorType.LessThanOrEqual: return FindBinaryOperator(op, BinaryOperatorType.LessThanOrEqual); case OperatorType.Implicit: return FindOperator(op, m => new FindImplicitOperatorNavigator(m)); case OperatorType.Explicit: return FindOperator(op, m => new FindExplicitOperatorNavigator(m)); default: throw new InvalidOperationException("Invalid value for OperatorType"); } } SearchScope FindOperator(IMethod op, Func factory) { return new SearchScope( delegate (ICompilation compilation) { IMethod imported = compilation.Import(op); return imported != null ? factory(imported) : null; }); } SearchScope FindUnaryOperator(IMethod op, UnaryOperatorType operatorType) { return FindOperator(op, m => new FindUnaryOperatorNavigator(m, operatorType)); } sealed class FindUnaryOperatorNavigator : FindReferenceNavigator { readonly IMethod op; readonly UnaryOperatorType operatorType; public FindUnaryOperatorNavigator(IMethod op, UnaryOperatorType operatorType) { this.op = op; this.operatorType = operatorType; } internal override bool CanMatch(AstNode node) { UnaryOperatorExpression uoe = node as UnaryOperatorExpression; if (uoe != null) { if (operatorType == UnaryOperatorType.Increment) return uoe.Operator == UnaryOperatorType.Increment || uoe.Operator == UnaryOperatorType.PostIncrement; else if (operatorType == UnaryOperatorType.Decrement) return uoe.Operator == UnaryOperatorType.Decrement || uoe.Operator == UnaryOperatorType.PostDecrement; else return uoe.Operator == operatorType; } return node is OperatorDeclaration; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } } SearchScope FindBinaryOperator(IMethod op, BinaryOperatorType operatorType) { return FindOperator(op, m => new FindBinaryOperatorNavigator(m, operatorType)); } sealed class FindBinaryOperatorNavigator : FindReferenceNavigator { readonly IMethod op; readonly BinaryOperatorType operatorType; public FindBinaryOperatorNavigator(IMethod op, BinaryOperatorType operatorType) { this.op = op; this.operatorType = operatorType; } internal override bool CanMatch(AstNode node) { BinaryOperatorExpression boe = node as BinaryOperatorExpression; if (boe != null) { return boe.Operator == operatorType; } return node is OperatorDeclaration; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } } sealed class FindImplicitOperatorNavigator : FindReferenceNavigator { readonly IMethod op; public FindImplicitOperatorNavigator(IMethod op) { this.op = op; } internal override bool CanMatch(AstNode node) { return true; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(op, mrr.Member, mrr.IsVirtualCall); } public override void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType) { if (conversion.IsUserDefined && findReferences.IsMemberMatch(op, conversion.Method, conversion.IsVirtualMethodLookup)) { ReportMatch(expression, result); } } } sealed class FindExplicitOperatorNavigator : FindReferenceNavigator { readonly IMethod op; public FindExplicitOperatorNavigator(IMethod op) { this.op = op; } internal override bool CanMatch(AstNode node) { return node is CastExpression; } internal override bool IsMatch(ResolveResult rr) { ConversionResolveResult crr = rr as ConversionResolveResult; return crr != null && crr.Conversion.IsUserDefined && findReferences.IsMemberMatch(op, crr.Conversion.Method, crr.Conversion.IsVirtualMethodLookup); } } #endregion #region Find Constructor References SearchScope FindObjectCreateReferences(IMethod ctor) { string searchTerm = null; if (KnownTypeReference.GetCSharpNameByTypeCode(ctor.DeclaringTypeDefinition.KnownTypeCode) == null) { // not a built-in type searchTerm = ctor.DeclaringTypeDefinition.Name; if (searchTerm.Length > 9 && searchTerm.EndsWith("Attribute", StringComparison.Ordinal)) { // we also need to look for the short form searchTerm = null; } } return new SearchScope( searchTerm, delegate (ICompilation compilation) { IMethod imported = compilation.Import(ctor); if (imported != null) return new FindObjectCreateReferencesNavigator(imported); else return null; }); } sealed class FindObjectCreateReferencesNavigator : FindReferenceNavigator { readonly IMethod ctor; public FindObjectCreateReferencesNavigator(IMethod ctor) { this.ctor = ctor; } internal override bool CanMatch(AstNode node) { return node is ObjectCreateExpression || node is ConstructorDeclaration || node is Attribute; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall); } } SearchScope FindChainedConstructorReferences(IMethod ctor) { SearchScope searchScope = new SearchScope( delegate (ICompilation compilation) { IMethod imported = compilation.Import(ctor); if (imported != null) return new FindChainedConstructorReferencesNavigator(imported); else return null; }); if (ctor.DeclaringTypeDefinition.IsSealed) searchScope.accessibility = Accessibility.Private; else searchScope.accessibility = Accessibility.Protected; searchScope.accessibility = MergeAccessibility(GetEffectiveAccessibility(ctor), searchScope.accessibility); return searchScope; } sealed class FindChainedConstructorReferencesNavigator : FindReferenceNavigator { readonly IMethod ctor; public FindChainedConstructorReferencesNavigator(IMethod ctor) { this.ctor = ctor; } internal override bool CanMatch(AstNode node) { return node is ConstructorInitializer; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(ctor, mrr.Member, mrr.IsVirtualCall); } } #endregion #region Find Destructor References SearchScope GetSearchScopeForDestructor(IMethod dtor) { var scope = new SearchScope ( delegate (ICompilation compilation) { IMethod imported = compilation.Import(dtor); if (imported != null) { return new FindDestructorReferencesNavigator (imported); } else { return null; } }); scope.accessibility = Accessibility.Private; return scope; } sealed class FindDestructorReferencesNavigator : FindReferenceNavigator { readonly IMethod dtor; public FindDestructorReferencesNavigator (IMethod dtor) { this.dtor = dtor; } internal override bool CanMatch(AstNode node) { return node is DestructorDeclaration; } internal override bool IsMatch(ResolveResult rr) { MemberResolveResult mrr = rr as MemberResolveResult; return mrr != null && findReferences.IsMemberMatch(dtor, mrr.Member, mrr.IsVirtualCall); } } #endregion #region Find Local Variable References /// /// Finds all references of a given variable. /// /// The variable for which to look. /// The type system representation of the file being searched. /// The syntax tree of the file being searched. /// The compilation. /// Callback used to report the references that were found. /// Cancellation token that may be used to cancel the operation. public void FindLocalReferences(IVariable variable, CSharpUnresolvedFile unresolvedFile, SyntaxTree syntaxTree, ICompilation compilation, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (variable == null) throw new ArgumentNullException("variable"); var searchScope = new SearchScope(c => new FindLocalReferencesNavigator(variable)); searchScope.declarationCompilation = compilation; FindReferencesInFile(searchScope, unresolvedFile, syntaxTree, compilation, callback, cancellationToken); } SearchScope GetSearchScopeForLocalVariable(IVariable variable) { var scope = new SearchScope ( delegate { return new FindLocalReferencesNavigator(variable); } ); scope.fileName = variable.Region.FileName; return scope; } class FindLocalReferencesNavigator : FindReferenceNavigator { readonly IVariable variable; public FindLocalReferencesNavigator(IVariable variable) { this.variable = variable; } internal override bool CanMatch(AstNode node) { var expr = node as IdentifierExpression; if (expr != null) return expr.TypeArguments.Count == 0 && variable.Name == expr.Identifier; var vi = node as VariableInitializer; if (vi != null) return vi.Name == variable.Name; var pd = node as ParameterDeclaration; if (pd != null) return pd.Name == variable.Name; var id = node as Identifier; if (id != null) return id.Name == variable.Name; return false; } internal override bool IsMatch(ResolveResult rr) { var lrr = rr as LocalResolveResult; return lrr != null && lrr.Variable.Name == variable.Name && lrr.Variable.Region == variable.Region; } } #endregion #region Find Type Parameter References /// /// Finds all references of a given type parameter. /// /// The type parameter for which to look. /// The type system representation of the file being searched. /// The syntax tree of the file being searched. /// The compilation. /// Callback used to report the references that were found. /// Cancellation token that may be used to cancel the operation. [Obsolete("Use GetSearchScopes(typeParameter) instead")] public void FindTypeParameterReferences(IType typeParameter, CSharpUnresolvedFile unresolvedFile, SyntaxTree syntaxTree, ICompilation compilation, FoundReferenceCallback callback, CancellationToken cancellationToken) { if (typeParameter == null) throw new ArgumentNullException("typeParameter"); if (typeParameter.Kind != TypeKind.TypeParameter) throw new ArgumentOutOfRangeException("typeParameter", "Only type parameters are allowed"); var searchScope = new SearchScope(c => new FindTypeParameterReferencesNavigator((ITypeParameter)typeParameter)); searchScope.declarationCompilation = compilation; searchScope.accessibility = Accessibility.Private; FindReferencesInFile(searchScope, unresolvedFile, syntaxTree, compilation, callback, cancellationToken); } SearchScope GetSearchScopeForTypeParameter(ITypeParameter tp) { var searchScope = new SearchScope(c => new FindTypeParameterReferencesNavigator(tp)); var compilationProvider = tp as ICompilationProvider; if (compilationProvider != null) searchScope.declarationCompilation = compilationProvider.Compilation; searchScope.topLevelTypeDefinition = GetTopLevelTypeDefinition(tp.Owner); searchScope.accessibility = Accessibility.Private; return searchScope; } class FindTypeParameterReferencesNavigator : FindReferenceNavigator { readonly ITypeParameter typeParameter; public FindTypeParameterReferencesNavigator(ITypeParameter typeParameter) { this.typeParameter = typeParameter; } internal override bool CanMatch(AstNode node) { var type = node as SimpleType; if (type != null) return type.Identifier == typeParameter.Name; var declaration = node as TypeParameterDeclaration; if (declaration != null) return declaration.Name == typeParameter.Name; return false; } internal override bool IsMatch(ResolveResult rr) { var lrr = rr as TypeResolveResult; return lrr != null && lrr.Type.Kind == TypeKind.TypeParameter && ((ITypeParameter)lrr.Type).Region == typeParameter.Region; } } #endregion #region Find Namespace References SearchScope GetSearchScopeForNamespace(INamespace ns) { var scope = new SearchScope ( delegate (ICompilation compilation) { return new FindNamespaceNavigator (ns); } ); return scope; } sealed class FindNamespaceNavigator : FindReferenceNavigator { readonly INamespace ns; public FindNamespaceNavigator (INamespace ns) { this.ns = ns; } internal override bool CanMatch(AstNode node) { var nsd = node as NamespaceDeclaration; if (nsd != null && nsd.FullName.StartsWith(ns.FullName, StringComparison.Ordinal)) return true; var ud = node as UsingDeclaration; if (ud != null && ud.Namespace == ns.FullName) return true; var st = node as SimpleType; if (st != null && st.Identifier == ns.Name) return !st.AncestorsAndSelf.TakeWhile (n => n is AstType).Any (m => m.Role == NamespaceDeclaration.NamespaceNameRole); var mt = node as MemberType; if (mt != null && mt.MemberName == ns.Name) return !mt.AncestorsAndSelf.TakeWhile (n => n is AstType).Any (m => m.Role == NamespaceDeclaration.NamespaceNameRole); var identifer = node as IdentifierExpression; if (identifer != null && identifer.Identifier == ns.Name) return true; var mrr = node as MemberReferenceExpression; if (mrr != null && mrr.MemberName == ns.Name) return true; return false; } internal override bool IsMatch(ResolveResult rr) { var nsrr = rr as NamespaceResolveResult; return nsrr != null && nsrr.NamespaceName.StartsWith(ns.FullName, StringComparison.Ordinal); } } #endregion #region Find Parameter References SearchScope GetSearchScopeForParameter(IParameter parameter) { var scope = new SearchScope ( delegate { return new FindParameterReferencesNavigator (parameter); } ); if (parameter.Owner == null) { scope.fileName = parameter.Region.FileName; } return scope; } class FindParameterReferencesNavigator : FindReferenceNavigator { readonly IParameter parameter; public FindParameterReferencesNavigator(IParameter parameter) { this.parameter = parameter; } internal override bool CanMatch(AstNode node) { var expr = node as IdentifierExpression; if (expr != null) return expr.TypeArguments.Count == 0 && parameter.Name == expr.Identifier; var vi = node as VariableInitializer; if (vi != null) return vi.Name == parameter.Name; var pd = node as ParameterDeclaration; if (pd != null) return pd.Name == parameter.Name; var id = node as Identifier; if (id != null) return id.Name == parameter.Name; var nae = node as NamedArgumentExpression; if (nae != null) return nae.Name == parameter.Name; return false; } internal override bool IsMatch(ResolveResult rr) { var lrr = rr as LocalResolveResult; if (lrr != null) return lrr.Variable.Name == parameter.Name && lrr.Variable.Region == parameter.Region; var nar = rr as NamedArgumentResolveResult; return nar != null && nar.Parameter.Name == parameter.Name && nar.Parameter.Region == parameter.Region; } } #endregion } }