Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -179,6 +179,8 @@ "attribute %0 ignored, because it is not attached to a declaration">, InGroup; def err_expected_method_body : Error<"expected method body">; +def err_declspec_after_virtspec : Error< + "'%0' qualifier may not appear after the virtual specifier '%1'">; def err_invalid_token_after_toplevel_declarator : Error< "expected ';' after top level declarator">; def err_invalid_token_after_declarator_suggest_equal : Error< Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2298,6 +2298,8 @@ VirtSpecifiers &VS, ExprResult &BitfieldSize, LateParsedAttrList &LateAttrs); + void MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(Declarator &D, + VirtSpecifiers &VS); void ParseCXXClassMemberDeclaration(AccessSpecifier AS, AttributeList *Attr, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), ParsingDeclRAIIObject *DiagsFromTParams = nullptr); Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ include/clang/Sema/DeclSpec.h @@ -2180,7 +2180,7 @@ VS_Sealed = 4 }; - VirtSpecifiers() : Specifiers(0) { } + VirtSpecifiers() : Specifiers(0), LastSpecifier(VS_None) { } bool SetSpecifier(Specifier VS, SourceLocation Loc, const char *&PrevSpec); @@ -2198,12 +2198,16 @@ static const char *getSpecifierName(Specifier VS); + SourceLocation getFirstLocation() const { return FirstLocation; } SourceLocation getLastLocation() const { return LastLocation; } + Specifier getLastSpecifier() const { return LastSpecifier; } private: unsigned Specifiers; + Specifier LastSpecifier; SourceLocation VS_overrideLoc, VS_finalLoc; + SourceLocation FirstLocation; SourceLocation LastLocation; }; Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -5303,7 +5303,7 @@ if (getLangOpts().CPlusPlus) { // FIXME: Accept these components in any order, and produce fixits to // correct the order if the user gets it wrong. Ideally we should deal - // with the virt-specifier-seq and pure-specifier in the same way. + // with the pure-specifier in the same way. // Parse cv-qualifier-seq[opt]. ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -2037,10 +2037,13 @@ BitfieldSize = ParseConstantExpression(); if (BitfieldSize.isInvalid()) SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); - } else + } else { ParseOptionalCXX11VirtSpecifierSeq( VS, getCurrentClass().IsInterface, DeclaratorInfo.getDeclSpec().getFriendSpecLoc()); + if (!VS.isUnset()) + MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo, VS); + } // If a simple-asm-expr is present, parse it. if (Tok.is(tok::kw_asm)) { @@ -2071,6 +2074,70 @@ Diag(Attr->getLoc(), diag::warn_gcc_attribute_location); Attr = Attr->getNext(); } + MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(DeclaratorInfo, VS); + } + } +} + +/// \brief Look for declaration specifiers possibly occurring after C++11 +/// virt-specifier-seq and diagnose them. +void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq( + Declarator &D, + VirtSpecifiers &VS) { + DeclSpec DS(AttrFactory); + + // GNU-style and C++11 attributes are not allowed here, but they will be + // handled by the caller. Diagnose everything else. + ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, false); + D.ExtendWithDeclSpec(DS); + + if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) { + FixItHint Insertion; + auto &Function = D.getFunctionTypeInfo(); + bool DeclIsConst = Function.TypeQuals & DeclSpec::TQ_const; + bool DeclIsVolatile = Function.TypeQuals & DeclSpec::TQ_volatile; + bool DeclIsRestrict = Function.TypeQuals & DeclSpec::TQ_restrict; + if (DS.getTypeQualifiers() & DeclSpec::TQ_const) { + if (!DeclIsConst) { + Insertion = FixItHint::CreateInsertion(VS.getFirstLocation(), "const "); + // Mark the declaration as const. + Function.TypeQuals |= DeclSpec::TQ_const; + Function.ConstQualifierLoc = DS.getConstSpecLoc().getRawEncoding(); + } + Diag(DS.getConstSpecLoc(), + diag::err_declspec_after_virtspec) + << "const" + << VirtSpecifiers::getSpecifierName(VS.getLastSpecifier()) + << FixItHint::CreateRemoval(DS.getConstSpecLoc()) + << Insertion; + } + if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) { + if (!DeclIsVolatile) { + Insertion = FixItHint::CreateInsertion(VS.getFirstLocation(), "volatile "); + // Mark the declaration as volatile. + Function.TypeQuals |= DeclSpec::TQ_volatile; + Function.ConstQualifierLoc = DS.getVolatileSpecLoc().getRawEncoding(); + } + Diag(DS.getVolatileSpecLoc(), + diag::err_declspec_after_virtspec) + << "volatile" + << VirtSpecifiers::getSpecifierName(VS.getLastSpecifier()) + << FixItHint::CreateRemoval(DS.getVolatileSpecLoc()) + << Insertion; + } + if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) { + if (!DeclIsRestrict) { + Insertion = FixItHint::CreateInsertion(VS.getFirstLocation(), "restrict "); + // Mark the declaration as restrict. + Function.TypeQuals |= DeclSpec::TQ_restrict; + Function.ConstQualifierLoc = DS.getRestrictSpecLoc().getRawEncoding(); + } + Diag(DS.getRestrictSpecLoc(), + diag::err_declspec_after_virtspec) + << "restrict" + << VirtSpecifiers::getSpecifierName(VS.getLastSpecifier()) + << FixItHint::CreateRemoval(DS.getRestrictSpecLoc()) + << Insertion; } } } Index: lib/Sema/DeclSpec.cpp =================================================================== --- lib/Sema/DeclSpec.cpp +++ lib/Sema/DeclSpec.cpp @@ -1213,7 +1213,10 @@ bool VirtSpecifiers::SetSpecifier(Specifier VS, SourceLocation Loc, const char *&PrevSpec) { + if (!FirstLocation.isValid()) + FirstLocation = Loc; LastLocation = Loc; + LastSpecifier = VS; if (Specifiers & VS) { PrevSpec = getSpecifierName(VS); Index: test/FixIt/fixit-cxx0x.cpp =================================================================== --- test/FixIt/fixit-cxx0x.cpp +++ test/FixIt/fixit-cxx0x.cpp @@ -158,3 +158,14 @@ template // expected-error {{'...' must immediately precede declared identifier}} void redundantEllipsisInNonTypeTemplateParameter(); } + +namespace MisplacedDeclSpecAfterVirtSpec { + struct B { + virtual void f(); + virtual void f() volatile const; + }; + struct D : B { + virtual void f() override; + virtual void f() override final const volatile; // expected-error {{'const' qualifier may not appear after the virtual specifier 'final'}} expected-error {{'volatile' qualifier may not appear after the virtual specifier 'final'}} + }; +}