[cfe-commits] r59803 - in /cfe/trunk: include/clang/Basic/DiagnosticKinds.def lib/Basic/Diagnostic.cpp lib/Sema/SemaDeclCXX.cpp

Chris Lattner sabre at nondot.org
Thu Nov 20 23:50:03 PST 2008


Author: lattner
Date: Fri Nov 21 01:50:02 2008
New Revision: 59803

URL: http://llvm.org/viewvc/llvm-project?rev=59803&view=rev
Log:
Add the concept of "modifiers" to the clang diagnostic format 
strings.  This allows us to have considerable flexibility in how
these things are displayed and provides extra information that
allows us to merge away diagnostics that are very similar.

Diagnostic modifiers are a string of characters with the regex
[-a-z]+ that occur between the % and digit.  They may 
optionally have an argument that can parameterize them.

For now, I've added two example modifiers.  One is a very useful
tool that allows you to factor commonality across diagnostics
that need single words or phrases combined.  Basically you can
use %select{a|b|c}4 with with an integer argument that selects
either a/b/c based on an integer value in the range [0..3).

The second modifier is also an integer modifier, aimed to help
English diagnostics handle plurality.  "%s3" prints to 's' if 
integer argument #3 is not 1, otherwise it prints to nothing.
I'm fully aware that 's' is an English concept and doesn't
apply to all situations (mouse vs mice).  However, this is very
useful and we can add other crazy modifiers once we add support
for polish! ;-)

I converted a couple C++ diagnostics over to use this as an
example, I'd appreciate it if others could merge the other
likely candiates.  If you have other modifiers that you want,
lets talk on cfe-dev.


Modified:
    cfe/trunk/include/clang/Basic/DiagnosticKinds.def
    cfe/trunk/lib/Basic/Diagnostic.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=59803&r1=59802&r2=59803&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Fri Nov 21 01:50:02 2008
@@ -1351,26 +1351,17 @@
      "overloaded '%0' cannot be a static member function")
 DIAG(err_operator_overload_default_arg, ERROR,
      "parameter of overloaded '%0' cannot have a default argument")
-DIAG(err_operator_overload_must_be_unary, ERROR,
-     "overloaded '%0' must be a unary operator (has %1 parameter)")
-DIAG(err_operator_overload_must_be_binary, ERROR,
-     "overloaded '%0' must be a binary operator (has %1 parameter)")
-DIAG(err_operator_overload_must_be_unary_or_binary, ERROR,
-     "overloaded '%0' must be a unary or binary operator (has %1 parameter)")
-DIAG(err_operator_overload_must_be_unary_plural, ERROR,
-     "overloaded '%0' must be a unary operator (has %1 parameters)")
-DIAG(err_operator_overload_must_be_binary_plural, ERROR,
-     "overloaded '%0' must be a binary operator (has %1 parameters)")
-DIAG(err_operator_overload_must_be_unary_or_binary_plural, ERROR,
-     "overloaded '%0' must be a unary or binary operator (has %1 parameters)")
+DIAG(err_operator_overload_must_be_unaryx, ERROR,
+     "overloaded '%0' must be a unary operator (has %1 parameter%s1)")
+DIAG(err_operator_overload_must_be_binaryx, ERROR,
+     "overloaded '%0' must be a binary operator (has %1 parameter%s1)")
+DIAG(err_operator_overload_must_be_unary_or_binaryx, ERROR,
+     "overloaded '%0' must be a unary or binary operator (has %1 parameter%s1)")
 DIAG(err_operator_overload_must_be_member, ERROR,
      "overloaded '%0' must be a non-static member function")
-DIAG(err_operator_overload_post_inc_must_be_int, ERROR,
-     "parameter of overloaded post-increment operator must have type 'int' "
-     "(not '%0')")
-DIAG(err_operator_overload_post_dec_must_be_int, ERROR,
-     "parameter of overloaded post-decrement operator must have type 'int' "
-     "(not '%0')")
+DIAG(err_operator_overload_post_incdec_must_be_int, ERROR,
+     "parameter of overloaded post-%select{increment|decrement}1 operator must"
+     " have type 'int' (not '%0')")
 DIAG(err_operator_missing_type_specifier, ERROR,
      "missing type specifier after 'operator'")
 

Modified: cfe/trunk/lib/Basic/Diagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Diagnostic.cpp?rev=59803&r1=59802&r2=59803&view=diff

==============================================================================
--- cfe/trunk/lib/Basic/Diagnostic.cpp (original)
+++ cfe/trunk/lib/Basic/Diagnostic.cpp Fri Nov 21 01:50:02 2008
@@ -249,6 +249,48 @@
 DiagnosticClient::~DiagnosticClient() {}
 
 
