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); } }
// 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, DiagnosticBag diagnostics) { const int arity = 0; CrefParameterListSyntax parameterListSyntax = syntax.Parameters; // 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); memberName = memberName ?? OperatorFacts.UnaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind); if (memberName == null) { 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)); }
private static void AddNonTypeMemberNames( Syntax.InternalSyntax.CSharpSyntaxNode member, ImmutableHashSet <string> .Builder set, ref bool anyNonTypeMembers) { switch (member.Kind) { case SyntaxKind.FieldDeclaration: anyNonTypeMembers = true; set.Add(((Syntax.InternalSyntax.FieldDeclarationSyntax)member).Declaration.Identifier.ValueText); break; case SyntaxKind.EventFieldDeclaration: anyNonTypeMembers = true; set.Add(((Syntax.InternalSyntax.EventFieldDeclarationSyntax)member).Declaration.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((int)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; } }
// 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); }