]> granicus.if.org Git - clang/commitdiff
PR17567: Improve diagnostic for a mistyped constructor name. If we see something
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 15 Oct 2013 00:00:26 +0000 (00:00 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 15 Oct 2013 00:00:26 +0000 (00:00 +0000)
that looks like a function declaration, except that it's missing a return type,
try typo-correcting it to the relevant constructor name.

In passing, fix a bug where the missing-type-specifier recovery codepath would
drop a preceding scope specifier on the floor, leading to follow-on diagnostics
and incorrect recovery for the auto-in-c++98 hack.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192644 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticParseKinds.td
include/clang/Sema/Sema.h
lib/Parse/ParseDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/CXX/drs/dr1xx.cpp
test/Parser/cxx-decl.cpp
test/Parser/cxx0x-in-cxx98.cpp
test/SemaCXX/nested-name-spec.cpp
test/SemaTemplate/alias-nested-nontag.cpp

index 61b871d37684b482924114dfef0991fce77fe047..b470bb4e78e5ac06dbcd3a2c6cb57e12c5a0fc79 100644 (file)
@@ -482,6 +482,8 @@ def err_expected_rbrace_or_comma : Error<"expected '}' or ','">;
 def err_expected_rsquare_or_comma : Error<"expected ']' or ','">;
 def err_using_namespace_in_class : Error<
   "'using namespace' is not allowed in classes">;
+def err_constructor_bad_name : Error<
+  "missing return type for function %0; did you mean the constructor name %1?">;
 def err_destructor_tilde_identifier : Error<
   "expected a class name after '~' to name a destructor">;
 def err_destructor_template_id : Error<
index a70e609aedaec297f091335ccd5cb37163ea7d8e..f5a93d442ca952466b4dd2c39cdda3b976c025ad 100644 (file)
@@ -4606,6 +4606,7 @@ public:
   //
   bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
                           const CXXScopeSpec *SS = 0);
+  bool isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS);
 
   bool ActOnAccessSpecifier(AccessSpecifier Access,
                             SourceLocation ASLoc,
index 31349ce9946105639537892f3a113a53596e0da9..0f480c133536089cc1bc5a9d05f4dee706e00215 100644 (file)
@@ -2095,6 +2095,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
       DS.getStorageClassSpec() == DeclSpec::SCS_auto) {
     // Don't require a type specifier if we have the 'auto' storage class
     // specifier in C++98 -- we'll promote it to a type specifier.
+    if (SS)
+      AnnotateScopeToken(*SS, /*IsNewAnnotation*/false);
     return false;
   }
 
@@ -2156,16 +2158,6 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
     // Look ahead to the next token to try to figure out what this declaration
     // was supposed to be.
     switch (NextToken().getKind()) {
-    case tok::comma:
-    case tok::equal:
-    case tok::kw_asm:
-    case tok::l_brace:
-    case tok::l_square:
-    case tok::semi:
-      // This looks like a variable declaration. The type is probably missing.
-      // We're done parsing decl-specifiers.
-      return false;
-
     case tok::l_paren: {
       // static x(4); // 'x' is not a type
       // x(int n);    // 'x' is not a type
@@ -2178,12 +2170,37 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS,
       ConsumeToken();
       TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false);
       PA.Revert();
-      if (TPR == TPResult::False())
-        return false;
-      // The identifier is followed by a parenthesized declarator.
-      // It's supposed to be a type.
-      break;
+
+      if (TPR != TPResult::False()) {
+        // The identifier is followed by a parenthesized declarator.
+        // It's supposed to be a type.
+        break;
+      }
+
+      // If we're in a context where we could be declaring a constructor,
+      // check whether this is a constructor declaration with a bogus name.
+      if (DSC == DSC_class || (DSC == DSC_top_level && SS)) {
+        IdentifierInfo *II = Tok.getIdentifierInfo();
+        if (Actions.isCurrentClassNameTypo(II, SS)) {
+          Diag(Loc, diag::err_constructor_bad_name)
+            << Tok.getIdentifierInfo() << II
+            << FixItHint::CreateReplacement(Tok.getLocation(), II->getName());
+          Tok.setIdentifierInfo(II);
+        }
+      }
+      // Fall through.
     }
+    case tok::comma:
+    case tok::equal:
+    case tok::kw_asm:
+    case tok::l_brace:
+    case tok::l_square:
+    case tok::semi:
+      // This looks like a variable or function declaration. The type is
+      // probably missing. We're done parsing decl-specifiers.
+      if (SS)
+        AnnotateScopeToken(*SS, /*IsNewAnnotation*/false);
+      return false;
 
     default:
       // This is probably supposed to be a type. This includes cases like:
index 6ec344d1e3c08ea3d313d9c245a01db47942bdd3..655c9e2c3d0d351d41b395c0edb266f241ac6bb7 100644 (file)
@@ -1227,6 +1227,32 @@ bool Sema::isCurrentClassName(const IdentifierInfo &II, Scope *,
   return false;
 }
 
