erik.pilkington created this revision.
erik.pilkington added a reviewer: rsmith.
erik.pilkington added a subscriber: cfe-commits.

Previously, invalid parameters in a literal operator function were diagnosed 
with an uninformative catch all. This commit breaks the catch all into a couple 
of more informative cases.

http://reviews.llvm.org/D16930

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/Sema/SemaDeclCXX.cpp
  test/CXX/over/over.oper/over.literal/p5.cpp
  test/CXX/over/over.oper/over.literal/p8.cpp
  test/SemaCXX/literal-operators.cpp

Index: test/SemaCXX/literal-operators.cpp
===================================================================
--- test/SemaCXX/literal-operators.cpp
+++ test/SemaCXX/literal-operators.cpp
@@ -35,13 +35,14 @@
 void operator "" _good (c*);
 
 // Check extra cv-qualifiers
-void operator "" _cv_good (volatile const char *, const size_t); // expected-error {{parameter declaration for literal operator 'operator""_cv_good' is not valid}}
+void operator "" _cv_good (volatile const char *, const size_t); // expected-error {{invalid literal operator parameter type 'const volatile char *', did you mean 'const char *'?}}
 
 // Template declaration
 template <char...> void operator "" _good ();
 
-// FIXME: Test some invalid decls that might crop up.
-template <typename...> void operator "" _invalid(); // expected-error {{parameter declaration for literal operator 'operator""_invalid' is not valid}}
+template <typename...> void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}}
+template <wchar_t...> void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}}
+template <unsigned long long...> void operator "" _invalid(); // expected-error {{invalid template parameters for literal operator}}
 
 _Complex float operator""if(long double); // expected-warning {{reserved}}
 _Complex float test_if_1() { return 2.0f + 1.5if; };
Index: test/CXX/over/over.oper/over.literal/p8.cpp
===================================================================
--- test/CXX/over/over.oper/over.literal/p8.cpp
+++ test/CXX/over/over.oper/over.literal/p8.cpp
@@ -12,6 +12,6 @@
 float operator " " B(const char *); // expected-error {{must be '""'}} expected-warning {{reserved}}
 string operator "" 5X(const char *, std::size_t); // expected-error {{expected identifier}}
 double operator "" _miles(double); // expected-error {{parameter}}
-template<char...> int operator "" j(const char*); // expected-error {{parameter}}
+template<char...> int operator "" j(const char*); // expected-error {{template}}
 
 float operator ""_E(const char *);
Index: test/CXX/over/over.oper/over.literal/p5.cpp
===================================================================
--- test/CXX/over/over.oper/over.literal/p5.cpp
+++ test/CXX/over/over.oper/over.literal/p5.cpp
@@ -12,11 +12,11 @@
   friend U operator "" _a(const T *, size_t); // expected-error {{parameter}}
 };
 template<char...> struct V {
-  friend void operator "" _b(); // expected-error {{parameter}}
+  friend void operator "" _b(); // expected-error {{template}}
 };
 
-template<char... C, int N = 0> void operator "" _b(); // expected-error {{parameter}}
-template<char... C> void operator "" _b(int N = 0); // expected-error {{parameter}}
-template<char, char...> void operator "" _b(); // expected-error {{parameter}}
-template<typename T> T operator "" _b(const char *); // expected-error {{parameter}}
-template<typename T> int operator "" _b(const T *, size_t); // expected-error {{parameter}}
+template<char... C, int N = 0> void operator "" _b(); // expected-error {{template}}
+template<char... C> void operator "" _b(int N = 0); // expected-error {{template}}
+template<char, char...> void operator "" _b(); // expected-error {{template}}
+template<typename T> T operator "" _b(const char *); // expected-error {{template}}
+template<typename T> int operator "" _b(const T *, size_t); // expected-error {{template}}
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -11780,10 +11780,9 @@
     return true;
   }
 
-  bool Valid = false;
-
   // This might be the definition of a literal operator template.
   FunctionTemplateDecl *TpDecl = FnDecl->getDescribedFunctionTemplate();
+
   // This might be a specialization of a literal operator template.
   if (!TpDecl)
     TpDecl = FnDecl->getPrimaryTemplate();
@@ -11793,100 +11792,155 @@
   // template signatures, and the only valid signatures with no parameters.
   if (TpDecl) {
     if (FnDecl->param_size() == 0) {
-      // Must have one or two template parameters
       TemplateParameterList *Params = TpDecl->getTemplateParameters();
+
+      // Must have one or two template parameters
       if (Params->size() == 1) {
         NonTypeTemplateParmDecl *PmDecl =
           dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(0));
 
         // The template parameter must be a char parameter pack.
         if (PmDecl && PmDecl->isTemplateParameterPack() &&
             Context.hasSameType(PmDecl->getType(), Context.CharTy))
-          Valid = true;
+          goto FinishedParams;
+
       } else if (Params->size() == 2) {
         TemplateTypeParmDecl *PmType =
           dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));
         NonTypeTemplateParmDecl *PmArgs =
           dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(1));
 
         // The second template parameter must be a parameter pack with the
         // first template parameter as its type.
