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); } }
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); } }
/// <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);
// 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)); }
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; } }
// 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); }