sepavloff created this revision.
sepavloff added reviewers: rsmith, doug.gregor.
sepavloff added a subscriber: cfe-commits.
If a declaration references a function from global namespace by its
qualified name and if that function return type is a class or enum, there
is possible ambiguity. The declaration:
```
friend A::B::C();
```
may be considered as a declaration of a function `::C()` that returns
`A::B`, or a function `::B::C()` that returns `A`.
With this change when the compiler sees 'A::B' while parsing decl-spec, it
tries to find `B` within 'A'. If it finds, 'A::B' is treated as a part of
qualified name. If it doesn't and the current declaration declares a
function, '::B' is assumed to be a part of declarator. For non-function
declarations 'B' can be searched for in the global namespace to improve
diagnostics.
This changes fixes https://llvm.org/bugs/show_bug.cgi?id=28422.
https://reviews.llvm.org/D23684
Files:
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/Parser.cpp
lib/Parse/RAIIObjectsForParser.h
lib/Sema/SemaCXXScopeSpec.cpp
lib/Sema/TreeTransform.h
test/CXX/drs/dr1xx.cpp
test/CXX/drs/dr2xx.cpp
test/Parser/cxx-decl.cpp
test/SemaCXX/nested-name-spec2.cpp
test/SemaCXX/pr18284-crash-on-invalid.cpp
test/SemaCXX/typo-correction.cpp
Index: test/SemaCXX/typo-correction.cpp
===================================================================
--- test/SemaCXX/typo-correction.cpp
+++ test/SemaCXX/typo-correction.cpp
@@ -478,22 +478,17 @@
}
}
-namespace PR18213 { // expected-note {{'PR18213' declared here}}
+namespace PR18213 {
struct WrapperInfo {
int i;
};
template <typename T> struct Wrappable {
static WrapperInfo kWrapperInfo;
};
-// Note the space before "::PR18213" is intended and needed, as it highlights
-// the actual typo, which is the leading "::".
-// TODO: Suggest removing the "::" from "::PR18213" (the right correction)
-// instead of incorrectly suggesting dropping "PR18213::WrapperInfo::".
template <>
-PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 }; // expected-error {{no member named 'PR18213' in 'PR18213::WrapperInfo'; did you mean simply 'PR18213'?}} \
- // expected-error {{C++ requires a type specifier for all declarations}}
+PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 };
}
namespace PR18651 {
Index: test/SemaCXX/pr18284-crash-on-invalid.cpp
===================================================================
--- test/SemaCXX/pr18284-crash-on-invalid.cpp
+++ test/SemaCXX/pr18284-crash-on-invalid.cpp
@@ -5,7 +5,7 @@
class A { };
class C { A a; };
-A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}}
+A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}}
void f() {
new C;
@@ -16,7 +16,7 @@
class A { };
class C : public A { };
-A::RunTest() {} // expected-error {{C++ requires a type specifier for all declarations}}
+A::RunTest() {} // expected-error {{definition or redeclaration of 'RunTest' cannot name the global scope}}
void f() {
new C;
Index: test/SemaCXX/nested-name-spec2.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/nested-name-spec2.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
+enum E1 { ABCD };
+
+struct C1 {
+ struct C2 {};
+ enum E2 { XYZ };
+};
+
+E1 func_01();
+E1 ::func_01(); // expected-warning{{extra qualification on member}}
+
+C1 func_02();
+C1 ::func_02(); // expected-warning{{extra qualification on member}}
+
+C1::C2 func_03();
+C1::C2 ::func_03(); // expected-warning{{extra qualification on member}}
+
+C1::E2 func_04();
+C1::E2 ::func_04(); // expected-warning{{extra qualification on member}}
+
+class C3 {
+ friend E1 ::func_01();
+ friend C1 ::func_02();
+ friend C1::C2 ::func_03();
+ friend C1::E2 ::func_04();
+};
+
+
+namespace N1 {
+ class C3 {
+ friend ::E1 ::func_01();
+ friend ::C1 ::func_02();
+ friend ::C1::C2 ::func_03();
+ friend ::C1::E2 ::func_04();
+ };
+}
+
+
+namespace N2 {
+ enum E1 { ABCD };
+
+ struct C1 {
+ struct C2 {};
+ enum E2 { XYZ };
+ };
+
+ E1 func_01();
+ C1 func_02();
+ C1::C2 func_03();
+ C1::E2 func_04();
+}
+
+namespace N3 {
+ class C4 {
+ friend ::N2::E1 ::N2::func_01();
+ friend ::N2::C1 ::N2::func_02();
+ friend ::N2::C1::C2 ::N2::func_03();
+ friend ::N2::C1::E2 ::N2::func_04();
+ };
+}
Index: test/Parser/cxx-decl.cpp
===================================================================
--- test/Parser/cxx-decl.cpp
+++ test/Parser/cxx-decl.cpp
@@ -249,8 +249,11 @@
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}}
+ FooBar::FooBar() {} // expected-error {{unknown type name 'FooBar'; did you mean 'Foobar'?}}
+ // expected-error@-1{{definition or redeclaration of 'FooBar' cannot name the global scope}}
+ // expected-error@-2{{use of undeclared identifier 'FooBar'; did you mean 'Foobar'?}}
+ // expected-note@-3{{'FooBar' declared here}}
+ FooBar::~FooBar() {} // expected-error {{'FooBar' is not a class, namespace, or enumeration}}
}
namespace DuplicateFriend {
Index: test/CXX/drs/dr2xx.cpp
===================================================================
--- test/CXX/drs/dr2xx.cpp
+++ test/CXX/drs/dr2xx.cpp
@@ -1038,7 +1038,7 @@
struct B b; // expected-error {{refers to a typedef}}
struct C c; // expected-error {{refers to a typedef}}
- B::B() {} // expected-error {{requires a type specifier}}
+ B::B() {} // expected-error{{definition or redeclaration of 'B' cannot name the global scope}}
B::A() {} // ok
C::~C() {} // expected-error {{destructor cannot be declared using a typedef 'C' (aka 'const dr298::A') of the class name}}
Index: test/CXX/drs/dr1xx.cpp
===================================================================
--- test/CXX/drs/dr1xx.cpp
+++ test/CXX/drs/dr1xx.cpp
@@ -226,18 +226,14 @@
// dr124: dup 201
// dr125: yes
-struct dr125_A { struct dr125_B {}; }; // expected-note {{here}}
+struct dr125_A { struct dr125_B {}; };
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 {{did you mean the constructor name 'dr125_B'?}}
- // expected-error@-1 {{missing exception specification}}
-#if __cplusplus >= 201103L
- // expected-error@-3 {{follows constexpr declaration}} expected-note@-10 {{here}}
-#endif
+ friend dr125_A::dr125_B ::dr125_C();
};
}
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -3442,7 +3442,7 @@
Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(),
Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType);
if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false,
- SS, FirstQualifierInScope, false))
+ SS, FirstQualifierInScope, false) != Sema::NNSResult::Success)
return NestedNameSpecifierLoc();
}
break;
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp
+++ lib/Sema/SemaCXXScopeSpec.cpp
@@ -473,13 +473,13 @@
/// scope if it *knows* that the result is correct. It should not return in a
/// dependent context, for example. Nor will it extend \p SS with the scope
/// specifier.
-bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
- NestedNameSpecInfo &IdInfo,
- bool EnteringContext,
- CXXScopeSpec &SS,
- NamedDecl *ScopeLookupResult,
- bool ErrorRecoveryLookup,
- bool *IsCorrectedToColon) {
+Sema::NNSResult Sema::BuildCXXNestedNameSpecifier(Scope *S,
+ NestedNameSpecInfo &IdInfo,
+ bool EnteringContext,
+ CXXScopeSpec &SS,
+ NamedDecl *ScopeLookupResult,
+ bool ErrorRecoveryLookup,
+ bool *IsCorrectedToColon) {
LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
LookupNestedNameSpecifierName);
QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
@@ -513,7 +513,7 @@
// The declaration context must be complete.
if (!LookupCtx->isDependentContext() &&
RequireCompleteDeclContext(SS, LookupCtx))
- return true;
+ return NNSResult::Failure;
LookupQualifiedName(Found, LookupCtx);
@@ -551,7 +551,7 @@
}
if (Found.isAmbiguous())
- return true;
+ return NNSResult::Failure;
// If we performed lookup into a dependent context and did not find anything,
// that's fine: just build a dependent nested-name-specifier.
@@ -561,14 +561,14 @@
!cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
// Don't speculate if we're just trying to improve error recovery.
if (ErrorRecoveryLookup)
- return true;
+ return NNSResult::Failure;
// We were not able to compute the declaration context for a dependent
// base object type or prior nested-name-specifier, so this
// nested-name-specifier refers to an unknown specialization. Just build
// a dependent nested-name-specifier.
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
- return false;
+ return NNSResult::Success;
}
if (Found.empty() && !ErrorRecoveryLookup) {
@@ -591,15 +591,15 @@
<< FixItHint::CreateReplacement(IdInfo.CCLoc, ":");
if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
Diag(ND->getLocation(), diag::note_declared_at);
- return true;
+ return NNSResult::Failure;
}
// Replacement '::' -> ':' is not allowed, just issue respective error.
Diag(R.getNameLoc(), diag::err_expected_class_or_namespace)
<< IdInfo.Identifier << getLangOpts().CPlusPlus;
if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
Diag(ND->getLocation(), diag::note_entity_declared_at)
<< IdInfo.Identifier;
- return true;
+ return NNSResult::Failure;
}
}
@@ -641,10 +641,50 @@
Found.isSingleResult() ? Found.getRepresentativeDecl() : nullptr;
bool IsExtension = false;
bool AcceptSpec = isAcceptableNestedNameSpecifier(SD, &IsExtension);
- if (!AcceptSpec && IsExtension) {
+ if (!AcceptSpec && IsExtension)
AcceptSpec = true;
- Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
+ if (AcceptSpec && IdInfo.InDeclSpec && IdInfo.NextIdentifier &&
+ !IdInfo.ObjectType) {
+ // Need to resolve possible ambiguity in construct 'T::A' - whether 'T::A'
+ // is a part of nested-name-spec or '::A' is a part of declarator.
+
+ NamedDecl *FoundDecl = SD;
+ if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(FoundDecl)) {
+ QualType QT = TD->getUnderlyingType();
+ if (const TagType *TT = QT->getAs<TagType>())
+ if (TagDecl *TD = TT->getDecl())
+ FoundDecl = TD;
+ }
+
+ if (TagDecl *TD = dyn_cast<TagDecl>(FoundDecl)) {
+ // In the construct 'T::A' tokens 'T::' are part of nested-name-spec if A
+ // is a member of 'T'.
+ if (TagDecl *Def = TD->getDefinition()) {
+ LookupResult Member(*this, IdInfo.NextIdentifier,
+ IdInfo.NextIdentifierLoc, LookupMemberName);
+ LookupQualifiedName(Member, Def);
+ LookupResult::LookupResultKind Res = Member.getResultKind();
+ Member.clear();
+ if (Res == LookupResult::NotFound) {
+ if (IdInfo.FunctionExpected)
+ return NNSResult::Stop;
+ // Variable must be declare in its namespace, this declaration can be
+ // found by lookup.
+ LookupResult Global(*this, IdInfo.NextIdentifier,
+ IdInfo.NextIdentifierLoc, LookupOrdinaryName);
+ LookupQualifiedName(Global, Context.getTranslationUnitDecl());
+ LookupResult::LookupResultKind Res = Global.getResultKind();
+ Global.clear();
+ if (Res != LookupResult::NotFound)
+ return NNSResult::Stop;
+ // Otherwise consider 'A' as a typo of a nested-name-spec.
+ }
+ }
+ }
}
+ if (IsExtension)
+ Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
+
if (AcceptSpec) {
if (!ObjectType.isNull() && !ObjectTypeSearchedInScope &&
!getLangOpts().CPlusPlus11) {
@@ -674,7 +714,7 @@
Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
if (ErrorRecoveryLookup)
- return true;
+ return NNSResult::Failure;
Diag(IdInfo.IdentifierLoc,
diag::err_nested_name_member_ref_lookup_ambiguous)
@@ -694,19 +734,19 @@
// If we're just performing this lookup for error-recovery purposes,
// don't extend the nested-name-specifier. Just return now.
if (ErrorRecoveryLookup)
- return false;
+ return NNSResult::Success;
// The use of a nested name specifier may trigger deprecation warnings.
DiagnoseUseOfDecl(SD, IdInfo.CCLoc);
if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
SS.Extend(Context, Namespace, IdInfo.IdentifierLoc, IdInfo.CCLoc);
- return false;
+ return NNSResult::Success;
}
if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) {
SS.Extend(Context, Alias, IdInfo.IdentifierLoc, IdInfo.CCLoc);
- return false;
+ return NNSResult::Success;
}
QualType T =
@@ -750,13 +790,13 @@
SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T),
IdInfo.CCLoc);
- return false;
+ return NNSResult::Success;
}
// Otherwise, we have an error case. If we don't want diagnostics, just
// return an error now.
if (ErrorRecoveryLookup)
- return true;
+ return NNSResult::Failure;
// If we didn't find anything during our lookup, try again with
// ordinary name lookup, which can help us produce better error
@@ -793,7 +833,7 @@
<< IdInfo.Identifier << ContainingClass;
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
IdInfo.CCLoc);
- return false;
+ return NNSResult::Success;
}
}
}
@@ -816,17 +856,17 @@
Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use)
<< IdInfo.Identifier;
- return true;
+ return NNSResult::Failure;
}
-bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
- NestedNameSpecInfo &IdInfo,
- bool EnteringContext,
- CXXScopeSpec &SS,
- bool ErrorRecoveryLookup,
- bool *IsCorrectedToColon) {
+Sema::NNSResult Sema::ActOnCXXNestedNameSpecifier(Scope *S,
+ NestedNameSpecInfo &IdInfo,
+ bool EnteringContext,
+ CXXScopeSpec &SS,
+ bool ErrorRecoveryLookup,
+ bool *IsCorrectedToColon) {
if (SS.isInvalid())
- return true;
+ return NNSResult::Failure;
return BuildCXXNestedNameSpecifier(S, IdInfo,
EnteringContext, SS,
@@ -869,8 +909,8 @@
if (SS.isInvalid())
return false;
- return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
- /*ScopeLookupResult=*/nullptr, true);
+ return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
+ /*ScopeLookupResult=*/nullptr, true) == NNSResult::Success;
}
bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Index: lib/Parse/RAIIObjectsForParser.h
===================================================================
--- lib/Parse/RAIIObjectsForParser.h
+++ lib/Parse/RAIIObjectsForParser.h
@@ -88,7 +88,7 @@
/// variable's initializer, but not when parsing the body of a
/// class or function definition.
class ParsingDeclRAIIObject {
- Sema &Actions;
+ Parser &TheParser;
sema::DelayedDiagnosticPool DiagnosticPool;
Sema::ParsingDeclState State;
bool Popped;
@@ -99,22 +99,22 @@
public:
enum NoParent_t { NoParent };
ParsingDeclRAIIObject(Parser &P, NoParent_t _)
- : Actions(P.getActions()), DiagnosticPool(nullptr) {
+ : TheParser(P), DiagnosticPool(nullptr) {
push();
}
/// Creates a RAII object whose pool is optionally parented by another.
ParsingDeclRAIIObject(Parser &P,
const sema::DelayedDiagnosticPool *parentPool)
- : Actions(P.getActions()), DiagnosticPool(parentPool) {
+ : TheParser(P), DiagnosticPool(parentPool) {
push();
}
/// Creates a RAII object and, optionally, initialize its
/// diagnostics pool by stealing the diagnostics from another
/// RAII object (which is assumed to be the current top pool).
ParsingDeclRAIIObject(Parser &P, ParsingDeclRAIIObject *other)
- : Actions(P.getActions()),
+ : TheParser(P),
DiagnosticPool(other ? other->DiagnosticPool.getParent() : nullptr) {
if (other) {
DiagnosticPool.steal(other->DiagnosticPool);
@@ -127,6 +127,8 @@
abort();
}
+ Parser &getParser() const { return TheParser; }
+
sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() {
return DiagnosticPool;
}
@@ -159,13 +161,13 @@
private:
void push() {
- State = Actions.PushParsingDeclaration(DiagnosticPool);
+ State = TheParser.getActions().PushParsingDeclaration(DiagnosticPool);
Popped = false;
}
void pop(Decl *D) {
if (!Popped) {
- Actions.PopParsingDeclaration(State, D);
+ TheParser.getActions().PopParsingDeclaration(State, D);
Popped = true;
}
}
@@ -178,10 +180,17 @@
public:
ParsingDeclSpec(Parser &P)
: DeclSpec(P.getAttrFactory()),
- ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {}
+ ParsingRAII(P, ParsingDeclRAIIObject::NoParent) {
+ P.InDeclSpec = true;
+ }
ParsingDeclSpec(Parser &P, ParsingDeclRAIIObject *RAII)
: DeclSpec(P.getAttrFactory()),
- ParsingRAII(P, RAII) {}
+ ParsingRAII(P, RAII) {
+ P.InDeclSpec = true;
+ }
+ ~ParsingDeclSpec() {
+ leave();
+ }
const sema::DelayedDiagnosticPool &getDelayedDiagnosticPool() const {
return ParsingRAII.getDelayedDiagnosticPool();
@@ -191,6 +200,10 @@
ParsingRAII.complete(D);
}
+ void leave() {
+ ParsingRAII.getParser().InDeclSpec = false;
+ }
+
void abort() {
ParsingRAII.abort();
}
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -69,8 +69,8 @@
Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
: PP(pp), Actions(actions), Diags(PP.getDiagnostics()),
- GreaterThanIsOperator(true), ColonIsSacred(false),
- InMessageExpression(false), TemplateParameterDepth(0),
+ GreaterThanIsOperator(true), ColonIsSacred(false),
+ InMessageExpression(false), InDeclSpec(false), TemplateParameterDepth(0),
ParsingInObjCContainer(false) {
SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies;
Tok.startToken();
@@ -853,6 +853,34 @@
Tok.is(tok::kw_try); // X() try { ... }
}
+/// \brief Checks if the current token finishes decl-spec and next '::' starts a
+/// function declarator.
+///
+/// This function is used to assist in parsing constructs 'A::B' where 'A' and
+/// 'B' are identifiers. So it expects that the current token is 'A'.
+/// FIXME: we must support all kinds of nested-name-specifiers.
+///
+bool Parser::isFunctionDeclaratorInGlobalNamespace() {
+ assert(Tok.is(tok::identifier));
+ assert(NextToken().isOneOf(tok::coloncolon, tok::colon));
+ unsigned Displ = 2;
+ while (true) {
+ const Token CurTok = GetLookAheadToken(Displ);
+ if (CurTok.is(tok::identifier)) {
+ const Token &NextTok = GetLookAheadToken(Displ + 1);
+ if (NextTok.isOneOf(tok::coloncolon, tok::colon)) {
+ Displ += 2;
+ continue;
+ }
+ if (NextTok.is(tok::l_paren))
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+
/// ParseDeclarationOrFunctionDefinition - Parse either a function-definition or
/// a declaration. We can't tell which we have until we read up to the
/// compound-statement in function-definition. TemplateParams, if
@@ -875,6 +903,7 @@
AccessSpecifier AS) {
// Parse the common declaration-specifiers piece.
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level);
+ DS.leave();
// If we had a free-standing type definition with a missing semicolon, we
// may get this far before the problem becomes obvious.
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp
+++ lib/Parse/ParseExprCXX.cpp
@@ -429,6 +429,33 @@
Token Next = NextToken();
Sema::NestedNameSpecInfo IdInfo(&II, Tok.getLocation(), Next.getLocation(),
ObjectType);
+ if (Next.isOneOf(tok::coloncolon, tok::colon)) {
+ Token TokenAfterCC = GetLookAheadToken(2);
+ if (TokenAfterCC.is(tok::identifier)) {
+ // We have sequence of tokens: 'identifier' '::' 'identifier'. If decl-
+ // spec is parsed, these tokens may compose a qualified name or the
+ // decl-spec might finish before the double colon:
+ //
+ // friend A::B func();
+ // friend A ::B();
+ //
+ // Sema can distinguish between the two cases by looking 'B' in 'A'.
+ IdInfo.InDeclSpec = InDeclSpec;
+ IdInfo.NextIdentifier = TokenAfterCC.getIdentifierInfo();
+ IdInfo.NextIdentifierLoc = TokenAfterCC.getLocation();
+
+ // If 'B' is not a member of 'A' in 'A::B', it may mean:
+ // - decl-spec is finished, '::B' starts a declarator,
+ // - 'B' is a typo.
+ // To distinguish between these cases Sema could search global scope for
+ // 'B', but it doesn't help if this declaration declares a function, as
+ // it may be the first declaration. If this declaration declares a
+ // variable, it must be preceded by a declaration in the global
+ // namespace and can be found by lookup.
+ IdInfo.FunctionExpected = InDeclSpec &&
+ isFunctionDeclaratorInGlobalNamespace();
+ }
+ }
// If we get foo:bar, this is almost certainly a typo for foo::bar. Recover
// and emit a fixit hint for it.
@@ -458,18 +485,18 @@
}
if (Next.is(tok::coloncolon)) {
- if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) &&
+ Token TokenAfterCC = GetLookAheadToken(2);
+ if (CheckForDestructor && TokenAfterCC.is(tok::tilde) &&
!Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) {
*MayBePseudoDestructor = true;
return false;
}
if (ColonIsSacred) {
- const Token &Next2 = GetLookAheadToken(2);
- if (Next2.is(tok::kw_private) || Next2.is(tok::kw_protected) ||
- Next2.is(tok::kw_public) || Next2.is(tok::kw_virtual)) {
- Diag(Next2, diag::err_unexpected_token_in_nested_name_spec)
- << Next2.getName()
+ if (TokenAfterCC.isOneOf(tok::kw_private, tok::kw_protected,
+ tok::kw_public, tok::kw_virtual)) {
+ Diag(TokenAfterCC, diag::err_unexpected_token_in_nested_name_spec)
+ << TokenAfterCC.getName()
<< FixItHint::CreateReplacement(Next.getLocation(), ":");
Token ColonColon;
PP.Lex(ColonColon);
@@ -484,33 +511,44 @@
// We have an identifier followed by a '::'. Lookup this name
// as the name in a nested-name-specifier.
- Token Identifier = Tok;
- SourceLocation IdLoc = ConsumeToken();
- assert(Tok.isOneOf(tok::coloncolon, tok::colon) &&
- "NextToken() not working properly!");
- Token ColonColon = Tok;
- SourceLocation CCLoc = ConsumeToken();
- CheckForLParenAfterColonColon();
+ if (TokenAfterCC.is(tok::l_paren)) {
+ ConsumeToken(); // identifier
+ ConsumeToken(); // ::
+ CheckForLParenAfterColonColon();
+ } else {
+ bool IsCorrectedToColon = false;
+ bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr;
+ Sema::NNSResult NSSRes = Actions.ActOnCXXNestedNameSpecifier(
+ getCurScope(), IdInfo, EnteringContext, SS, false, CorrectionFlagPtr);
+ if (NSSRes == Sema::NNSResult::Stop) {
+ // Current identifier followed by '::' is not a part of
+ // nested-name-spec. Stop parsing it.
+ return false;
+ }
- bool IsCorrectedToColon = false;
- bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr;
- if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), IdInfo,
- EnteringContext, SS,
- false, CorrectionFlagPtr)) {
- // Identifier is not recognized as a nested name, but we can have
- // mistyped '::' instead of ':'.
- if (CorrectionFlagPtr && IsCorrectedToColon) {
- ColonColon.setKind(tok::colon);
- PP.EnterToken(Tok);
- PP.EnterToken(ColonColon);
- Tok = Identifier;
- break;
+ Token Identifier = Tok;
+ SourceLocation IdLoc = ConsumeToken();
+ assert(Tok.isOneOf(tok::coloncolon, tok::colon) &&
+ "NextToken() not working properly!");
+ Token ColonColon = Tok;
+ SourceLocation CCLoc = ConsumeToken();
+
+ if (NSSRes == Sema::NNSResult::Failure) {
+ // Identifier is not recognized as a nested name, but we can have
+ // mistyped '::' instead of ':'.
+ if (CorrectionFlagPtr && IsCorrectedToColon) {
+ ColonColon.setKind(tok::colon);
+ PP.EnterToken(Tok);
+ PP.EnterToken(ColonColon);
+ Tok = Identifier;
+ break;
+ }
+ SS.SetInvalid(SourceRange(IdLoc, CCLoc));
}
- SS.SetInvalid(SourceRange(IdLoc, CCLoc));
+ HasScopeSpecifier = true;
+ continue;
}
- HasScopeSpecifier = true;
- continue;
}
CheckForTemplateAndDigraph(Next, ObjectType, EnteringContext, II, SS);
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -517,6 +517,7 @@
// Parse nested-name-specifier.
IdentifierInfo *LastII = nullptr;
+ InDeclSpec = false;
ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false,
/*MayBePseudoDtor=*/nullptr,
/*IsTypename=*/false,
@@ -2405,6 +2406,7 @@
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC_class,
&CommonLateParsedAttrs);
+ DS.leave();
// Turn off colon protection that was set for declspec.
X.restore();
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -1548,6 +1548,7 @@
DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext);
+ DS.leave();
// If we had a free-standing type definition with a missing semicolon, we
// may get this far before the problem becomes obvious.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4964,7 +4964,8 @@
bool *CanCorrect = nullptr);
NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
- /// \brief Keeps information about an identifier in a nested-name-spec.
+ /// \brief Keeps information about identifier that is examined if it is a part
+ /// of a nested-name-spec.
///
struct NestedNameSpecInfo {
/// \brief The type of the object, if we're parsing nested-name-specifier in
@@ -4980,30 +4981,56 @@
/// \brief The location of the '::'.
SourceLocation CCLoc;
- /// \brief Creates info object for the most typical case.
+ /// \brief If not null, the identifier following the '::'.
+ IdentifierInfo *NextIdentifier;
+
+ /// \brief The location of the identifier following '::'.
+ SourceLocation NextIdentifierLoc;
+
+ /// \brief True if the identifier was found during parsing of decl-spec.
+ bool InDeclSpec;
+
+ /// \brief True if the declaration seems to be a function declaration.
+ bool FunctionExpected;
+
NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc,
SourceLocation ColonColonLoc, ParsedType ObjectType = ParsedType())
: ObjectType(ObjectType), Identifier(II), IdentifierLoc(IdLoc),
- CCLoc(ColonColonLoc) {
+ CCLoc(ColonColonLoc), NextIdentifier(nullptr),
+ InDeclSpec(false), FunctionExpected(false) {
}
NestedNameSpecInfo(IdentifierInfo *II, SourceLocation IdLoc,
SourceLocation ColonColonLoc, QualType ObjectType)
: ObjectType(ParsedType::make(ObjectType)), Identifier(II),
- IdentifierLoc(IdLoc), CCLoc(ColonColonLoc) {
+ IdentifierLoc(IdLoc), CCLoc(ColonColonLoc), NextIdentifier(nullptr),
+ InDeclSpec(false), FunctionExpected(false) {
}
};
bool isNonTypeNestedNameSpecifier(Scope *S, CXXScopeSpec &SS,
NestedNameSpecInfo &IdInfo);
- bool BuildCXXNestedNameSpecifier(Scope *S,
- NestedNameSpecInfo &IdInfo,
- bool EnteringContext,
- CXXScopeSpec &SS,
- NamedDecl *ScopeLookupResult,
- bool ErrorRecoveryLookup,
- bool *IsCorrectedToColon = nullptr);
+ /// \brief Enumerates possible results that may be observed when adding an
+ /// identifier to nested-name-spec.
+ enum NNSResult {
+ /// \brief The identifier was successfully added to the nested-name-spec.
+ Success,
+
+ /// \brief The provided identifier makes the nested-name-spec invalid.
+ Failure,
+
+ // \brief The identifier is not a part of the nested-name-spec.
+ Stop
+ };
+
+ NNSResult BuildCXXNestedNameSpecifier(Scope *S,
+ NestedNameSpecInfo &IdInfo,
+ bool EnteringContext,
+ CXXScopeSpec &SS,
+ NamedDecl *ScopeLookupResult,
+ bool ErrorRecoveryLookup,
+ bool *IsCorrectedToColon = nullptr);
/// \brief The parser has parsed a nested-name-specifier 'identifier::'.
///
@@ -5028,12 +5055,12 @@
/// if the identifier is treated as if it was followed by ':', not '::'.
///
/// \returns true if an error occurred, false otherwise.
- bool ActOnCXXNestedNameSpecifier(Scope *S,
- NestedNameSpecInfo &IdInfo,
- bool EnteringContext,
- CXXScopeSpec &SS,
- bool ErrorRecoveryLookup = false,
- bool *IsCorrectedToColon = nullptr);
+ NNSResult ActOnCXXNestedNameSpecifier(Scope *S,
+ NestedNameSpecInfo &IdInfo,
+ bool EnteringContext,
+ CXXScopeSpec &SS,
+ bool ErrorRecoveryLookup = false,
+ bool *IsCorrectedToColon = nullptr);
ExprResult ActOnDecltypeExpression(Expr *E);
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -61,6 +61,7 @@
friend class ObjCDeclContextSwitch;
friend class ParenBraceBracketBalancer;
friend class BalancedDelimiterTracker;
+ friend class ParsingDeclSpec;
Preprocessor &PP;
@@ -198,6 +199,9 @@
/// should not be set directly.
bool InMessageExpression;
+ /// \brief When true, parser processes decl-spec, or tries to recognize it.
+ bool InDeclSpec;
+
/// The "depth" of the template parameters currently being parsed.
unsigned TemplateParameterDepth;
@@ -1257,6 +1261,7 @@
ParsingDeclSpec *DS = nullptr);
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
+ bool isFunctionDeclaratorInGlobalNamespace();
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
ParsedAttributesWithRange &attrs,
ParsingDeclSpec *DS = nullptr,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits