private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression receiver, string name, BindingDiagnosticBag diagnostics) { var receiverType = receiver.Type as NamedTypeSymbol; if ((object)receiverType == null || !receiverType.IsAnonymousType) { // We only construct transparent query variables using anonymous types, so if we're trying to navigate through // some other type, we must have some query API where the types don't match up as expected. var info = new CSDiagnosticInfo(ErrorCode.ERR_UnsupportedTransparentIdentifierAccess, name, receiver.ExpressionSymbol ?? receiverType); if (receiver.Type?.IsErrorType() != true) { Error(diagnostics, info, node); } return(new BoundBadExpression( node, LookupResultKind.Empty, ImmutableArray.Create <Symbol>(receiver.ExpressionSymbol), ImmutableArray.Create(BindToTypeForErrorRecovery(receiver)), new ExtendedErrorTypeSymbol(this.Compilation, "", 0, info))); } LookupResult lookupResult = LookupResult.GetInstance(); LookupOptions options = LookupOptions.MustBeInstance; CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); LookupMembersWithFallback(lookupResult, receiver.Type, name, 0, ref useSiteInfo, basesBeingResolved: null, options: options); diagnostics.Add(node, useSiteInfo); var result = BindMemberOfType(node, node, name, 0, indexed: false, receiver, default(SeparatedSyntaxList <TypeSyntax>), default(ImmutableArray <TypeWithAnnotations>), lookupResult, BoundMethodGroupFlags.None, diagnostics); lookupResult.Free(); return(result); }
private BoundExpression SelectField(SimpleNameSyntax node, BoundExpression receiver, string name, DiagnosticBag diagnostics) { var receiverType = receiver.Type as NamedTypeSymbol; if ((object)receiverType == null || !receiverType.IsAnonymousType) { // We only construct transparent query variables using anonymous types, so if we're trying to navigate through // some other type, we must have some hinky query API where the types don't match up as expected. // We should report this as an error of some sort. // TODO: DevDiv #737822 - reword error message and add test. var info = new CSDiagnosticInfo(ErrorCode.ERR_UnsupportedTransparentIdentifierAccess, name, receiver.ExpressionSymbol ?? receiverType); Error(diagnostics, info, node); return(new BoundBadExpression( node, LookupResultKind.Empty, ImmutableArray.Create <Symbol>(receiver.ExpressionSymbol), ImmutableArray.Create <BoundNode>(receiver), new ExtendedErrorTypeSymbol(this.Compilation, "", 0, info))); } LookupResult lookupResult = LookupResult.GetInstance(); LookupOptions options = LookupOptions.MustBeInstance; HashSet <DiagnosticInfo> useSiteDiagnostics = null; LookupMembersWithFallback(lookupResult, receiver.Type, name, 0, ref useSiteDiagnostics, basesBeingResolved: null, options: options); diagnostics.Add(node, useSiteDiagnostics); var result = BindMemberOfType(node, node, name, 0, receiver, default(SeparatedSyntaxList <TypeSyntax>), default(ImmutableArray <TypeSymbol>), lookupResult, BoundMethodGroupFlags.None, diagnostics); result.WasCompilerGenerated = true; lookupResult.Free(); return(result); }
/// <summary> /// Check for a GetEnumerator method on collectionExprType. Failing to satisfy the pattern is not an error - /// it just means that we have to check for an interface instead. /// </summary> /// <param name="collectionExprType">Type of the expression over which to iterate.</param> /// <param name="diagnostics">Populated with *warnings* if there are near misses.</param> /// <param name="builder">Builder to fill in. <see cref="ForEachEnumeratorInfo.Builder.GetEnumeratorMethod"/> set if the pattern in satisfied.</param> /// <returns>True if the method was found (still have to verify that the return (i.e. enumerator) type is acceptable).</returns> /// <remarks> /// Only adds warnings, so does not affect control flow (i.e. no need to check for failure). /// </remarks> private bool SatisfiesGetEnumeratorPattern(ref ForEachEnumeratorInfo.Builder builder, TypeSymbol collectionExprType, DiagnosticBag diagnostics) { LookupResult lookupResult = LookupResult.GetInstance(); MethodSymbol getEnumeratorMethod = FindForEachPatternMethod(collectionExprType, GetEnumeratorMethodName, lookupResult, warningsOnly: true, diagnostics: diagnostics); lookupResult.Free(); builder.GetEnumeratorMethod = getEnumeratorMethod; return((object)getEnumeratorMethod != null); }
private ImmutableArray <Symbol> ComputeSortedCrefMembers(NamespaceOrTypeSymbol?containerOpt, string memberName, string memberNameText, int arity, bool hasParameterList, CSharpSyntaxNode syntax, BindingDiagnosticBag diagnostics, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo) { // Since we may find symbols without going through the lookup API, // expose the symbols via an ArrayBuilder. ArrayBuilder <Symbol> builder; { LookupResult result = LookupResult.GetInstance(); this.LookupSymbolsOrMembersInternal( result, containerOpt, name: memberName, arity: arity, basesBeingResolved: null, options: LookupOptions.AllMethodsOnArityZero, diagnose: false, useSiteInfo: ref useSiteInfo); // CONSIDER: Dev11 also checks for a constructor in the event of an ambiguous result. if (result.IsMultiViable) { // Dev11 doesn't consider members from System.Object when the container is an interface. // Lookup should already have dropped such members. builder = ArrayBuilder <Symbol> .GetInstance(); builder.AddRange(result.Symbols); result.Free(); } else if (memberNameText is "nint" or "nuint" && containerOpt is null && arity == 0 && !hasParameterList) { result.Free(); // Won't be using this. Debug.Assert(memberName == memberNameText); CheckFeatureAvailability(syntax, MessageID.IDS_FeatureNativeInt, diagnostics); builder = ArrayBuilder <Symbol> .GetInstance(); builder.Add(this.GetSpecialType(memberName == "nint" ? SpecialType.System_IntPtr : SpecialType.System_UIntPtr, diagnostics, syntax).AsNativeInteger()); }
internal override void LookupSymbolsInSingleBinder( LookupResult result, string name, int arity, ConsList <Symbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if ((options & LookupOptions.NamespaceAliasesOnly) != 0) { return; } LookupResult tmp = LookupResult.GetInstance(); // usings: Imports.Empty.LookupSymbolInUsings(ConsolidatedUsings, originalBinder, tmp, name, arity, basesBeingResolved, options, diagnose, ref useSiteDiagnostics); // if we found a viable result in imported namespaces, use it instead of unviable symbols found in source: if (tmp.IsMultiViable) { result.MergeEqual(tmp); } tmp.Free(); }
/// <summary> /// Called after it is determined that the expression being enumerated is of a type that /// has a GetEnumerator method. Checks to see if the return type of the GetEnumerator /// method is suitable (i.e. has Current and MoveNext). /// </summary> /// <param name="builder">Must be non-null and contain a non-null GetEnumeratorMethod.</param> /// <param name="diagnostics">Will be populated with pattern diagnostics.</param> /// <returns>True if the return type has suitable members.</returns> /// <remarks> /// It seems that every failure path reports the same diagnostics, so that is left to the caller. /// </remarks> private bool SatisfiesForEachPattern(ref ForEachEnumeratorInfo.Builder builder, DiagnosticBag diagnostics) { Debug.Assert((object)builder.GetEnumeratorMethod != null); MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod; TypeSymbol enumeratorType = getEnumeratorMethod.ReturnType; switch (enumeratorType.TypeKind) { case TypeKind.Class: case TypeKind.Struct: case TypeKind.Interface: case TypeKind.TypeParameter: // Not specifically mentioned in the spec, but consistent with Dev10. case TypeKind.DynamicType: // Not specifically mentioned in the spec, but consistent with Dev10. break; case TypeKind.Submission: // submission class is synthesized and should never appear in a foreach: throw ExceptionUtilities.UnexpectedValue(enumeratorType.TypeKind); default: return(false); } // Use a try-finally since there are many return points LookupResult lookupResult = LookupResult.GetInstance(); try { // If we searched for the accessor directly, we could reuse FindForEachPatternMethod and we // wouldn't have to mangle CurrentPropertyName. However, Dev10 searches for the property and // then extracts the accessor, so we should do the same (in case of accessors with non-standard // names). HashSet <DiagnosticInfo> useSiteDiagnostics = null; this.LookupMembersInType( lookupResult, enumeratorType, CurrentPropertyName, arity: 0, basesBeingResolved: null, options: LookupOptions.Default, // properties are not invocable - their accessors are originalBinder: this, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(this.syntax.Expression, useSiteDiagnostics); useSiteDiagnostics = null; if (!lookupResult.IsSingleViable) { ReportPatternMemberLookupDiagnostics(lookupResult, enumeratorType, CurrentPropertyName, warningsOnly: false, diagnostics: diagnostics); return(false); } // lookupResult.IsSingleViable above guaranteed there is exactly one symbol. Symbol lookupSymbol = lookupResult.SingleSymbolOrDefault; Debug.Assert((object)lookupSymbol != null); if (lookupSymbol.IsStatic || lookupSymbol.DeclaredAccessibility != Accessibility.Public || lookupSymbol.Kind != SymbolKind.Property) { return(false); } // NOTE: accessor can be inherited from overridden property MethodSymbol currentPropertyGetterCandidate = ((PropertySymbol)lookupSymbol).GetOwnOrInheritedGetMethod(); if ((object)currentPropertyGetterCandidate == null) { return(false); } else { bool isAccessible = this.IsAccessible(currentPropertyGetterCandidate, ref useSiteDiagnostics); diagnostics.Add(this.syntax.Expression, useSiteDiagnostics); if (!isAccessible) { // NOTE: per Dev10 and the spec, the property has to be public, but the accessor just has to be accessible return(false); } } builder.CurrentPropertyGetter = currentPropertyGetterCandidate; lookupResult.Clear(); // Reuse the same LookupResult MethodSymbol moveNextMethodCandidate = FindForEachPatternMethod(enumeratorType, MoveNextMethodName, lookupResult, warningsOnly: false, diagnostics: diagnostics); // SPEC VIOLATION: Dev10 checks the return type of the original definition, rather than the return type of the actual method. if ((object)moveNextMethodCandidate == null || moveNextMethodCandidate.IsStatic || moveNextMethodCandidate.DeclaredAccessibility != Accessibility.Public || ((MethodSymbol)moveNextMethodCandidate.OriginalDefinition).ReturnType.SpecialType != SpecialType.System_Boolean) { return(false); } builder.MoveNextMethod = moveNextMethodCandidate; return(true); } finally { lookupResult.Free(); } }
private ImmutableArray <Symbol> ComputeSortedCrefMembers(NamespaceOrTypeSymbol containerOpt, string memberName, int arity, bool hasParameterList, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Since we may find symbols without going through the lookup API, // expose the symbols via an ArrayBuilder. ArrayBuilder <Symbol> builder; { LookupResult result = LookupResult.GetInstance(); this.LookupSymbolsOrMembersInternal( result, containerOpt, name: memberName, arity: arity, basesBeingResolved: null, options: LookupOptions.AllMethodsOnArityZero, diagnose: false, useSiteDiagnostics: ref useSiteDiagnostics); // CONSIDER: Dev11 also checks for a constructor in the event of an ambiguous result. if (result.IsMultiViable) { // Dev11 doesn't consider members from System.Object when the container is an interface. // Lookup should already have dropped such members. builder = ArrayBuilder <Symbol> .GetInstance(); builder.AddRange(result.Symbols); result.Free(); } else { result.Free(); // Won't be using this. // Dev11 has a complicated two-stage process for determining when a cref is really referring to a constructor. // Under two sets of conditions, XmlDocCommentBinder::bindXMLReferenceName will decide that a name refers // to a constructor and under one set of conditions, the calling method, XmlDocCommentBinder::bindXMLReference, // will roll back that decision and return null. // In XmlDocCommentBinder::bindXMLReferenceName: // 1) If an unqualified, non-generic name didn't bind to anything and the name matches the name of the type // to which the doc comment is applied, then bind to a constructor. // 2) If a qualified, non-generic name didn't bind to anything and the LHS of the qualified name is a type // with the same name, then bind to a constructor. // Quoted from XmlDocCommentBinder::bindXMLReference: // Filtering out the case where specifying the name of a generic type without specifying // any arity returns a constructor. This case shouldn't return anything. Note that // returning the constructors was a fix for the wonky constructor behavior, but in order // to not introduce a regression and breaking change we return NULL in this case. // e.g. // // /// <see cref="Goo"/> // class Goo<T> { } // // This cref used not to bind to anything, because before it was looking for a type and // since there was no arity, it didn't find Goo<T>. Now however, it finds Goo<T>.ctor, // which is arguably correct, but would be a breaking change (albeit with minimal impact) // so we catch this case and chuck out the symbol found. // In Roslyn, we're doing everything in one pass, rather than guessing and rolling back. // As in the native compiler, we treat this as a fallback case - something that actually has the // specified name is preferred. NamedTypeSymbol constructorType = null; if (arity == 0) // Member arity { NamedTypeSymbol containerType = containerOpt as NamedTypeSymbol; if ((object)containerType != null) { // Case 1: If the name is qualified by a type with the same name, then we want a // constructor (unless the type is generic, the cref is on/in the type (but not // on/in a nested type), and there were no parens after the member name). if (containerType.Name == memberName && (hasParameterList || containerType.Arity == 0 || this.ContainingType != containerType.OriginalDefinition)) { constructorType = containerType; } } else if ((object)containerOpt == null && hasParameterList) { // Case 2: If the name is not qualified by anything, but we're in the scope // of a type with the same name (regardless of arity), then we want a constructor, // as long as there were parens after the member name. NamedTypeSymbol binderContainingType = this.ContainingType; if ((object)binderContainingType != null && memberName == binderContainingType.Name) { constructorType = binderContainingType; } } } if ((object)constructorType != null) { ImmutableArray <MethodSymbol> instanceConstructors = constructorType.InstanceConstructors; int numInstanceConstructors = instanceConstructors.Length; if (numInstanceConstructors == 0) { return(ImmutableArray <Symbol> .Empty); } builder = ArrayBuilder <Symbol> .GetInstance(numInstanceConstructors); builder.AddRange(instanceConstructors); } else { return(ImmutableArray <Symbol> .Empty); } } } Debug.Assert(builder != null); // Since we resolve ambiguities by just picking the first symbol we encounter, // the order of the symbols matters for repeatability. if (builder.Count > 1) { builder.Sort(ConsistentSymbolOrder.Instance); } return(builder.ToImmutableAndFree()); }