Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1228,6 +1228,8 @@ def err_nested_name_spec_is_not_class : Error< "%0 cannot appear before '::' because it is not a class" "%select{ or namespace|, namespace, or scoped enumeration}1; did you mean ':'?">; +def err_nested_name_spec_is_enum : Error< + "unscoped enumeration name cannot be specified as a nested name specifier">; // C++ class members def err_storageclass_invalid_for_member : Error< @@ -4043,7 +4045,10 @@ "forward references to 'enum' types are a Microsoft extension">, InGroup; def ext_forward_ref_enum_def : Extension< "redeclaration of already-defined enum %0 is a GNU extension">, InGroup; - +def ext_ms_enum_in_nested_name_spec : Extension< + "using 'enum' types in nested name specifier is a Microsoft extension">, + InGroup; + def err_redefinition_of_enumerator : Error<"redefinition of enumerator %0">; def err_duplicate_member : Error<"duplicate member %0">; def err_misplaced_ivar : Error< @@ -4062,7 +4067,8 @@ def warn_flag_enum_constant_out_of_range : Warning< "enumeration value %0 is out of range of flags in enumeration type %1">, InGroup; - +def err_unknown_enumerator : Error<"%0 is not an enumerator of %1">; + def warn_illegal_constant_array_size : Extension< "size of static array must be an integer constant expression">; def err_vm_decl_in_file_scope : Error< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4635,6 +4635,22 @@ IdentifierInfo &II, ParsedType ObjectType); + /// \brief Collects information that may be used to recover erroneous nested + /// name specifier. + struct NameSpecRecoveryInfo { + bool CorrectToColon; //> Suggestions to replace '::' -> ':' are allowed. + IdentifierInfo *NextName; //> If set, it follows '::' after current name. + SourceLocation NextIdLoc; //> Location of the next identifier. + bool IsLast; //> True if current name is the last in nested-name-spec. + + bool CorrectionToColonRequested; //> Replacement '::' -> ':' recovers error. + bool IgnoreName; //> Ignore current name. + + explicit NameSpecRecoveryInfo(bool CorrectToColon) + : CorrectToColon(CorrectToColon), NextName(nullptr), IsLast(false), + CorrectionToColonRequested(false), IgnoreName(false) { } + }; + bool BuildCXXNestedNameSpecifier(Scope *S, IdentifierInfo &Identifier, SourceLocation IdentifierLoc, @@ -4644,7 +4660,7 @@ CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, - bool *IsCorrectedToColon = nullptr); + NameSpecRecoveryInfo *RecovInfo = nullptr); /// \brief The parser has parsed a nested-name-specifier 'identifier::'. /// @@ -4670,9 +4686,8 @@ /// \param ErrorRecoveryLookup If true, then this method is called to improve /// error recovery. In this case do not emit error message. /// - /// \param IsCorrectedToColon If not null, suggestions to replace '::' -> ':' - /// are allowed. The bool value pointed by this parameter is set to 'true' - /// if the identifier is treated as if it was followed by ':', not '::'. + /// \param RecovInfo If not null, points to a structure that contains info + /// for error recovery. /// /// \returns true if an error occurred, false otherwise. bool ActOnCXXNestedNameSpecifier(Scope *S, @@ -4683,7 +4698,7 @@ bool EnteringContext, CXXScopeSpec &SS, bool ErrorRecoveryLookup = false, - bool *IsCorrectedToColon = nullptr); + NameSpecRecoveryInfo *RecovInfo = nullptr); ExprResult ActOnDecltypeExpression(Expr *E); Index: lib/Parse/ParseExprCXX.cpp =================================================================== --- lib/Parse/ParseExprCXX.cpp +++ lib/Parse/ParseExprCXX.cpp @@ -491,20 +491,29 @@ CheckForLParenAfterColonColon(); - bool IsCorrectedToColon = false; - bool *CorrectionFlagPtr = ColonIsSacred ? &IsCorrectedToColon : nullptr; + Sema::NameSpecRecoveryInfo RecovInfo(ColonIsSacred); + // To help error recovery, determine, if the current name is the final + // nested name component. + if (Tok.is(tok::identifier)) { + RecovInfo.NextName = Tok.getIdentifierInfo(); + RecovInfo.NextIdLoc = Tok.getLocation(); + RecovInfo.IsLast = NextToken().isNot(tok::coloncolon); + } if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), II, IdLoc, CCLoc, ObjectType, EnteringContext, SS, - false, CorrectionFlagPtr)) { + false, &RecovInfo)) { // Identifier is not recognized as a nested name, but we can have // mistyped '::' instead of ':'. - if (CorrectionFlagPtr && IsCorrectedToColon) { + if (RecovInfo.CorrectToColon && RecovInfo.CorrectionToColonRequested) { ColonColon.setKind(tok::colon); PP.EnterToken(Tok); PP.EnterToken(ColonColon); Tok = Identifier; break; } + if (RecovInfo.IgnoreName) + // Error must be already reported. + break; SS.SetInvalid(SourceRange(IdLoc, CCLoc)); } HasScopeSpecifier = true; Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -424,10 +424,8 @@ /// definition time. /// \param ErrorRecoveryLookup Specifies if the method is called to improve /// error recovery and what kind of recovery is performed. -/// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':' -/// are allowed. The bool value pointed by this parameter is set to -/// 'true' if the identifier is treated as if it was followed by ':', -/// not '::'. +/// \param RecovInfo If not null, points to a structure that contains info +/// for error recovery. /// /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in /// that it contains an extra parameter \p ScopeLookupResult, which provides @@ -449,15 +447,13 @@ CXXScopeSpec &SS, NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, - bool *IsCorrectedToColon) { + NameSpecRecoveryInfo *RecovInfo) { LookupResult Found(*this, &Identifier, IdentifierLoc, LookupNestedNameSpecifierName); // Determine where to perform name lookup DeclContext *LookupCtx = nullptr; bool isDependent = false; - if (IsCorrectedToColon) - *IsCorrectedToColon = false; if (!ObjectType.isNull()) { // This nested-name-specifier occurs in a member access expression, e.g., // x->B::f, and we are looking into the type of the object. @@ -550,8 +546,8 @@ if (!R.empty()) { // The identifier is found in ordinary lookup. If correction to colon is // allowed, suggest replacement to ':'. - if (IsCorrectedToColon) { - *IsCorrectedToColon = true; + if (RecovInfo && RecovInfo->CorrectToColon) { + RecovInfo->CorrectionToColonRequested = true; Diag(CCLoc, diag::err_nested_name_spec_is_not_class) << &Identifier << getLangOpts().CPlusPlus << FixItHint::CreateReplacement(CCLoc, ":"); @@ -751,10 +747,48 @@ } if (!Found.empty()) { - if (TypeDecl *TD = Found.getAsSingle()) - Diag(IdentifierLoc, diag::err_expected_class_or_namespace) - << QualType(TD->getTypeForDecl(), 0) << getLangOpts().CPlusPlus; - else { + if (TypeDecl *TD = Found.getAsSingle()) { + // The current identifier does not represent an entity that may be used as + // a nested name spec, it nevertheless resolves to a declaration. If the + // declaration is an enum, probably it is used as a scope for its + // enumerator. In Microsoft mode this is allowed usage. + bool DiagnosticNotIssued = true; + if (EnumDecl *ED = dyn_cast(TD)) { + assert(!ED->isScoped() && "Scoped enum must be already handled."); + if (getLangOpts().MicrosoftExt) + Diag(IdentifierLoc, diag::ext_ms_enum_in_nested_name_spec); + if (RecovInfo) { + if (RecovInfo->NextName && + (getLangOpts().MicrosoftExt || RecovInfo->IsLast)) { + IdentifierInfo *NextId = RecovInfo->NextName; + for (auto Enumerator : ED->enumerators()) { + if (Enumerator->getName().equals(NextId->getName())) { + // We can recover parsing by throwing away enumeration name. + if (!getLangOpts().MicrosoftExt) { + Diag(IdentifierLoc, diag::err_nested_name_spec_is_enum) << + FixItHint::CreateRemoval(SourceRange(IdentifierLoc, CCLoc)); + } + DiagnosticNotIssued = false; + break; + } + } + if (DiagnosticNotIssued && getLangOpts().MicrosoftExt) { + // The current identifier represents enumeration, in Microsoft + // mode it must be followed by an enumerator of the respective + // enumeration. + Diag(RecovInfo->NextIdLoc, diag::err_unknown_enumerator) + << NextId << ED; + DiagnosticNotIssued = false; + } + } + } + if (getLangOpts().MicrosoftExt && DiagnosticNotIssued) + return false; + } + if (DiagnosticNotIssued) + Diag(IdentifierLoc, diag::err_expected_class_or_namespace) + << QualType(TD->getTypeForDecl(), 0) << getLangOpts().CPlusPlus; + } else { Diag(IdentifierLoc, diag::err_expected_class_or_namespace) << &Identifier << getLangOpts().CPlusPlus; if (NamedDecl *ND = Found.getAsSingle()) @@ -777,7 +811,7 @@ bool EnteringContext, CXXScopeSpec &SS, bool ErrorRecoveryLookup, - bool *IsCorrectedToColon) { + NameSpecRecoveryInfo *RecovInfo) { if (SS.isInvalid()) return true; @@ -785,7 +819,7 @@ GetTypeFromParser(ObjectType), EnteringContext, SS, /*ScopeLookupResult=*/nullptr, false, - IsCorrectedToColon); + RecovInfo); } bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS, Index: test/SemaCXX/ms-enum.cpp =================================================================== --- /dev/null +++ test/SemaCXX/ms-enum.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify -fms-extensions + +namespace ns { + enum E1 { + V1 + }; +} + +int x1 = ns::E1::V1; +int x2 = ns::E1::V1::vvv; // expected -error{{'V1' is not a class, namespace, or scoped enumeration}} +int x3 = ns::E1::X; // expected-error{{'X' is not an enumerator of 'E1'}} + +enum E2 { + V2 +}; + +int x4 = E2::V2; +int x5 = E2::X2; // expected-error{{'X2' is not an enumerator of 'E2'}} Index: test/SemaCXX/nested-name-spec.cpp =================================================================== --- test/SemaCXX/nested-name-spec.cpp +++ test/SemaCXX/nested-name-spec.cpp @@ -116,7 +116,7 @@ }; void f() { - return E::X; // expected-error{{'E::Nested::E' is not a class, namespace, or scoped enumeration}} + return E::X; // expected-error{{unscoped enumeration name cannot be specified as a nested name specifier}} } } } @@ -410,3 +410,22 @@ }; } + +namespace PR16951 { + namespace ns { + enum an_enumeration { + ENUMERATOR + }; + } + + int x1 = ns::an_enumeration::ENUMERATOR; // expected-error{{unscoped enumeration name cannot be specified as a nested name specifier}} + int x2 = ns::an_enumeration::ENUMERATOR::vvv; // expected-error{{'PR16951::ns::an_enumeration' is not a class, namespace, or scoped enumeration}} + int x3 = ns::an_enumeration::X; // expected-error{{'PR16951::ns::an_enumeration' is not a class, namespace, or scoped enumeration}} + + enum enumerator_2 { + ENUMERATOR_2 + }; + + int x4 = enumerator_2::ENUMERATOR_2; // expected-error{{unscoped enumeration name cannot be specified as a nested name specifier}} + int x5 = enumerator_2::X2; // expected-error{{'PR16951::enumerator_2' is not a class, namespace, or scoped enumeration}} +}