Beispiel #1
0
        public static string OperatorNameFromDeclaration(Syntax.InternalSyntax.OperatorDeclarationSyntax declaration)
        {
            var opTokenKind = declaration.OperatorToken.Kind;

            if (SyntaxFacts.IsBinaryExpressionOperatorToken(opTokenKind))
            {
                // Some tokens may be either unary or binary operators (e.g. +, -).
                if (SyntaxFacts.IsPrefixUnaryExpressionOperatorToken(opTokenKind) &&
                    declaration.ParameterList.Parameters.Count == 1)
                {
                    return(OperatorFacts.UnaryOperatorNameFromSyntaxKind(opTokenKind));
                }

                return(OperatorFacts.BinaryOperatorNameFromSyntaxKind(opTokenKind));
            }
            else if (SyntaxFacts.IsUnaryOperatorDeclarationToken(opTokenKind))
            {
                return(OperatorFacts.UnaryOperatorNameFromSyntaxKind(opTokenKind));
            }
            else
            {
                // fallback for error recovery
                return(WellKnownMemberNames.UnaryPlusOperatorName);
            }
        }
Beispiel #2
0
        public static string OperatorNameFromDeclaration(Syntax.InternalSyntax.OperatorDeclarationSyntax declaration)
        {
            var  opTokenKind = declaration.OperatorToken.Kind;
            bool isChecked   = declaration.CheckedKeyword?.Kind == SyntaxKind.CheckedKeyword;

            if (SyntaxFacts.IsBinaryExpressionOperatorToken(opTokenKind))
            {
                // Some tokens may be either unary or binary operators (e.g. +, -).
                if (opTokenKind != SyntaxKind.AsteriskToken && // IsPrefixUnaryExpressionOperatorToken treats it as pointer dereference operator
                    SyntaxFacts.IsPrefixUnaryExpressionOperatorToken(opTokenKind) &&
                    declaration.ParameterList.Parameters.Count == 1)
                {
                    return(OperatorFacts.UnaryOperatorNameFromSyntaxKind(opTokenKind, isChecked));
                }

                return(OperatorFacts.BinaryOperatorNameFromSyntaxKind(opTokenKind, isChecked));
            }
            else if (SyntaxFacts.IsUnaryOperatorDeclarationToken(opTokenKind))
            {
                return(OperatorFacts.UnaryOperatorNameFromSyntaxKind(opTokenKind, isChecked));
            }
            else
            {
                // fallback for error recovery
                return(WellKnownMemberNames.UnaryPlusOperatorName);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Populates a list of unary operator results with those from any
        /// witness in scope at the operator site.
        /// </summary>
        /// <param name="kind">
        /// The unary operator kind of the expression.
        /// </param>
        /// <param name="operand">
        /// The operand expression.
        /// </param>
        /// <param name="results">
        /// The results list to populate.
        /// </param>
        /// <param name="useSiteDiagnostics">
        /// The set of diagnostics to populate with any errors.
        /// </param>
        /// <returns>
        /// True if we managed to find candidate operators from the concept
        /// witnesses in scope; false otherwise.
        /// </returns>
        private bool GetUnaryWitnessOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(operand != null);

            string name = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind);

            var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

            foreach (var method in GetWitnessOperators(name, 1, ref useSiteDiagnostics))
            {
                // TODO: nullability
                operators.Add(new UnaryOperatorSignature(UnaryOperatorKind.UserDefined | kind, method.ParameterTypes[0], method.ReturnType, method));
            }

            bool hasCandidates = CandidateOperators(operators, operand, results, ref useSiteDiagnostics);

            operators.Free();
            return(hasCandidates);
        }
        private void GetUserDefinedUnaryOperatorsFromType(
            TypeSymbol constrainedToTypeOpt,
            NamedTypeSymbol type,
            UnaryOperatorKind kind,
            bool isChecked,
            ArrayBuilder <UnaryOperatorSignature> operators)
        {
            Debug.Assert(operators.Count == 0);

            string name1 = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind, isChecked);

            getDeclaredOperators(constrainedToTypeOpt, type, kind, name1, operators);

            if (isChecked && SyntaxFacts.IsCheckedOperator(name1))
            {
                string name2      = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind, isChecked: false);
                var    operators2 = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

                // Add regular operators as well.
                getDeclaredOperators(constrainedToTypeOpt, type, kind, name2, operators2);

                // Drop operators that have a match among the checked ones.
                if (operators.Count != 0)
                {
                    for (int i = operators2.Count - 1; i >= 0; i--)
                    {
                        foreach (UnaryOperatorSignature signature1 in operators)
                        {
                            if (SourceMemberContainerTypeSymbol.DoOperatorsPair(signature1.Method, operators2[i].Method))
                            {
                                operators2.RemoveAt(i);
                                break;
                            }
                        }
                    }
                }

                operators.AddRange(operators2);
                operators2.Free();
            }

            addLiftedOperators(constrainedToTypeOpt, kind, operators);
Beispiel #5
0
        // NOTE: not guaranteed to be a method (e.g. class op_Addition)
        // NOTE: constructor fallback logic applies
        private ImmutableArray <Symbol> BindOperatorMemberCref(OperatorMemberCrefSyntax syntax, NamespaceOrTypeSymbol?containerOpt, out Symbol?ambiguityWinner, BindingDiagnosticBag diagnostics)
        {
            const int arity = 0;

            CrefParameterListSyntax?parameterListSyntax = syntax.Parameters;
            bool isChecked = syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword);

            // NOTE: Prefer binary to unary, unless there is exactly one parameter.
            // CONSIDER: we're following dev11 by never using a binary operator name if there's
            // exactly one parameter, but doing so would allow us to match single-parameter constructors.
            SyntaxKind operatorTokenKind = syntax.OperatorToken.Kind();
            string?    memberName        = parameterListSyntax != null && parameterListSyntax.Parameters.Count == 1
                ? null
                : OperatorFacts.BinaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked);

            memberName = memberName ?? OperatorFacts.UnaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked: isChecked);

            if (memberName == null ||
                (isChecked && !syntax.OperatorToken.IsMissing && !SyntaxFacts.IsCheckedOperator(memberName))) // the operator cannot be checked
            {
                ambiguityWinner = null;
                return(ImmutableArray <Symbol> .Empty);
            }

            ImmutableArray <Symbol> sortedSymbols = ComputeSortedCrefMembers(syntax, containerOpt, memberName, arity, syntax.Parameters != null, diagnostics);

            if (sortedSymbols.IsEmpty)
            {
                ambiguityWinner = null;
                return(ImmutableArray <Symbol> .Empty);
            }

            return(ProcessCrefMemberLookupResults(
                       sortedSymbols,
                       arity,
                       syntax,
                       typeArgumentListSyntax: null,
                       parameterListSyntax: parameterListSyntax,
                       ambiguityWinner: out ambiguityWinner,
                       diagnostics: diagnostics));
        }