+/// ModifierIs - Return true if the specified modifier matches specified string.
+template <std::size_t StrLen>
+static bool ModifierIs(const char *Modifier, unsigned ModifierLen,
+                       const char (&Str)[StrLen]) {
+  return StrLen-1 == ModifierLen && !memcmp(Modifier, Str, StrLen-1);
+}
+
+/// HandleSelectModifier - Handle the integer 'select' modifier.  This is used
+/// like this:  %select{foo|bar|baz}2.  This means that the integer argument
+/// "%2" has a value from 0-2.  If the value is 0, the diagnostic prints 'foo'.
+/// If the value is 1, it prints 'bar'.  If it has the value 2, it prints 'baz'.
+/// This is very useful for certain classes of variant diagnostics.
+static void HandleSelectModifier(unsigned ValNo,
+                                 const char *Argument, unsigned ArgumentLen,
+                                 llvm::SmallVectorImpl<char> &OutStr) {
+  const char *ArgumentEnd = Argument+ArgumentLen;
+  
+  // Skip over 'ValNo' |'s.
+  while (ValNo) {
+    const char *NextVal = std::find(Argument, ArgumentEnd, '|');
+    assert(NextVal != ArgumentEnd && "Value for integer select modifier was"
+           " larger than the number of options in the diagnostic string!");
+    Argument = NextVal+1;  // Skip this string.
+    --ValNo;
+  }
+  
+  // Get the end of the value.  This is either the } or the |.
+  const char *EndPtr = std::find(Argument, ArgumentEnd, '|');
+  // Add the value to the output string.
+  OutStr.append(Argument, EndPtr);
+}
+
+/// HandleIntegerSModifier - Handle the integer 's' modifier.  This adds the
+/// letter 's' to the string if the value is not 1.  This is used in cases like
+/// this:  "you idiot, you have %4 parameter%s4!".
+static void HandleIntegerSModifier(unsigned ValNo,
+                                   llvm::SmallVectorImpl<char> &OutStr) {
+  if (ValNo != 1)
+    OutStr.push_back('s');
+}
+
+
 /// FormatDiagnostic - Format this diagnostic into a string, substituting the
 /// formal arguments into the %0 slots.  The result is appended onto the Str
 /// array.
@@ -263,43 +305,97 @@
       const char *StrEnd = std::find(DiagStr, DiagEnd, '%');
       OutStr.append(DiagStr, StrEnd);
       DiagStr = StrEnd;
