public void InferFromMethodReturnType() { // static void M<T>(Func<T> f) {} // M(Console.ReadKey); // type inference produces ConsoleKeyInfo var T = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "T"); IType declType = compilation.FindType(typeof(Console)); var methods = new MethodListWithDeclaringType(declType, declType.GetMethods(m => m.Name == "ReadKey")); var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "ReadKey", new[] { methods }, new IType[0]); bool success; Assert.AreEqual( new [] { compilation.FindType(typeof(ConsoleKeyInfo)) }, ti.InferTypeArguments(new [] { T }, new [] { argument }, new [] { new ParameterizedType(compilation.FindType(typeof(Func<>)).GetDefinition(), new[] { T }) }, out success)); Assert.IsTrue(success); }
IEnumerable<ICompletionData> CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, SyntaxTree unit, int parameter, bool controlSpace) { var result = new CompletionDataWrapper(this); var addedEnums = new HashSet<string>(); var addedDelegates = new HashSet<string>(); foreach (var method in resolveResult.Methods) { CreateParameterForInvocation(result, method, state, parameter, addedEnums, addedDelegates); } foreach (var methods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in methods) { if (resolveResult.Methods.Contains(method)) continue; CreateParameterForInvocation(result, new ReducedExtensionMethod(method), state, parameter, addedEnums, addedDelegates); } } foreach (var method in resolveResult.Methods) { if (parameter < method.Parameters.Count && method.Parameters [parameter].Type.Kind == TypeKind.Delegate) { AutoSelect = false; AutoCompleteEmptyMatch = false; } foreach (var p in method.Parameters) { result.AddNamedParameterVariable(p); } } if (!controlSpace) { if (addedEnums.Count + addedDelegates.Count == 0) { return Enumerable.Empty<ICompletionData>(); } AutoCompleteEmptyMatch = false; AutoSelect = false; } AddContextCompletion(result, state, invocation); // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); // if (addedDelegates.Count > 0) { // foreach (var data in result.Result) { // if (data is MemberCompletionData) // ((MemberCompletionData)data).IsDelegateExpected = true; // } // } return result.Result; }
public void CannotInferFromMethodParameterTypes() { // static void M<A, B>(Func<A, B> f) {} // M(int.Parse); // type inference fails var A = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "A"); var B = new DefaultTypeParameter(compilation, SymbolKind.Method, 1, "B"); IType declType = compilation.FindType(typeof(int)); var methods = new MethodListWithDeclaringType(declType, declType.GetMethods(m => m.Name == "Parse")); var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "Parse", new[] { methods }, new IType[0]); bool success; ti.InferTypeArguments(new [] { A, B }, new [] { argument }, new [] { new ParameterizedType(compilation.FindType(typeof(Func<,>)).GetDefinition(), new[] { A, B }) }, out success); Assert.IsFalse(success); }
/// <summary> /// Resolves an object creation. /// </summary> /// <param name="type">Type of the object to create.</param> /// <param name="arguments"> /// Arguments passed to the constructor. /// The resolver may mutate this array to wrap elements in <see cref="ConversionResolveResult"/>s! /// </param> /// <param name="argumentNames"> /// The argument names. Pass the null string for positional arguments. /// </param> /// <param name="allowProtectedAccess"> /// Whether to allow calling protected constructors. /// This should be false except when resolving constructor initializers. /// </param> /// <param name="initializerStatements"> /// Statements for Objects/Collections initializer. /// <see cref="InvocationResolveResult.InitializerStatements"/> /// </param> /// <returns>InvocationResolveResult or ErrorResolveResult</returns> public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList<ResolveResult> initializerStatements = null) { if (type.Kind == TypeKind.Delegate && arguments.Length == 1) { ResolveResult input = arguments[0]; IMethod invoke = input.Type.GetDelegateInvokeMethod(); if (invoke != null) { input = new MethodGroupResolveResult( input, invoke.Name, methods: new[] { new MethodListWithDeclaringType(invoke.DeclaringType) { invoke } }, typeArguments: EmptyList<IType>.Instance ); } return Convert(input, type); } OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List<IMethod>() : null); foreach (IMethod ctor in type.GetConstructors()) { if (lookup.IsAccessible(ctor, allowProtectedAccess)) { var orErrors = or.AddCandidate(ctor); if (allApplicable != null && OverloadResolution.IsApplicable(orErrors)) allApplicable.Add(ctor); } else or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible); } if (allApplicable != null && allApplicable.Count > 1) { // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor. return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, AddArgumentNamesIfNecessary(arguments, argumentNames), initializerStatements); } if (or.BestCandidate != null) { return or.CreateResolveResult(null, initializerStatements); } else { return new ErrorResolveResult(type); } }
IEnumerable<ICompletionData> CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, CompilationUnit unit, int parameter, bool controlSpace) { var result = new CompletionDataWrapper(this); var addedEnums = new HashSet<string>(); var addedDelegates = new HashSet<string>(); foreach (var method in resolveResult.Methods) { if (method.Parameters.Count <= parameter) { continue; } var resolvedType = method.Parameters [parameter].Type; if (resolvedType.Kind == TypeKind.Enum) { if (addedEnums.Contains(resolvedType.ReflectionName)) { continue; } addedEnums.Add(resolvedType.ReflectionName); AddEnumMembers(result, resolvedType, state); } else if (resolvedType.Kind == TypeKind.Delegate) { if (addedDelegates.Contains(resolvedType.ReflectionName)) continue; string parameterDefinition = AddDelegateHandlers(result, resolvedType); string varName = "Handle" + method.Parameters [parameter].Type.Name + method.Parameters [parameter].Name; result.Result.Add( factory.CreateEventCreationCompletionData( varName, resolvedType, null, parameterDefinition, currentMember, currentType) ); } } if (!controlSpace) { if (addedEnums.Count + addedDelegates.Count == 0) { return Enumerable.Empty<ICompletionData>(); } AutoCompleteEmptyMatch = false; AutoSelect = false; } AddContextCompletion(result, state, invocation, unit); // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); // if (addedDelegates.Count > 0) { // foreach (var data in result.Result) { // if (data is MemberCompletionData) // ((MemberCompletionData)data).IsDelegateExpected = true; // } // } return result.Result; }
IEnumerable<IMethod> CollectMethods(AstNode resolvedNode, MethodGroupResolveResult resolveResult) { var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); bool onlyStatic = false; if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) { onlyStatic = true; } var methods = new List<IMethod>(); foreach (var method in resolveResult.Methods) { if (method.IsConstructor) { continue; } if (!lookup.IsAccessible (method, true)) continue; if (onlyStatic && !method.IsStatic) { continue; } if (method.IsShadowing) { for (int j = 0; j < methods.Count; j++) { if (ParameterListComparer.Instance.Equals(methods[j].Parameters, method.Parameters)) { methods.RemoveAt (j); j--; } } } methods.Add (method); } foreach (var m in methods) { yield return m; } foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in extMethods) { if (methods.Contains (method)) continue; yield return new ReducedExtensionMethod (method); } } }
IEnumerable<ICompletionData> CreateParameterCompletion(MethodGroupResolveResult resolveResult, CSharpResolver state, AstNode invocation, int parameter, bool controlSpace) { var result = new CompletionDataWrapper (this); var addedEnums = new HashSet<string> (); var addedDelegates = new HashSet<string> (); foreach (var method in resolveResult.Methods) { if (method.Parameters.Count <= parameter) continue; var resolvedType = method.Parameters [parameter].Type; if (resolvedType.Kind == TypeKind.Enum) { if (addedEnums.Contains (resolvedType.ReflectionName)) continue; addedEnums.Add (resolvedType.ReflectionName); AddEnumMembers (result, resolvedType, state); } else if (resolvedType.Kind == TypeKind.Delegate) { // if (addedDelegates.Contains (resolvedType.DecoratedFullName)) // continue; // addedDelegates.Add (resolvedType.DecoratedFullName); // string parameterDefinition = AddDelegateHandlers (completionList, resolvedType, false, addedDelegates.Count == 1); // string varName = "Handle" + method.Parameters [parameter].ReturnType.Name + method.Parameters [parameter].Name; // result.Add (new EventCreationCompletionData (document, varName, resolvedType, null, parameterDefinition, resolver.Unit.GetMemberAt (location), resolvedType) { AddSemicolon = false }); } } if (!controlSpace) { if (addedEnums.Count + addedDelegates.Count == 0) return Enumerable.Empty<ICompletionData> (); AutoCompleteEmptyMatch = false; AutoSelect = false; } AddContextCompletion (result, state, invocation); // resolver.AddAccessibleCodeCompletionData (ExpressionContext.MethodBody, cdc); // if (addedDelegates.Count > 0) { // foreach (var data in result.Result) { // if (data is MemberCompletionData) // ((MemberCompletionData)data).IsDelegateExpected = true; // } // } return result.Result; }
IEnumerable<IMethod> CollectMethods(AstNode resolvedNode, MethodGroupResolveResult resolveResult) { var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly); bool onlyStatic = false; if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) { onlyStatic = true; } foreach (var method in resolveResult.Methods) { if (method.IsConstructor) { continue; } if (!lookup.IsAccessible (method, true)) continue; if (onlyStatic && !method.IsStatic) { continue; } yield return method; } foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in extMethods) { yield return method; } } }
void MakeOutputTypeInference(ResolveResult e, IType t) { Log.WriteLine(" MakeOutputTypeInference from " + e + " to " + t); // If E is an anonymous function with inferred return type U (§7.5.2.12) and T is a delegate type or expression // tree type with return type Tb, then a lower-bound inference (§7.5.2.9) is made from U to Tb. LambdaResolveResult lrr = e as LambdaResolveResult; if (lrr != null) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { IType inferredReturnType; if (lrr.IsImplicitlyTyped) { if (m.Parameters.Count != lrr.Parameters.Count) { return; // cannot infer due to mismatched parameter lists } TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); IType[] inferredParameterTypes = new IType[m.Parameters.Count]; for (int i = 0; i < inferredParameterTypes.Length; i++) { IType parameterType = m.Parameters[i].Type; inferredParameterTypes[i] = parameterType.AcceptVisitor(substitution); } inferredReturnType = lrr.GetInferredReturnType(inferredParameterTypes); } else { inferredReturnType = lrr.GetInferredReturnType(null); } MakeLowerBoundInference(inferredReturnType, m.ReturnType); return; } } // Otherwise, if E is a method group and T is a delegate type or expression tree type // with parameter types T1…Tk and return type Tb, and overload resolution // of E with the types T1…Tk yields a single method with return type U, then a lower-bound // inference is made from U to Tb. MethodGroupResolveResult mgrr = e as MethodGroupResolveResult; if (mgrr != null) { IMethod m = GetDelegateOrExpressionTreeSignature(t); if (m != null) { ResolveResult[] args = new ResolveResult[m.Parameters.Count]; TypeParameterSubstitution substitution = GetSubstitutionForFixedTPs(); for (int i = 0; i < args.Length; i++) { IParameter param = m.Parameters[i]; IType parameterType = param.Type.AcceptVisitor(substitution); if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); } else { args[i] = new ResolveResult(parameterType); } } var or = mgrr.PerformOverloadResolution(compilation, args, allowExpandingParams: false); if (or.FoundApplicableCandidate && or.BestCandidateAmbiguousWith == null) { IType returnType = or.GetBestCandidateWithSubstitutedTypeArguments().ReturnType; MakeLowerBoundInference(returnType, m.ReturnType); } } return; } // Otherwise, if E is an expression with type U, then a lower-bound inference is made from U to T. if (IsValidType(e.Type)) { MakeLowerBoundInference(e.Type, t); } }
public static ResolveResult Resolve(Lazy <ICompilation> compilation, CSharpUnresolvedFile unresolvedFile, SyntaxTree syntaxTree, TextLocation location, out AstNode node, CancellationToken cancellationToken = default(CancellationToken)) { node = syntaxTree.GetNodeAt(location); if (node == null || node is ArrayInitializerExpression) { return(null); } if (node.Parent is UsingAliasDeclaration && node.Role == UsingAliasDeclaration.AliasRole) { var r = new CSharpAstResolver(compilation.Value, syntaxTree, unresolvedFile); return(r.Resolve(((UsingAliasDeclaration)node.Parent).Import, cancellationToken)); } if (CSharpAstResolver.IsUnresolvableNode(node)) { if (node is Identifier) { node = node.Parent; } else if (node.NodeType == NodeType.Token) { if (node.Parent is IndexerExpression || node.Parent is ConstructorInitializer || node.Role == IndexerDeclaration.ThisKeywordRole) { // There's no other place where one could hover to see the indexer's tooltip, // so we need to resolve it when hovering over the '[' or ']'. // For constructor initializer, the same applies to the 'base'/'this' token. node = node.Parent; } else { return(null); } } else { // don't resolve arbitrary nodes - we don't want to show tooltips for everything return(null); } } else { // It's a resolvable node. // However, we usually don't want to show the tooltip everywhere // For example, hovering with the mouse over an empty line between two methods causes // node==TypeDeclaration, but we don't want to show any tooltip. if (!node.GetChildByRole(Roles.Identifier).IsNull) { // We'll suppress the tooltip for resolvable nodes if there is an identifier that // could be hovered over instead: return(null); } } if (node == null) { return(null); } if (node.Parent is ObjectCreateExpression && node.Role == Roles.Type) { node = node.Parent; } InvocationExpression parentInvocation = null; if ((node is IdentifierExpression || node is MemberReferenceExpression || node is PointerReferenceExpression) && node.Role != Roles.Argument) { // we also need to resolve the invocation parentInvocation = node.Parent as InvocationExpression; } // TODO: I think we should provide an overload so that an existing CSharpAstResolver can be reused CSharpAstResolver resolver = new CSharpAstResolver(compilation.Value, syntaxTree, unresolvedFile); ResolveResult rr = resolver.Resolve(node, cancellationToken); MethodGroupResolveResult mgrr = rr as MethodGroupResolveResult; if (mgrr != null) { // For method groups, resolve the parent invocation instead. if (parentInvocation != null) { return(resolver.Resolve(parentInvocation)); } if (node is Expression) { // If it's not an invocation, try if it's a conversion to a delegate type: Conversion c = resolver.GetConversion((Expression)node, cancellationToken); if (c.IsMethodGroupConversion) { return(new MemberResolveResult(mgrr.TargetResult, c.Method)); } } } return(rr); }