+/// \brief Determine whether the identifier II is a typo for the name of
+/// the class type currently being defined. If so, update it to the identifier
+/// that should have been used.
+bool Sema::isCurrentClassNameTypo(IdentifierInfo *&II, const CXXScopeSpec *SS) {
+  assert(getLangOpts().CPlusPlus && "No class names in C!");
+
+  if (!getLangOpts().SpellChecking)
+    return false;
+
+  CXXRecordDecl *CurDecl;
+  if (SS && SS->isSet() && !SS->isInvalid()) {
+    DeclContext *DC = computeDeclContext(*SS, true);
+    CurDecl = dyn_cast_or_null<CXXRecordDecl>(DC);
+  } else
+    CurDecl = dyn_cast_or_null<CXXRecordDecl>(CurContext);
+
+  if (CurDecl && CurDecl->getIdentifier() && II != CurDecl->getIdentifier() &&
+      3 * II->getName().edit_distance(CurDecl->getIdentifier()->getName())
+          < II->getLength()) {
+    II = CurDecl->getIdentifier();
+    return true;
+  }
+
+  return false;
+}
+
 /// \brief Determine whether the given class is a base class of the given
 /// class, including looking at dependent bases.
 static bool findCircularInheritance(const CXXRecordDecl *Class,
index 88ae6ba0e446b83bd31921c818255630554afdd9..c67c51aa720f5a6fc0a806eadbd1ea713d162bca 100644 (file)
@@ -223,14 +223,18 @@ namespace dr122 { // dr122: yes
 // dr124: dup 201
 
 // dr125: yes
-struct dr125_A { struct dr125_B {}; };
+struct dr125_A { struct dr125_B {}; }; // expected-note {{here}}
 dr125_A::dr125_B dr125_C();
 namespace dr125_B { dr125_A dr125_C(); }
 namespace dr125 {
   struct X {
     friend dr125_A::dr125_B (::dr125_C)(); // ok
     friend dr125_A (::dr125_B::dr125_C)(); // ok
-    friend dr125_A::dr125_B::dr125_C(); // expected-error {{requires a type specifier}}
+    friend dr125_A::dr125_B::dr125_C(); // expected-error {{did you mean the constructor name 'dr125_B'?}}
+    // expected-warning@-1 {{missing exception specification}}
+#if __cplusplus >= 201103L
+    // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}}
+#endif
   };
 }
 
index ea20354bf9a75bcda922076f94013660f4d542a6..06272504bc53e4b815226cace82e962699fa6cf3 100644 (file)
@@ -223,6 +223,15 @@ void foo() {
 }
 }
 
+namespace PR17567 {
+  struct Foobar { // expected-note 2{{declared here}}
+    FooBar(); // expected-error {{missing return type for function 'FooBar'; did you mean the constructor name 'Foobar'?}}
+    ~FooBar(); // expected-error {{expected the class name after '~' to name a destructor}}
+  };
+  FooBar::FooBar() {} // expected-error {{undeclared}} expected-error {{missing return type}}
+  FooBar::~FooBar() {} // expected-error {{undeclared}} expected-error {{expected the class name}}
+}
+
 // PR8380
 extern ""      // expected-error {{unknown linkage language}}
 test6a { ;// expected-error {{C++ requires a type specifier for all declarations}} \
index b4bda89d2781cf222d7f34369f8953c8ada21197..724993811631b2a68101099f997c8142ed73c212 100644 (file)
@@ -21,3 +21,10 @@ void NewBracedInitList() {
   // A warning on this would be sufficient once we can handle it correctly.
   new int {}; // expected-error {{}}
 }
+
+struct Auto {
+  static int n;
+};
+auto Auto::n = 0; // expected-warning {{'auto' type specifier is a C++11 extension}}
+auto Auto::m = 0; // expected-error {{no member named 'm' in 'Auto'}}
+                  // expected-warning@-1 {{'auto' type specifier is a C++11 extension}}
index 855af93c2387f8460a7748e9059dad378b4dea02..df4f1b269d70ab7e3f77b464536ae90f84ab266f 100644 (file)
@@ -167,9 +167,7 @@ void N::f() { } // okay
 struct Y;  // expected-note{{forward declaration of 'Y'}}
 Y::foo y; // expected-error{{incomplete type 'Y' named in nested name specifier}}
 
-X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}} \
-      // expected-error{{C++ requires a type specifier for all declarations}} \
-      // expected-error{{only constructors take base initializers}}
+X::X() : a(5) { } // expected-error{{use of undeclared identifier 'X'}}
 
 struct foo_S {
   static bool value;
index 4b5226b26ebbde5b5f4a1f566f456dbb23601b94..2d0f0874e5e74403ac9623f7752fce7bee68b07d 100644 (file)
@@ -2,5 +2,4 @@
 
 template<typename T> using Id = T; // expected-note {{type alias template 'Id' declared here}}
 struct U { static Id<int> V; };
-Id<int> ::U::V; // expected-error {{type 'Id<int>' (aka 'int') cannot be used prior to '::' because it has no members}} \
-                   expected-error {{C++ requires a type specifier}}
+Id<int> ::U::V; // expected-error {{type 'Id<int>' (aka 'int') cannot be used prior to '::' because it has no members}}