r192644 - PR17567: Improve diagnostic for a mistyped constructor name. If we see something
Richard Smith
richard-llvm at metafoo.co.uk
Mon Oct 14 17:00:26 PDT 2013
Author: rsmith
Date: Mon Oct 14 19:00:26 2013
New Revision: 192644
URL: http://llvm.org/viewvc/llvm-project?rev=192644&view=rev
Log:
PR17567: Improve diagnostic for a mistyped constructor name. If we see something
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.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Parse/ParseDecl.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/test/CXX/drs/dr1xx.cpp
cfe/trunk/test/Parser/cxx-decl.cpp
cfe/trunk/test/Parser/cxx0x-in-cxx98.cpp
cfe/trunk/test/SemaCXX/nested-name-spec.cpp
cfe/trunk/test/SemaTemplate/alias-nested-nontag.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Mon Oct 14 19:00:26 2013
@@ -482,6 +482,8 @@ def err_expected_rbrace_or_comma : Error
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<
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Oct 14 19:00:26 2013
@@ -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,
Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Mon Oct 14 19:00:26 2013
@@ -2095,6 +2095,8 @@ bool Parser::ParseImplicitInt(DeclSpec &
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 &
// 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 &
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:
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon Oct 14 19:00:26 2013
@@ -1227,6 +1227,32 @@ bool Sema::isCurrentClassName(const Iden
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,
Modified: cfe/trunk/test/CXX/drs/dr1xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr1xx.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr1xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr1xx.cpp Mon Oct 14 19:00:26 2013
@@ -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 at -1 {{missing exception specification}}
+#if __cplusplus >= 201103L
+ // expected-error at -3 {{follows constexpr declaration}} expected-note at -10 {{here}}
+#endif
};
}
Modified: cfe/trunk/test/Parser/cxx-decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-decl.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx-decl.cpp (original)
+++ cfe/trunk/test/Parser/cxx-decl.cpp Mon Oct 14 19:00:26 2013
@@ -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}} \
Modified: cfe/trunk/test/Parser/cxx0x-in-cxx98.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx0x-in-cxx98.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx0x-in-cxx98.cpp (original)
+++ cfe/trunk/test/Parser/cxx0x-in-cxx98.cpp Mon Oct 14 19:00:26 2013
@@ -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 at -1 {{'auto' type specifier is a C++11 extension}}
Modified: cfe/trunk/test/SemaCXX/nested-name-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nested-name-spec.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/nested-name-spec.cpp (original)
+++ cfe/trunk/test/SemaCXX/nested-name-spec.cpp Mon Oct 14 19:00:26 2013
@@ -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;
Modified: cfe/trunk/test/SemaTemplate/alias-nested-nontag.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/alias-nested-nontag.cpp?rev=192644&r1=192643&r2=192644&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/alias-nested-nontag.cpp (original)
+++ cfe/trunk/test/SemaTemplate/alias-nested-nontag.cpp Mon Oct 14 19:00:26 2013
@@ -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}}
More information about the cfe-commits
mailing list