From e4c452c4c7b9124fe94a96f559ff077d59cdf996 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sat, 22 Nov 2008 13:44:36 +0000 Subject: [PATCH] Implement a %plural modifier for complex plural forms in diagnostics. Use it in the overload diagnostics. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59871 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 20 ++-- lib/Basic/Diagnostic.cpp | 126 ++++++++++++++++++++++++ lib/Sema/SemaDeclCXX.cpp | 10 +- lib/Sema/SemaExpr.cpp | 15 +-- lib/Sema/SemaOverload.cpp | 18 ++-- test/SemaCXX/copy-initialization.cpp | 2 +- test/SemaCXX/overloaded-operator.cpp | 2 +- 7 files changed, 152 insertions(+), 41 deletions(-) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 4525355ba3..552cb75a0e 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -873,11 +873,11 @@ DIAG(err_first_label, ERROR, DIAG(err_ovl_diff_return_type, ERROR, "functions that differ only in their return type cannot be overloaded") DIAG(err_ovl_static_nonstatic_member, ERROR, - "static and non-static member functions with the same parameter types cannot be overloaded") + "static and non-static member functions with the same parameter types " + "cannot be overloaded") DIAG(err_ovl_no_viable_function_in_call, ERROR, - "no matching function for call to '%0'.") -DIAG(err_ovl_no_viable_function_in_call_with_cands, ERROR, - "no matching function for call to '%0'; candidates are:") + "no matching function for call to '%0'" + "%plural{0:.|1:; candidate is|:; candidates are:}1") DIAG(err_ovl_ambiguous_call, ERROR, "call to '%0' is ambiguous; candidates are:") DIAG(err_ovl_candidate, NOTE, @@ -885,19 +885,17 @@ DIAG(err_ovl_candidate, NOTE, DIAG(err_ovl_builtin_candidate, NOTE, "built-in candidate function '%0'") DIAG(err_ovl_no_viable_function_in_init, ERROR, - "no matching constructor for initialization of '%0'.") -DIAG(err_ovl_no_viable_function_in_init_with_cands, ERROR, - "no matching constructor for initialization of '%0'; candidates are:") + "no matching constructor for initialization of '%0'" + "%plural{0:.|1:; candidate is|:; candidates are:}1") DIAG(err_ovl_ambiguous_init, ERROR, "call to constructor of '%0' is ambiguous; candidates are:") DIAG(err_ovl_ambiguous_oper, ERROR, "use of overloaded operator '%0' is ambiguous; candidates are:") DIAG(err_ovl_no_viable_oper, ERROR, - "no viable overloaded '%0'; candidates are:") + "no viable overloaded '%0'; candidate%plural{1: is|:s are}1:") DIAG(err_ovl_no_viable_object_call, ERROR, - "no matching function for call to object of type '%0'") -DIAG(err_ovl_no_viable_object_call_with_cands, ERROR, - "no matching function for call to object of type '%0'; candidates are:") + "no matching function for call to object of type '%0'" + "%plural{0:.|1:; candidate is|:; candidates are:}1") DIAG(err_ovl_ambiguous_object_call, ERROR, "call to object of type '%0' is ambiguous; candidates are:") DIAG(err_ovl_surrogate_cand, NOTE, diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 164ac1b636..e1183c602c 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -293,6 +293,128 @@ static void HandleIntegerSModifier(unsigned ValNo, } +/// PluralNumber - Parse an unsigned integer and advance Start. +static unsigned PluralNumber(const char *&Start, const char *End) +{ + // Programming 101: Parse a decimal number :-) + unsigned Val = 0; + while (Start != End && *Start >= '0' && *Start <= '9') { + Val *= 10; + Val += *Start - '0'; + ++Start; + } + return Val; +} + +/// TestPluralRange - Test if Val is in the parsed range. Modifies Start. +static bool TestPluralRange(unsigned Val, const char *&Start, const char *End) +{ + if (*Start != '[') { + unsigned Ref = PluralNumber(Start, End); + return Ref == Val; + } + + ++Start; + unsigned Low = PluralNumber(Start, End); + assert(*Start == ',' && "Bad plural expression syntax: expected ,"); + ++Start; + unsigned High = PluralNumber(Start, End); + assert(*Start == ']' && "Bad plural expression syntax: expected )"); + ++Start; + return Low <= Val && Val <= High; +} + +/// EvalPluralExpr - Actual expression evaluator for HandlePluralModifier. +static bool EvalPluralExpr(unsigned ValNo, const char *Start, const char *End) +{ + // Empty condition? + if (*Start == ':') + return true; + + while (1) { + char C = *Start; + if (C == '%') { + // Modulo expression + ++Start; + unsigned Arg = PluralNumber(Start, End); + assert(*Start == '=' && "Bad plural expression syntax: expected ="); + ++Start; + unsigned ValMod = ValNo % Arg; + if (TestPluralRange(ValMod, Start, End)) + return true; + } else { + assert(C == '[' || (C >= '0' && C <= '9') && + "Bad plural expression syntax: unexpected character"); + // Range expression + if (TestPluralRange(ValNo, Start, End)) + return true; + } + + // Scan for next or-expr part. + Start = std::find(Start, End, ','); + if(Start == End) + break; + ++Start; + } + return false; +} + +/// HandlePluralModifier - Handle the integer 'plural' modifier. This is used +/// for complex plural forms, or in languages where all plurals are complex. +/// The syntax is: %plural{cond1:form1|cond2:form2|:form3}, where condn are +/// conditions that are tested in order, the form corresponding to the first +/// that applies being emitted. The empty condition is always true, making the +/// last form a default case. +/// Conditions are simple boolean expressions, where n is the number argument. +/// Here are the rules. +/// condition := expression | empty +/// empty := -> always true +/// expression := numeric [',' expression] -> logical or +/// numeric := range -> true if n in range +/// | '%' number '=' range -> true if n % number in range +/// range := number +/// | '[' number ',' number ']' -> ranges are inclusive both ends +/// +/// Here are some examples from the GNU gettext manual written in this form: +/// English: +/// {1:form0|:form1} +/// Latvian: +/// {0:form2|%100=11,%10=0,%10=[2,9]:form1|:form0} +/// Gaeilge: +/// {1:form0|2:form1|:form2} +/// Romanian: +/// {1:form0|0,%100=[1,19]:form1|:form2} +/// Lithuanian: +/// {%10=0,%100=[10,19]:form2|%10=1:form0|:form1} +/// Russian (requires repeated form): +/// {%100=[11,14]:form2|%10=1:form0|%10=[2,4]:form1|:form2} +/// Slovak +/// {1:form0|[2,4]:form1|:form2} +/// Polish (requires repeated form): +/// {1:form0|%100=[10,20]:form2|%10=[2,4]:form1|:form2} +static void HandlePluralModifier(unsigned ValNo, + const char *Argument, unsigned ArgumentLen, + llvm::SmallVectorImpl &OutStr) +{ + const char *ArgumentEnd = Argument + ArgumentLen; + while (1) { + assert(Argument < ArgumentEnd && "Plural expression didn't match."); + const char *ExprEnd = Argument; + while (*ExprEnd != ':') { + assert(ExprEnd != ArgumentEnd && "Plural missing expression end"); + ++ExprEnd; + } + if (EvalPluralExpr(ValNo, Argument, ExprEnd)) { + Argument = ExprEnd + 1; + ExprEnd = std::find(Argument, ArgumentEnd, '|'); + OutStr.append(Argument, ExprEnd); + return; + } + Argument = std::find(Argument, ArgumentEnd - 1, '|') + 1; + } +} + + /// FormatDiagnostic - Format this diagnostic into a string, substituting the /// formal arguments into the %0 slots. The result is appended onto the Str /// array. @@ -374,6 +496,8 @@ FormatDiagnostic(llvm::SmallVectorImpl &OutStr) const { HandleSelectModifier((unsigned)Val, Argument, ArgumentLen, OutStr); } else if (ModifierIs(Modifier, ModifierLen, "s")) { HandleIntegerSModifier(Val, OutStr); + } else if (ModifierIs(Modifier, ModifierLen, "plural")) { + HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr); } else { assert(ModifierLen == 0 && "Unknown integer modifier"); // FIXME: Optimize @@ -389,6 +513,8 @@ FormatDiagnostic(llvm::SmallVectorImpl &OutStr) const { HandleSelectModifier(Val, Argument, ArgumentLen, OutStr); } else if (ModifierIs(Modifier, ModifierLen, "s")) { HandleIntegerSModifier(Val, OutStr); + } else if (ModifierIs(Modifier, ModifierLen, "plural")) { + HandlePluralModifier((unsigned)Val, Argument, ArgumentLen, OutStr); } else { assert(ModifierLen == 0 && "Unknown integer modifier"); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index cf1f01728d..d65d6fff5e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1440,13 +1440,9 @@ Sema::PerformInitializationByConstructor(QualType ClassType, return cast(Best->Function); case OR_No_Viable_Function: - if (CandidateSet.empty()) - Diag(Loc, diag::err_ovl_no_viable_function_in_init) << InitEntity <getSourceRange().getBegin(), - diag::err_ovl_no_viable_function_in_call) - << Ovl->getName() << Fn->getSourceRange(); - else { - Diag(Fn->getSourceRange().getBegin(), - diag::err_ovl_no_viable_function_in_call_with_cands) - << Ovl->getName() << Fn->getSourceRange(); - PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); - } + Diag(Fn->getSourceRange().getBegin(), + diag::err_ovl_no_viable_function_in_call) + << Ovl->getName() << (unsigned)CandidateSet.size() + << Fn->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); return true; case OR_Ambiguous: diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index eb38eee8d5..3c2c0ab489 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -3050,16 +3050,11 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, break; case OR_No_Viable_Function: - if (CandidateSet.empty()) - Diag(Object->getSourceRange().getBegin(), - diag::err_ovl_no_viable_object_call) - << Object->getType().getAsString() << Object->getSourceRange(); - else { - Diag(Object->getSourceRange().getBegin(), - diag::err_ovl_no_viable_object_call_with_cands) - << Object->getType().getAsString() << Object->getSourceRange(); - PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); - } + Diag(Object->getSourceRange().getBegin(), + diag::err_ovl_no_viable_object_call) + << Object->getType().getAsString() << (unsigned)CandidateSet.size() + << Object->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); break; case OR_Ambiguous: @@ -3220,7 +3215,8 @@ Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, << BasePtr->getType().getAsString() << BasePtr->getSourceRange(); else Diag(OpLoc, diag::err_ovl_no_viable_oper) - << "operator->" << BasePtr->getSourceRange(); + << "operator->" << (unsigned)CandidateSet.size() + << BasePtr->getSourceRange(); PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); return true; diff --git a/test/SemaCXX/copy-initialization.cpp b/test/SemaCXX/copy-initialization.cpp index 17028da069..5ef84de9ac 100644 --- a/test/SemaCXX/copy-initialization.cpp +++ b/test/SemaCXX/copy-initialization.cpp @@ -10,7 +10,7 @@ public: class Y : public X { }; void f(Y y, int *ip, float *fp) { - X x1 = y; // expected-error{{no matching constructor for initialization of 'x1'; candidates are:}} + X x1 = y; // expected-error{{no matching constructor for initialization of 'x1'; candidate is:}} X x2 = 0; X x3 = ip; X x4 = fp; // expected-error{{incompatible type initializing 'x4', expected 'class X'}} diff --git a/test/SemaCXX/overloaded-operator.cpp b/test/SemaCXX/overloaded-operator.cpp index 98f0bb07f6..bc473834bf 100644 --- a/test/SemaCXX/overloaded-operator.cpp +++ b/test/SemaCXX/overloaded-operator.cpp @@ -170,5 +170,5 @@ struct Arrow2 { void test_arrow(Arrow1 a1, Arrow2 a2, const Arrow2 a3) { int &i1 = a1->m; int &i2 = a2->m; - a3->m; // expected-error{{no viable overloaded 'operator->'; candidates are}} + a3->m; // expected-error{{no viable overloaded 'operator->'; candidate is}} } -- 2.40.0