/// <summary> /// Finds all of the matching type definitions for the return type of this method definition /// </summary> /// <returns>An enumerable of the matching type definitions for this method</returns> public override IEnumerable <TypeDefinition> ResolveType() { var matchingMethods = FindMatches().OfType <MethodDefinition>().ToList(); if (matchingMethods.Any()) { foreach (var methodDefinition in matchingMethods) { var matchingTypes = Enumerable.Empty <TypeDefinition>(); if (methodDefinition.ReturnType != null) { matchingTypes = methodDefinition.ReturnType.ResolveType(); } else if (methodDefinition.IsConstructor) { var methodName = methodDefinition.Name; //define local var because of Resharper warning about accessing foreach var in closure matchingTypes = methodDefinition.GetAncestors <TypeDefinition>().Where(td => td.Name == methodName); } foreach (var result in matchingTypes) { yield return(result); } } } else { //no matches //handle case of calls to default (implicit) constructors if (IsConstructor && Arguments.Count == 0) { var tempType = new TypeUse() { Name = this.Name, Location = this.Location, ParentStatement = this.ParentStatement, ProgrammingLanguage = this.ProgrammingLanguage }; foreach (var result in tempType.ResolveType()) { yield return(result); } } } }
/// <summary> /// Finds all of the matching type definitions for the return type of this method definition /// </summary> /// <returns>An enumerable of the matching type definitions for this method</returns> public override IEnumerable<TypeDefinition> ResolveType() { var matchingMethods = FindMatches().OfType<MethodDefinition>().ToList(); if(matchingMethods.Any()) { foreach(var methodDefinition in matchingMethods) { var matchingTypes = Enumerable.Empty<TypeDefinition>(); if(methodDefinition.ReturnType != null) { matchingTypes = methodDefinition.ReturnType.ResolveType(); } else if(methodDefinition.IsConstructor) { var methodName = methodDefinition.Name; //define local var because of Resharper warning about accessing foreach var in closure matchingTypes = methodDefinition.GetAncestors<TypeDefinition>().Where(td => td.Name == methodName); } foreach(var result in matchingTypes) { yield return result; } } } else { //no matches //handle case of calls to default (implicit) constructors if(IsConstructor && Arguments.Count == 0) { var tempType = new TypeUse() { Name = this.Name, Location = this.Location, ParentStatement = this.ParentStatement, ProgrammingLanguage = this.ProgrammingLanguage }; foreach(var result in tempType.ResolveType()) { yield return result; } } } }
/// <summary> /// Finds matching <see cref="MethodDefinition">method definitions</see> for this method call. /// This method searches for matches in the ancestor scopes of the call. Because method calls can also be /// to constructors and destructors, this will also search for matching types and then /// constructors within those types /// </summary> /// <returns>An enumerable of method definitions that match this method call</returns> public override IEnumerable<INamedEntity> FindMatches() { if(ParentStatement == null) { throw new InvalidOperationException("ParentStatement is null"); } if(IsConstructor || IsDestructor) { List<TypeDefinition> typeDefinitions; if(this.Name == "this" || (this.Name == "base" && this.ProgrammingLanguage == Language.CSharp) || (this.Name == "super" && this.ProgrammingLanguage == Language.Java)) { typeDefinitions = TypeDefinition.GetTypeForKeyword(this).ToList(); } else { var tempTypeUse = new TypeUse() { Name = this.Name, ParentStatement = this.ParentStatement, Location = this.Location }; typeDefinitions = tempTypeUse.ResolveType().ToList(); } //Handle case of C++ constructor initialization lists. //These will be marked as constructor calls. They can be used to initialize fields, though, in which case the call name will be the field name, //rather than a type name. if(!typeDefinitions.Any() && IsConstructorInitializer && ProgrammingLanguage == Language.CPlusPlus) { var containingType = ParentStatement.GetAncestorsAndSelf<TypeDefinition>().FirstOrDefault(); if(containingType != null) { //search this type and its parents for a field matching the name of the call var matchingField = containingType.GetParentTypesAndSelf(true).SelectMany(t => t.GetNamedChildren<VariableDeclaration>(this.Name)).FirstOrDefault(); if(matchingField != null) { typeDefinitions = matchingField.VariableType.ResolveType().ToList(); } } } var matchingMethods = from typeDefinition in typeDefinitions from method in typeDefinition.GetNamedChildren<MethodDefinition>(typeDefinition.Name) where SignatureMatches(typeDefinition.Name, method) select method; return matchingMethods; } //If there's a calling expression, resolve and search under the results var callingScopes = GetCallingScope(); if(callingScopes != null) { IEnumerable<INamedEntity> matches = Enumerable.Empty<INamedEntity>(); foreach(var scope in callingScopes) { var localMatches = scope.GetNamedChildren<MethodDefinition>(this.Name).Where(SignatureMatches).ToList(); var callingType = scope as TypeDefinition; if(!localMatches.Any() && callingType != null) { //also search under the base types of the calling scope matches = matches.Concat(callingType.SearchParentTypes<MethodDefinition>(this.Name, SignatureMatches)); } else { matches = matches.Concat(localMatches); } } return matches; } //search enclosing scopes and base types for the method foreach(var scope in ParentStatement.GetAncestors()) { var matches = scope.GetNamedChildren<MethodDefinition>(this).Where(SignatureMatches).ToList(); if(matches.Any()) { return matches; } var typeDef = scope as TypeDefinition; if(typeDef != null) { //search the base types var baseTypeMatches = typeDef.SearchParentTypes<MethodDefinition>(this.Name, SignatureMatches).ToList(); if(baseTypeMatches.Any()) { return baseTypeMatches; } } } //we didn't find it locally, search under imported namespaces return (from import in GetImports() from match in import.ImportedNamespace.GetDescendantsAndSelf<NameUse>().Last().FindMatches().OfType<NamedScope>() from child in match.GetNamedChildren<MethodDefinition>(this.Name) where SignatureMatches(child) select child); }
/// <summary> /// Finds matching <see cref="MethodDefinition">method definitions</see> for this method call. /// This method searches for matches in the ancestor scopes of the call. Because method calls can also be /// to constructors and destructors, this will also search for matching types and then /// constructors within those types /// </summary> /// <returns>An enumerable of method definitions that match this method call</returns> public override IEnumerable <INamedEntity> FindMatches() { if (ParentStatement == null) { throw new InvalidOperationException("ParentStatement is null"); } if (IsConstructor || IsDestructor) { List <TypeDefinition> typeDefinitions; if (this.Name == "this" || (this.Name == "base" && this.ProgrammingLanguage == Language.CSharp) || (this.Name == "super" && this.ProgrammingLanguage == Language.Java)) { typeDefinitions = TypeDefinition.GetTypeForKeyword(this).ToList(); } else { var tempTypeUse = new TypeUse() { Name = this.Name, ParentStatement = this.ParentStatement, Location = this.Location }; typeDefinitions = tempTypeUse.ResolveType().ToList(); } //Handle case of C++ constructor initialization lists. //These will be marked as constructor calls. They can be used to initialize fields, though, in which case the call name will be the field name, //rather than a type name. if (!typeDefinitions.Any() && IsConstructorInitializer && ProgrammingLanguage == Language.CPlusPlus) { var containingType = ParentStatement.GetAncestorsAndSelf <TypeDefinition>().FirstOrDefault(); if (containingType != null) { //search this type and its parents for a field matching the name of the call var matchingField = containingType.GetParentTypesAndSelf(true).SelectMany(t => t.GetNamedChildren <VariableDeclaration>(this.Name)).FirstOrDefault(); if (matchingField != null) { typeDefinitions = matchingField.VariableType.ResolveType().ToList(); } } } var matchingMethods = from typeDefinition in typeDefinitions from method in typeDefinition.GetNamedChildren <MethodDefinition>(typeDefinition.Name) where SignatureMatches(typeDefinition.Name, method) select method; return(matchingMethods); } //If there's a calling expression, resolve and search under the results var callingScopes = GetCallingScope(); if (callingScopes != null) { IEnumerable <INamedEntity> matches = Enumerable.Empty <INamedEntity>(); foreach (var scope in callingScopes) { var localMatches = scope.GetNamedChildren <MethodDefinition>(this.Name).Where(SignatureMatches).ToList(); var callingType = scope as TypeDefinition; if (!localMatches.Any() && callingType != null) { //also search under the base types of the calling scope matches = matches.Concat(callingType.SearchParentTypes <MethodDefinition>(this.Name, SignatureMatches)); } else { matches = matches.Concat(localMatches); } } return(matches); } //search enclosing scopes and base types for the method foreach (var scope in ParentStatement.GetAncestors()) { var matches = scope.GetNamedChildren <MethodDefinition>(this).Where(SignatureMatches).ToList(); if (matches.Any()) { return(matches); } var typeDef = scope as TypeDefinition; if (typeDef != null) { //search the base types var baseTypeMatches = typeDef.SearchParentTypes <MethodDefinition>(this.Name, SignatureMatches).ToList(); if (baseTypeMatches.Any()) { return(baseTypeMatches); } } } //we didn't find it locally, search under imported namespaces return(from import in GetImports() from match in import.ImportedNamespace.GetDescendantsAndSelf <NameUse>().Last().FindMatches().OfType <NamedScope>() from child in match.GetNamedChildren <MethodDefinition>(this.Name) where SignatureMatches(child) select child); }
/// <summary> /// If there is a calling expession preceding this NameUse, this method resolves it /// to determine the scope(s) in which to search for the use's name. /// </summary> /// <returns>An enumerable of the named entities that may contain the name being used in this NameUse. /// Returns null if there is no suitable calling expression. /// Returns an empty enumerable if there is a calling expression, but no matches are found.</returns> protected IEnumerable <NamedScope> GetCallingScope() { var siblings = GetSiblingsBeforeSelf().ToList(); var priorOp = siblings.LastOrDefault() as OperatorUse; if (priorOp == null || !NameInclusionOperators.Contains(priorOp.Text)) { return(null); } if (siblings.Count == 1) { //This use is preceded by a name inclusion operator and nothing else //this is probably only possible in C++: ::MyGlobalClass //just return the global namespace return(ParentStatement.GetAncestorsAndSelf <NamespaceDefinition>().Where(n => n.IsGlobal)); } var callingExp = siblings[siblings.Count - 2]; //second-to-last sibling var callingName = callingExp as NameUse; if (callingName == null) { //Not a NameUse, probably an Expression return(callingExp.ResolveType()); } var matches = callingName.FindMatches(); var scopes = new List <NamedScope>(); foreach (var match in matches) { //TODO: update this to use polymorphism if (match is MethodDefinition) { var method = match as MethodDefinition; if (method.ReturnType != null) { scopes.AddRange(((MethodDefinition)match).ReturnType.ResolveType()); } else if (method.IsConstructor) { //create the constructor return type var tempTypeUse = new TypeUse() { Name = method.Name, ParentStatement = method.ParentStatement, Location = method.PrimaryLocation }; scopes.AddRange(tempTypeUse.ResolveType()); } } else if (match is PropertyDefinition) { scopes.AddRange(((PropertyDefinition)match).ReturnType.ResolveType()); } else if (match is VariableDeclaration) { scopes.AddRange(((VariableDeclaration)match).VariableType.ResolveType()); } else { //the only other possibilities are all NamedScopes scopes.Add((NamedScope)match); } } if (scopes.Count == 0) { return(null); } else { return(scopes); } }
/// <summary> /// If there is a calling expession preceding this NameUse, this method resolves it /// to determine the scope(s) in which to search for the use's name. /// </summary> /// <returns>An enumerable of the named entities that may contain the name being used in this NameUse. /// Returns null if there is no suitable calling expression. /// Returns an empty enumerable if there is a calling expression, but no matches are found.</returns> protected IEnumerable<NamedScope> GetCallingScope() { var siblings = GetSiblingsBeforeSelf().ToList(); var priorOp = siblings.LastOrDefault() as OperatorUse; if(priorOp == null || !NameInclusionOperators.Contains(priorOp.Text)) { return null; } if(siblings.Count == 1) { //This use is preceded by a name inclusion operator and nothing else //this is probably only possible in C++: ::MyGlobalClass //just return the global namespace return ParentStatement.GetAncestorsAndSelf<NamespaceDefinition>().Where(n => n.IsGlobal); } var callingExp = siblings[siblings.Count - 2]; //second-to-last sibling var callingName = callingExp as NameUse; if(callingName == null) { //Not a NameUse, probably an Expression return callingExp.ResolveType(); } var matches = callingName.FindMatches(); var scopes = new List<NamedScope>(); foreach(var match in matches) { //TODO: update this to use polymorphism if(match is MethodDefinition) { var method = match as MethodDefinition; if(method.ReturnType != null) { scopes.AddRange(((MethodDefinition)match).ReturnType.ResolveType()); } else if(method.IsConstructor) { //create the constructor return type var tempTypeUse = new TypeUse() { Name = method.Name, ParentStatement = method.ParentStatement, Location = method.PrimaryLocation }; scopes.AddRange(tempTypeUse.ResolveType()); } } else if(match is PropertyDefinition) { scopes.AddRange(((PropertyDefinition)match).ReturnType.ResolveType()); } else if(match is VariableDeclaration) { scopes.AddRange(((VariableDeclaration)match).VariableType.ResolveType()); } else { //the only other possibilities are all NamedScopes scopes.Add((NamedScope)match); } } return scopes; }