Beispiel #6
0
        private static void AddNonTypeMemberNames(Syntax.InternalSyntax.CSharpSyntaxNode member, HashSet <string> set, ref bool anyNonTypeMembers)
        {
            switch (member.Kind)
            {
            case SyntaxKind.FieldDeclaration:
                anyNonTypeMembers = true;
                Syntax.InternalSyntax.SeparatedSyntaxList <Syntax.InternalSyntax.VariableDeclaratorSyntax> fieldDeclarators =
                    ((Syntax.InternalSyntax.FieldDeclarationSyntax)member).Declaration.Variables;
                int numFieldDeclarators = fieldDeclarators.Count;
                for (int i = 0; i < numFieldDeclarators; i++)
                {
                    set.Add(fieldDeclarators[i].Identifier.ValueText);
                }
                break;

            case SyntaxKind.EventFieldDeclaration:
                anyNonTypeMembers = true;
                Syntax.InternalSyntax.SeparatedSyntaxList <Syntax.InternalSyntax.VariableDeclaratorSyntax> eventDeclarators =
                    ((Syntax.InternalSyntax.EventFieldDeclarationSyntax)member).Declaration.Variables;
                int numEventDeclarators = eventDeclarators.Count;
                for (int i = 0; i < numEventDeclarators; i++)
                {
                    set.Add(eventDeclarators[i].Identifier.ValueText);
                }
                break;

            case SyntaxKind.MethodDeclaration:
                anyNonTypeMembers = true;
                // Member names are exposed via NamedTypeSymbol.MemberNames and are used primarily
                // as an acid test to determine whether a more in-depth search of a type is worthwhile.
                // We decided that it was reasonable to exclude explicit interface implementations
                // from the list of member names.
                var methodDecl = (Syntax.InternalSyntax.MethodDeclarationSyntax)member;
                if (methodDecl.ExplicitInterfaceSpecifier == null)
                {
                    set.Add(methodDecl.Identifier.ValueText);
                }
                break;

            case SyntaxKind.PropertyDeclaration:
                anyNonTypeMembers = true;
                // Handle in the same way as explicit method implementations
                var propertyDecl = (Syntax.InternalSyntax.PropertyDeclarationSyntax)member;
                if (propertyDecl.ExplicitInterfaceSpecifier == null)
                {
                    set.Add(propertyDecl.Identifier.ValueText);
                }
                break;

            case SyntaxKind.EventDeclaration:
                anyNonTypeMembers = true;
                // Handle in the same way as explicit method implementations
                var eventDecl = (Syntax.InternalSyntax.EventDeclarationSyntax)member;
                if (eventDecl.ExplicitInterfaceSpecifier == null)
                {
                    set.Add(eventDecl.Identifier.ValueText);
                }
                break;

            case SyntaxKind.ConstructorDeclaration:
                anyNonTypeMembers = true;
                set.Add(((Syntax.InternalSyntax.ConstructorDeclarationSyntax)member).Modifiers.Any(SyntaxKind.StaticKeyword)
                        ? WellKnownMemberNames.StaticConstructorName
                        : WellKnownMemberNames.InstanceConstructorName);
                break;

            case SyntaxKind.DestructorDeclaration:
                anyNonTypeMembers = true;
                set.Add(WellKnownMemberNames.DestructorName);
                break;

            case SyntaxKind.IndexerDeclaration:
                anyNonTypeMembers = true;
                set.Add(WellKnownMemberNames.Indexer);
                break;

            case SyntaxKind.OperatorDeclaration:
                anyNonTypeMembers = true;
                var opDecl = (Syntax.InternalSyntax.OperatorDeclarationSyntax)member;
                var name   = OperatorFacts.OperatorNameFromDeclaration(opDecl);
                set.Add(name);
                break;

            case SyntaxKind.ConversionOperatorDeclaration:
                anyNonTypeMembers = true;
                set.Add(((Syntax.InternalSyntax.ConversionOperatorDeclarationSyntax)member).ImplicitOrExplicitKeyword.Kind == SyntaxKind.ImplicitKeyword
                        ? WellKnownMemberNames.ImplicitConversionName
                        : WellKnownMemberNames.ExplicitConversionName);
                break;

            case SyntaxKind.GlobalStatement:
                anyNonTypeMembers = true;
                break;
            }
        }
