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);
            }
Пример #3
0
        /// <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);
        }
Пример #4
0
        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());
                }
Пример #5
0
        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();
        }
Пример #6
0
        /// <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();
            }
        }
Пример #7
0
        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());
        }