-        if (PmType && PmArgs &&
-            !PmType->isTemplateParameterPack() &&
-            PmArgs->isTemplateParameterPack()) {
+        if (PmType && PmArgs && !PmType->isTemplateParameterPack() &&
+          PmArgs->isTemplateParameterPack()) {
           const TemplateTypeParmType *TArgs =
             PmArgs->getType()->getAs<TemplateTypeParmType>();
           if (TArgs && TArgs->getDepth() == PmType->getDepth() &&
               TArgs->getIndex() == PmType->getIndex()) {
-            Valid = true;
             if (ActiveTemplateInstantiations.empty())
               Diag(FnDecl->getLocation(),
                    diag::ext_string_literal_operator_template);
+            goto FinishedParams;
           }
         }
+      } else {
+        Diag(TpDecl->getLocation(),
+             diag::err_literal_operator_template_with_params);
+        return true;
       }
     }
-  } else if (FnDecl->param_size()) {
-    // Check the first parameter
-    FunctionDecl::param_iterator Param = FnDecl->param_begin();
 
-    QualType T = (*Param)->getType().getUnqualifiedType();
-
-    // unsigned long long int, long double, and any character type are allowed
-    // as the only parameters.
-    if (Context.hasSameType(T, Context.UnsignedLongLongTy) ||
-        Context.hasSameType(T, Context.LongDoubleTy) ||
-        Context.hasSameType(T, Context.CharTy) ||
-        Context.hasSameType(T, Context.WideCharTy) ||
-        Context.hasSameType(T, Context.Char16Ty) ||
-        Context.hasSameType(T, Context.Char32Ty)) {
-      if (++Param == FnDecl->param_end())
-        Valid = true;
+    Diag(TpDecl->getTemplateParameters()->getSourceRange().getBegin(),
+         diag::err_literal_operator_template)
+        << TpDecl->getTemplateParameters()->getSourceRange();
+    return true;
+
+  } else if (FnDecl->param_size() == 1) {
+    const ParmVarDecl *Param = FnDecl->getParamDecl(0);
+
+    QualType ParamType = Param->getType().getUnqualifiedType();
+
+    // Only unsigned long long int, long double, and any character type are
+    // allowed as the only parameters.
+    if (Context.hasSameType(ParamType, Context.UnsignedLongLongTy) ||
+        Context.hasSameType(ParamType, Context.LongDoubleTy) ||
+        Context.hasSameType(ParamType, Context.CharTy) ||
+        Context.hasSameType(ParamType, Context.WideCharTy) ||
+        Context.hasSameType(ParamType, Context.Char16Ty) ||
+        Context.hasSameType(ParamType, Context.Char32Ty)) {
       goto FinishedParams;
+    } else if (const PointerType *Ptr = ParamType->getAs<PointerType>()) {
+      QualType InnerType = Ptr->getPointeeType();
+
+      if (Context.hasSameType(InnerType.getUnqualifiedType(), Context.CharTy) &&
+          InnerType.isConstQualified() && !InnerType.isVolatileQualified())
+        goto FinishedParams;
+
+      Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param)
+          << ParamType << "'const char *'" << Param->getSourceRange();
+
+    } else if (ParamType->isRealFloatingType()) {
+      Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param)
+          << ParamType << Context.LongDoubleTy << Param->getSourceRange();
+
+    } else if (ParamType->isIntegerType()) {
+      Diag(Param->getSourceRange().getBegin(), diag::err_literal_operator_param)
+          << ParamType << Context.UnsignedLongLongTy << Param->getSourceRange();
+
+    } else if (!ParamType->isBuiltinType()) {
+      Diag(Param->getSourceRange().getBegin(),
+           diag::err_literal_operator_defined_type)
+          << ParamType << Param->getSourceRange();
+
+    } else {
+      Diag(Param->getSourceRange().getBegin(),
+           diag::err_literal_operator_invalid_param)
+          << ParamType << Param->getSourceRange();
     }
 
-    // Otherwise it must be a pointer to const; let's strip those qualifiers.
-    const PointerType *PT = T->getAs<PointerType>();
-    if (!PT)
-      goto FinishedParams;
-    T = PT->getPointeeType();
-    if (!T.isConstQualified() || T.isVolatileQualified())
-      goto FinishedParams;
-    T = T.getUnqualifiedType();
+    return true;
 