Beispiel #7
0
        // Returns true if there were any applicable candidates.
        private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(operand != null);

            if ((object)operand.Type == null)
            {
                // If the operand has no type -- because it is a null reference or a lambda or a method group --
                // there is no way we can determine what type to search for user-defined operators.
                return(false);
            }

            // Spec 7.3.5 Candidate user-defined operators
            // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined
            // SPEC: operators provided by T for op(A) is determined as follows:

            // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T.
            // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if
            // SPEC: at least one operator is applicable with respect to A then the set of candidate
            // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object
            // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate
            // SPEC: operators is the set provided by the direct base class of T0, or the effective
            // SPEC: base class of T0 if T0 is a type parameter.

            TypeSymbol type0 = operand.Type.StrippedType();

            // Searching for user-defined operators is expensive; let's take an early out if we can.
            if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0))
            {
                return(false);
            }

            string name      = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind);
            var    operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

            bool hadApplicableCandidates = false;

            NamedTypeSymbol current = type0 as NamedTypeSymbol;

            if ((object)current == null)
            {
                current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics);
            }

            if ((object)current == null && type0.IsTypeParameter())
            {
                current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics);
            }

            for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
            {
                operators.Clear();
                GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators);
                results.Clear();
                if (CandidateOperators(operators, operand, results, ref useSiteDiagnostics))
                {
                    hadApplicableCandidates = true;
                    break;
                }
            }

            operators.Free();

            return(hadApplicableCandidates);
        }
        private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(operand != null);

            if ((object)operand.Type == null)
            {
                // If the operand has no type -- because it is a null reference or a lambda or a method group --
                // there is no way we can determine what type to search for user-defined operators.
                return(false);
            }

            // Spec 7.3.5 Candidate user-defined operators
            // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined
            // SPEC: operators provided by T for op(A) is determined as follows:

            // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T.
            // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if
            // SPEC: at least one operator is applicable with respect to A then the set of candidate
            // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object
            // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate
            // SPEC: operators is the set provided by the direct base class of T0, or the effective
            // SPEC: base class of T0 if T0 is a type parameter.

            // https://github.com/dotnet/roslyn/issues/34451: The spec quote should be adjusted to cover operators from interfaces as well.
            // From https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-27.md:
            // - We only even look for operator implementations in interfaces if one of the operands has a type that is an interface or
            // a type parameter with a non-empty effective base interface list.
            // - The applicable operators from classes / structs shadow those in interfaces.This matters for constrained type parameters:
            // the effective base class can shadow operators from effective base interfaces.
            // - If we find an applicable candidate in an interface, that candidate shadows all applicable operators in base interfaces:
            // we stop looking.

            TypeSymbol type0 = operand.Type.StrippedType();

            // Searching for user-defined operators is expensive; let's take an early out if we can.
            if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0))
            {
                return(false);
            }

            string name      = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind);
            var    operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance();

            bool hadApplicableCandidates = false;

            NamedTypeSymbol current = type0 as NamedTypeSymbol;

            if ((object)current == null)
            {
                current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics);
            }

            if ((object)current == null && type0.IsTypeParameter())
            {
                current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics);
            }

            for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
            {
                operators.Clear();
                GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators);
                results.Clear();
                if (CandidateOperators(operators, operand, results, ref useSiteDiagnostics))
                {
                    hadApplicableCandidates = true;
                    break;
                }
            }

            // Look in base interfaces, or effective interfaces for type parameters
            if (!hadApplicableCandidates)
            {
                ImmutableArray <NamedTypeSymbol> interfaces = default;
                if (type0.IsInterfaceType())
                {
                    interfaces = type0.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics);
                }
                else if (type0.IsTypeParameter())
                {
                    interfaces = ((TypeParameterSymbol)type0).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics);
                }

                if (!interfaces.IsDefaultOrEmpty)
                {
                    var shadowedInterfaces = PooledHashSet <NamedTypeSymbol> .GetInstance();

                    var resultsFromInterface = ArrayBuilder <UnaryOperatorAnalysisResult> .GetInstance();

                    results.Clear();

                    foreach (NamedTypeSymbol @interface in interfaces)
                    {
                        if ([email protected])
                        {
                            // this code could be reachable in error situations
                            continue;
                        }

                        if (shadowedInterfaces.Contains(@interface))
                        {
                            // this interface is "shadowed" by a derived interface
                            continue;
                        }

                        operators.Clear();
                        resultsFromInterface.Clear();
                        GetUserDefinedUnaryOperatorsFromType(@interface, kind, name, operators);
                        if (CandidateOperators(operators, operand, resultsFromInterface, ref useSiteDiagnostics))
                        {
                            hadApplicableCandidates = true;
                            results.AddRange(resultsFromInterface);

                            // this interface "shadows" all its base interfaces
                            shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics));
                        }
                    }

                    shadowedInterfaces.Free();
                    resultsFromInterface.Free();
                }
            }

            operators.Free();

            return(hadApplicableCandidates);
        }