forked from OSchip/llvm-project
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. llvm-svn: 192644
This commit is contained in:
parent
b0cba4ce75
commit
fb8b7b9a1c
|
@ -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<
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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}} \
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
|
|
Loading…
Reference in New Issue