-    // Move on to the second parameter;
-    ++Param;
+  } else if (FnDecl->param_size() == 2) {
+    FunctionDecl::param_iterator Param = FnDecl->param_begin();
 
-    // If there is no second parameter, the first must be a const char *
-    if (Param == FnDecl->param_end()) {
-      if (Context.hasSameType(T, Context.CharTy))
-        Valid = true;
-      goto FinishedParams;
+    // First, verify that the first parameter is correct.
+
+    QualType FirstParamType = (*Param)->getType().getUnqualifiedType();
+
+    // Two parameter function must have a pointer to const as a
+    // first parameter; let's strip those qualifiers.
+    const PointerType *PT = FirstParamType->getAs<PointerType>();
+
+    if (!PT) {
+      Diag((*Param)->getSourceRange().getBegin(),
+           diag::err_literal_operator_param)
+          << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+      return true;
+    }
+
+    QualType PointeeType = PT->getPointeeType();
+    // First parameter must be const
+    if (!PointeeType.isConstQualified() || PointeeType.isVolatileQualified()) {
+      Diag((*Param)->getSourceRange().getBegin(),
+           diag::err_literal_operator_param)
+          << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+      return true;
     }
 
-    // const char *, const wchar_t*, const char16_t*, and const char32_t*
+    QualType InnerType = PointeeType.getUnqualifiedType();
+    // Only const char *, const wchar_t*, const char16_t*, and const char32_t*
     // are allowed as the first parameter to a two-parameter function
-    if (!(Context.hasSameType(T, Context.CharTy) ||
-          Context.hasSameType(T, Context.WideCharTy) ||
-          Context.hasSameType(T, Context.Char16Ty) ||
-          Context.hasSameType(T, Context.Char32Ty)))
-      goto FinishedParams;
+    if (!(Context.hasSameType(InnerType, Context.CharTy) ||
+          Context.hasSameType(InnerType, Context.WideCharTy) ||
+          Context.hasSameType(InnerType, Context.Char16Ty) ||
+          Context.hasSameType(InnerType, Context.Char32Ty))) {
+      Diag((*Param)->getSourceRange().getBegin(),
+           diag::err_literal_operator_param)
+          << FirstParamType << "'const char *'" << (*Param)->getSourceRange();
+      return true;
+    }
 
-    // The second and final parameter must be an std::size_t
-    T = (*Param)->getType().getUnqualifiedType();
-    if (Context.hasSameType(T, Context.getSizeType()) &&
-        ++Param == FnDecl->param_end())
-      Valid = true;
-  }
+    // Move on to the second and final parameter.
+    ++Param;
 
-  // FIXME: This diagnostic is absolutely terrible.
-FinishedParams:
-  if (!Valid) {
-    Diag(FnDecl->getLocation(), diag::err_literal_operator_params)
-      << FnDecl->getDeclName();
+    // The second parameter must be a std::size_t.
+    QualType SecondParamType = (*Param)->getType().getUnqualifiedType();
+    if (Context.hasSameType(SecondParamType, Context.getSizeType()))
+      goto FinishedParams;
+    else {
+      Diag((*Param)->getSourceRange().getBegin(),
+           diag::err_literal_operator_param)
+          << SecondParamType << Context.getSizeType()
+          << (*Param)->getSourceRange();
+      return true;
+    }
+  } else {
+    Diag(FnDecl->getLocation(), diag::err_literal_operator_bad_param_count);
     return true;
   }
 
+FinishedParams:
   // A parameter-declaration-clause containing a default argument is not
   // equivalent to any of the permitted forms.
   for (auto Param : FnDecl->params()) {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -6883,9 +6883,18 @@
   "non-namespace scope '%0' cannot have a literal operator member">;
 def err_literal_operator_default_argument : Error<
   "literal operator cannot have a default argument">;
-// FIXME: This diagnostic sucks
-def err_literal_operator_params : Error<
-  "parameter declaration for literal operator %0 is not valid">;
+def err_literal_operator_bad_param_count : Error<
+  "non-template literal operator must have one or two parameters">;
+def err_literal_operator_invalid_param : Error<
+  "invalid literal operator parameter type %0">;
+def err_literal_operator_param : Error<
+  "invalid literal operator parameter type %0, did you mean %1?">;
+def err_literal_operator_template_with_params : Error<
+  "literal operator template cannot have any parameters">;
+def err_literal_operator_defined_type : Error<
+  "cannot define a literal operator on user-defined type %0">;
+def err_literal_operator_template : Error<
+  "invalid template parameters for literal operator">;
 def err_literal_operator_extern_c : Error<
   "literal operator must have C++ linkage">;
 def ext_string_literal_operator_template : ExtWarn<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to