+      continue;
     } else if (DiagStr[1] == '%') {
       OutStr.push_back('%');  // %% -> %.
       DiagStr += 2;
-    } else {
-      assert(isdigit(DiagStr[1]) && "Must escape % with %%");
-      unsigned StrNo = DiagStr[1] - '0';
-
-      switch (getArgKind(StrNo)) {
-      case DiagnosticInfo::ak_std_string: {
-        const std::string &S = getArgStdStr(StrNo);
-        OutStr.append(S.begin(), S.end());
-        break;
-      }
-      case DiagnosticInfo::ak_c_string: {
-        const char *S = getArgCStr(StrNo);
-        OutStr.append(S, S + strlen(S));
-        break;
+      continue;
+    }
+    
+    // Skip the %.
+    ++DiagStr;
+    
+    // This must be a placeholder for a diagnostic argument.  The format for a
+    // placeholder is one of "%0", "%modifier0", or "%modifier{arguments}0".
+    // The digit is a number from 0-9 indicating which argument this comes from.
+    // The modifier is a string of digits from the set [-a-z]+, arguments is a
+    // brace enclosed string.
+    const char *Modifier = 0, *Argument = 0;
+    unsigned ModifierLen = 0, ArgumentLen = 0;
+    
+    // Check to see if we have a modifier.  If so eat it.
+    if (!isdigit(DiagStr[0])) {
+      Modifier = DiagStr;
+      while (DiagStr[0] == '-' ||
+             (DiagStr[0] >= 'a' && DiagStr[0] <= 'z'))
+        ++DiagStr;
+      ModifierLen = DiagStr-Modifier;
+
+      // If we have an argument, get it next.
+      if (DiagStr[0] == '{') {
+        ++DiagStr; // Skip {.
+        Argument = DiagStr;
+        
+        for (; DiagStr[0] != '}'; ++DiagStr)
+          assert(DiagStr[0] && "Mismatched {}'s in diagnostic string!");
+        ArgumentLen = DiagStr-Argument;
+        ++DiagStr;  // Skip }.
       }
-      case DiagnosticInfo::ak_sint: {
+    }
+      
+    assert(isdigit(*DiagStr) && "Invalid format for argument in diagnostic");
+    unsigned StrNo = *DiagStr++ - '0';
+
+    switch (getArgKind(StrNo)) {
+    case DiagnosticInfo::ak_std_string: {
+      const std::string &S = getArgStdStr(StrNo);
+      assert(ModifierLen == 0 && "No modifiers for strings yet");
+      OutStr.append(S.begin(), S.end());
+      break;
+    }
+    case DiagnosticInfo::ak_c_string: {
+      const char *S = getArgCStr(StrNo);
+      assert(ModifierLen == 0 && "No modifiers for strings yet");
+      OutStr.append(S, S + strlen(S));
+      break;
+    }
+    case DiagnosticInfo::ak_identifierinfo: {
+      const IdentifierInfo *II = getArgIdentifier(StrNo);
+      assert(ModifierLen == 0 && "No modifiers for strings yet");
+      OutStr.append(II->getName(), II->getName() + II->getLength());
+      break;
+    }
+    case DiagnosticInfo::ak_sint: {
+      int Val = getArgSInt(StrNo);
+      
+      if (ModifierIs(Modifier, ModifierLen, "select")) {
+        HandleSelectModifier((unsigned)Val, Argument, ArgumentLen, OutStr);
+      } else if (ModifierIs(Modifier, ModifierLen, "s")) {
+        HandleIntegerSModifier(Val, OutStr);
+      } else {
+        assert(ModifierLen == 0 && "Unknown integer modifier");
         // FIXME: Optimize
-        std::string S = llvm::itostr(getArgSInt(StrNo));
+        std::string S = llvm::itostr(Val);
         OutStr.append(S.begin(), S.end());
-        break;
       }
-      case DiagnosticInfo::ak_uint: {
+      break;
+    }
+    case DiagnosticInfo::ak_uint: {
+      unsigned Val = getArgUInt(StrNo);
+      
+      if (ModifierIs(Modifier, ModifierLen, "select")) {
+        HandleSelectModifier(Val, Argument, ArgumentLen, OutStr);
+      } else if (ModifierIs(Modifier, ModifierLen, "s")) {
+        HandleIntegerSModifier(Val, OutStr);
+      } else {
+        assert(ModifierLen == 0 && "Unknown integer modifier");
+        
         // FIXME: Optimize
-        std::string S = llvm::utostr_32(getArgUInt(StrNo));
+        std::string S = llvm::utostr_32(Val);
         OutStr.append(S.begin(), S.end());
         break;
       }
-      case DiagnosticInfo::ak_identifierinfo: {
-        const IdentifierInfo *II = getArgIdentifier(StrNo);
-        OutStr.append(II->getName(), II->getName() + II->getLength());
-        break;
-      }
-      }
-      DiagStr += 2;
+    }
     }
   }
 }

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=59803&r1=59802&r2=59803&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Nov 21 01:50:02 2008
@@ -1874,22 +1874,13 @@
     // We have the wrong number of parameters.
     diag::kind DK;
     if (CanBeUnaryOperator && CanBeBinaryOperator) {
-      if (NumParams == 1)
-        DK = diag::err_operator_overload_must_be_unary_or_binary;
-      else
-        DK = diag::err_operator_overload_must_be_unary_or_binary;
+      DK = diag::err_operator_overload_must_be_unary_or_binaryx;
     } else if (CanBeUnaryOperator) {
-      if (NumParams == 1)
-        DK = diag::err_operator_overload_must_be_unary;
-      else
-        DK = diag::err_operator_overload_must_be_unary_plural;
-    } else if (CanBeBinaryOperator) {
-      if (NumParams == 1)
-        DK = diag::err_operator_overload_must_be_binary;
-      else
-        DK = diag::err_operator_overload_must_be_binary_plural;
+      DK = diag::err_operator_overload_must_be_unaryx;
     } else {
-      assert(false && "All non-call overloaded operators are unary or binary!");
+      assert(CanBeBinaryOperator &&
+             "All non-call overloaded operators are unary or binary!");
+      DK = diag::err_operator_overload_must_be_binaryx;
     }
 
     return Diag(FnDecl->getLocation(), DK) << FnDecl->getName() << NumParams;
@@ -1925,15 +1916,10 @@
     if (const BuiltinType *BT = LastParam->getType()->getAsBuiltinType())
       ParamIsInt = BT->getKind() == BuiltinType::Int;
 
-    if (!ParamIsInt) {
-      diag::kind DK;
-      if (Op == OO_PlusPlus)
-        DK = diag::err_operator_overload_post_inc_must_be_int;
-      else
-        DK = diag::err_operator_overload_post_dec_must_be_int;
-      return Diag(LastParam->getLocation(), DK) 
-        << LastParam->getType().getAsString();
-    }
+    if (!ParamIsInt)
+      return Diag(LastParam->getLocation(),
+                  diag::err_operator_overload_post_incdec_must_be_int) 
+        << LastParam->getType().getAsString() << (Op == OO_MinusMinus);
   }
 
   return false;





More information about the cfe-commits mailing list