forked from OSchip/llvm-project
[clang] Add support for the "abstract" contextual keyword of MSVC
https://docs.microsoft.com/en-us/cpp/extensions/abstract-cpp-component-extensions?view=msvc-160 Note: like the already supported "sealed" keyword, the "abstract" keyword is supported by MSVC by default. Differential revision: https://reviews.llvm.org/D102517
This commit is contained in:
parent
bf35f4af51
commit
818338add7
|
@ -1786,6 +1786,7 @@ public:
|
|||
static bool classofKind(Kind K) {
|
||||
return K >= firstCXXRecord && K <= lastCXXRecord;
|
||||
}
|
||||
void markAbstract() { data().Abstract = true; }
|
||||
};
|
||||
|
||||
/// Store information needed for an explicit specifier.
|
||||
|
|
|
@ -1086,6 +1086,7 @@ def MicrosoftInclude : DiagGroup<"microsoft-include">;
|
|||
def MicrosoftCppMacro : DiagGroup<"microsoft-cpp-macro">;
|
||||
def MicrosoftFixedEnum : DiagGroup<"microsoft-fixed-enum">;
|
||||
def MicrosoftSealed : DiagGroup<"microsoft-sealed">;
|
||||
def MicrosoftAbstract : DiagGroup<"microsoft-abstract">;
|
||||
def MicrosoftUnqualifiedFriend : DiagGroup<"microsoft-unqualified-friend">;
|
||||
def MicrosoftExceptionSpec : DiagGroup<"microsoft-exception-spec">;
|
||||
def MicrosoftUsingDecl : DiagGroup<"microsoft-using-decl">;
|
||||
|
@ -1123,7 +1124,7 @@ def : DiagGroup<"msvc-include", [MicrosoftInclude]>;
|
|||
// Warnings group for warnings about Microsoft extensions.
|
||||
def Microsoft : DiagGroup<"microsoft",
|
||||
[MicrosoftCharize, MicrosoftDrectveSection, MicrosoftInclude,
|
||||
MicrosoftCppMacro, MicrosoftFixedEnum, MicrosoftSealed,
|
||||
MicrosoftCppMacro, MicrosoftFixedEnum, MicrosoftSealed, MicrosoftAbstract,
|
||||
MicrosoftUnqualifiedFriend, MicrosoftExceptionSpec, MicrosoftUsingDecl,
|
||||
MicrosoftMutableReference, MicrosoftPureDefinition,
|
||||
MicrosoftUnionMemberReference, MicrosoftExplicitConstructorCall,
|
||||
|
|
|
@ -922,10 +922,16 @@ def err_override_control_interface : Error<
|
|||
def ext_ms_sealed_keyword : ExtWarn<
|
||||
"'sealed' keyword is a Microsoft extension">,
|
||||
InGroup<MicrosoftSealed>;
|
||||
def ext_ms_abstract_keyword : ExtWarn<
|
||||
"'abstract' keyword is a Microsoft extension">,
|
||||
InGroup<MicrosoftAbstract>;
|
||||
|
||||
def err_access_specifier_interface : Error<
|
||||
"interface types cannot specify '%select{private|protected}0' access">;
|
||||
|
||||
def err_duplicate_class_virt_specifier : Error<
|
||||
"class already marked '%0'">;
|
||||
|
||||
def err_duplicate_virt_specifier : Error<
|
||||
"class member already marked '%0'">;
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ class Parser : public CodeCompletionHandler {
|
|||
/// Contextual keywords for Microsoft extensions.
|
||||
IdentifierInfo *Ident__except;
|
||||
mutable IdentifierInfo *Ident_sealed;
|
||||
mutable IdentifierInfo *Ident_abstract;
|
||||
|
||||
/// Ident_super - IdentifierInfo for "super", to support fast
|
||||
/// comparison.
|
||||
|
@ -2913,6 +2914,7 @@ private:
|
|||
SourceLocation FriendLoc);
|
||||
|
||||
bool isCXX11FinalKeyword() const;
|
||||
bool isClassCompatibleKeyword() const;
|
||||
|
||||
/// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to
|
||||
/// enter a new C++ declarator scope and exit it when the function is
|
||||
|
|
|
@ -2620,7 +2620,8 @@ public:
|
|||
VS_Final = 2,
|
||||
VS_Sealed = 4,
|
||||
// Represents the __final keyword, which is legal for gcc in pre-C++11 mode.
|
||||
VS_GNU_Final = 8
|
||||
VS_GNU_Final = 8,
|
||||
VS_Abstract = 16
|
||||
};
|
||||
|
||||
VirtSpecifiers() : Specifiers(0), LastSpecifier(VS_None) { }
|
||||
|
@ -2636,6 +2637,7 @@ public:
|
|||
bool isFinalSpecified() const { return Specifiers & (VS_Final | VS_Sealed | VS_GNU_Final); }
|
||||
bool isFinalSpelledSealed() const { return Specifiers & VS_Sealed; }
|
||||
SourceLocation getFinalLoc() const { return VS_finalLoc; }
|
||||
SourceLocation getAbstractLoc() const { return VS_abstractLoc; }
|
||||
|
||||
void clear() { Specifiers = 0; }
|
||||
|
||||
|
@ -2649,7 +2651,7 @@ private:
|
|||
unsigned Specifiers;
|
||||
Specifier LastSpecifier;
|
||||
|
||||
SourceLocation VS_overrideLoc, VS_finalLoc;
|
||||
SourceLocation VS_overrideLoc, VS_finalLoc, VS_abstractLoc;
|
||||
SourceLocation FirstLocation;
|
||||
SourceLocation LastLocation;
|
||||
};
|
||||
|
|
|
@ -3147,6 +3147,7 @@ public:
|
|||
void ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagDecl,
|
||||
SourceLocation FinalLoc,
|
||||
bool IsFinalSpelledSealed,
|
||||
bool IsAbstract,
|
||||
SourceLocation LBraceLoc);
|
||||
|
||||
/// ActOnTagFinishDefinition - Invoked once we have finished parsing
|
||||
|
|
|
@ -1710,7 +1710,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
TUK = Sema::TUK_Reference;
|
||||
else if (Tok.is(tok::l_brace) ||
|
||||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
|
||||
(isCXX11FinalKeyword() &&
|
||||
(isClassCompatibleKeyword() &&
|
||||
(NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) {
|
||||
if (DS.isFriendSpecified()) {
|
||||
// C++ [class.friend]p2:
|
||||
|
@ -1726,14 +1726,18 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
// Okay, this is a class definition.
|
||||
TUK = Sema::TUK_Definition;
|
||||
}
|
||||
} else if (isCXX11FinalKeyword() && (NextToken().is(tok::l_square) ||
|
||||
NextToken().is(tok::kw_alignas))) {
|
||||
} else if (isClassCompatibleKeyword() &&
|
||||
(NextToken().is(tok::l_square) ||
|
||||
NextToken().is(tok::kw_alignas) ||
|
||||
isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None)) {
|
||||
// We can't tell if this is a definition or reference
|
||||
// until we skipped the 'final' and C++11 attribute specifiers.
|
||||
TentativeParsingAction PA(*this);
|
||||
|
||||
// Skip the 'final' keyword.
|
||||
ConsumeToken();
|
||||
// Skip the 'final', abstract'... keywords.
|
||||
while (isClassCompatibleKeyword()) {
|
||||
ConsumeToken();
|
||||
}
|
||||
|
||||
// Skip C++11 attribute specifiers.
|
||||
while (true) {
|
||||
|
@ -1982,7 +1986,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
|||
if (TUK == Sema::TUK_Definition) {
|
||||
assert(Tok.is(tok::l_brace) ||
|
||||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
|
||||
isCXX11FinalKeyword());
|
||||
isClassCompatibleKeyword());
|
||||
if (SkipBody.ShouldSkip)
|
||||
SkipCXXMemberSpecification(StartLoc, AttrFixitLoc, TagType,
|
||||
TagOrTempResult.get());
|
||||
|
@ -2249,8 +2253,10 @@ VirtSpecifiers::Specifier Parser::isCXX11VirtSpecifier(const Token &Tok) const {
|
|||
Ident_final = &PP.getIdentifierTable().get("final");
|
||||
if (getLangOpts().GNUKeywords)
|
||||
Ident_GNU_final = &PP.getIdentifierTable().get("__final");
|
||||
if (getLangOpts().MicrosoftExt)
|
||||
if (getLangOpts().MicrosoftExt) {
|
||||
Ident_sealed = &PP.getIdentifierTable().get("sealed");
|
||||
Ident_abstract = &PP.getIdentifierTable().get("abstract");
|
||||
}
|
||||
Ident_override = &PP.getIdentifierTable().get("override");
|
||||
}
|
||||
|
||||
|
@ -2260,6 +2266,9 @@ VirtSpecifiers::Specifier Parser::isCXX11VirtSpecifier(const Token &Tok) const {
|
|||
if (II == Ident_sealed)
|
||||
return VirtSpecifiers::VS_Sealed;
|
||||
|
||||
if (II == Ident_abstract)
|
||||
return VirtSpecifiers::VS_Abstract;
|
||||
|
||||
if (II == Ident_final)
|
||||
return VirtSpecifiers::VS_Final;
|
||||
|
||||
|
@ -2305,6 +2314,8 @@ void Parser::ParseOptionalCXX11VirtSpecifierSeq(VirtSpecifiers &VS,
|
|||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
} else if (Specifier == VirtSpecifiers::VS_Sealed) {
|
||||
Diag(Tok.getLocation(), diag::ext_ms_sealed_keyword);
|
||||
} else if (Specifier == VirtSpecifiers::VS_Abstract) {
|
||||
Diag(Tok.getLocation(), diag::ext_ms_abstract_keyword);
|
||||
} else if (Specifier == VirtSpecifiers::VS_GNU_Final) {
|
||||
Diag(Tok.getLocation(), diag::ext_warn_gnu_final);
|
||||
} else {
|
||||
|
@ -2327,6 +2338,16 @@ bool Parser::isCXX11FinalKeyword() const {
|
|||
Specifier == VirtSpecifiers::VS_Sealed;
|
||||
}
|
||||
|
||||
/// isClassCompatibleKeyword - Determine whether the next token is a C++11
|
||||
/// 'final' or Microsoft 'sealed' or 'abstract' contextual keywords.
|
||||
bool Parser::isClassCompatibleKeyword() const {
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier();
|
||||
return Specifier == VirtSpecifiers::VS_Final ||
|
||||
Specifier == VirtSpecifiers::VS_GNU_Final ||
|
||||
Specifier == VirtSpecifiers::VS_Sealed ||
|
||||
Specifier == VirtSpecifiers::VS_Abstract;
|
||||
}
|
||||
|
||||
/// Parse a C++ member-declarator up to, but not including, the optional
|
||||
/// brace-or-equal-initializer or pure-specifier.
|
||||
bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
|
||||
|
@ -2893,8 +2914,13 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
|
|||
HasStaticInitializer = true;
|
||||
}
|
||||
|
||||
if (PureSpecLoc.isValid() && VS.getAbstractLoc().isValid()) {
|
||||
Diag(PureSpecLoc, diag::err_duplicate_virt_specifier) << "abstract";
|
||||
}
|
||||
if (ThisDecl && PureSpecLoc.isValid())
|
||||
Actions.ActOnPureSpecifier(ThisDecl, PureSpecLoc);
|
||||
else if (ThisDecl && VS.getAbstractLoc().isValid())
|
||||
Actions.ActOnPureSpecifier(ThisDecl, VS.getAbstractLoc());
|
||||
|
||||
// Handle the initializer.
|
||||
if (HasInClassInit != ICIS_NoInit) {
|
||||
|
@ -3279,30 +3305,53 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
|||
Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
|
||||
|
||||
SourceLocation FinalLoc;
|
||||
SourceLocation AbstractLoc;
|
||||
bool IsFinalSpelledSealed = false;
|
||||
bool IsAbstract = false;
|
||||
|
||||
// Parse the optional 'final' keyword.
|
||||
if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) {
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
|
||||
assert((Specifier == VirtSpecifiers::VS_Final ||
|
||||
Specifier == VirtSpecifiers::VS_GNU_Final ||
|
||||
Specifier == VirtSpecifiers::VS_Sealed) &&
|
||||
while (true) {
|
||||
VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok);
|
||||
if (Specifier == VirtSpecifiers::VS_None)
|
||||
break;
|
||||
if (isCXX11FinalKeyword()) {
|
||||
if (FinalLoc.isValid()) {
|
||||
auto Skipped = ConsumeToken();
|
||||
Diag(Skipped, diag::err_duplicate_class_virt_specifier)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
} else {
|
||||
FinalLoc = ConsumeToken();
|
||||
if (Specifier == VirtSpecifiers::VS_Sealed)
|
||||
IsFinalSpelledSealed = true;
|
||||
}
|
||||
} else {
|
||||
if (AbstractLoc.isValid()) {
|
||||
auto Skipped = ConsumeToken();
|
||||
Diag(Skipped, diag::err_duplicate_class_virt_specifier)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
} else {
|
||||
AbstractLoc = ConsumeToken();
|
||||
IsAbstract = true;
|
||||
}
|
||||
}
|
||||
if (TagType == DeclSpec::TST_interface)
|
||||
Diag(FinalLoc, diag::err_override_control_interface)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
else if (Specifier == VirtSpecifiers::VS_Final)
|
||||
Diag(FinalLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_override_control_keyword
|
||||
: diag::ext_override_control_keyword)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
else if (Specifier == VirtSpecifiers::VS_Sealed)
|
||||
Diag(FinalLoc, diag::ext_ms_sealed_keyword);
|
||||
else if (Specifier == VirtSpecifiers::VS_Abstract)
|
||||
Diag(AbstractLoc, diag::ext_ms_abstract_keyword);
|
||||
else if (Specifier == VirtSpecifiers::VS_GNU_Final)
|
||||
Diag(FinalLoc, diag::ext_warn_gnu_final);
|
||||
}
|
||||
assert((FinalLoc.isValid() || AbstractLoc.isValid()) &&
|
||||
"not a class definition");
|
||||
FinalLoc = ConsumeToken();
|
||||
IsFinalSpelledSealed = Specifier == VirtSpecifiers::VS_Sealed;
|
||||
|
||||
if (TagType == DeclSpec::TST_interface)
|
||||
Diag(FinalLoc, diag::err_override_control_interface)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
else if (Specifier == VirtSpecifiers::VS_Final)
|
||||
Diag(FinalLoc, getLangOpts().CPlusPlus11
|
||||
? diag::warn_cxx98_compat_override_control_keyword
|
||||
: diag::ext_override_control_keyword)
|
||||
<< VirtSpecifiers::getSpecifierName(Specifier);
|
||||
else if (Specifier == VirtSpecifiers::VS_Sealed)
|
||||
Diag(FinalLoc, diag::ext_ms_sealed_keyword);
|
||||
else if (Specifier == VirtSpecifiers::VS_GNU_Final)
|
||||
Diag(FinalLoc, diag::ext_warn_gnu_final);
|
||||
|
||||
// Parse any C++11 attributes after 'final' keyword.
|
||||
// These attributes are not allowed to appear here,
|
||||
|
@ -3375,7 +3424,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
|||
|
||||
if (TagDecl)
|
||||
Actions.ActOnStartCXXMemberDeclarations(getCurScope(), TagDecl, FinalLoc,
|
||||
IsFinalSpelledSealed,
|
||||
IsFinalSpelledSealed, IsAbstract,
|
||||
T.getOpenLocation());
|
||||
|
||||
// C++ 11p3: Members of a class defined with the keyword class are private
|
||||
|
|
|
@ -1475,6 +1475,7 @@ bool VirtSpecifiers::SetSpecifier(Specifier VS, SourceLocation Loc,
|
|||
case VS_GNU_Final:
|
||||
case VS_Sealed:
|
||||
case VS_Final: VS_finalLoc = Loc; break;
|
||||
case VS_Abstract: VS_abstractLoc = Loc; break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1487,5 +1488,6 @@ const char *VirtSpecifiers::getSpecifierName(Specifier VS) {
|
|||
case VS_Final: return "final";
|
||||
case VS_GNU_Final: return "__final";
|
||||
case VS_Sealed: return "sealed";
|
||||
case VS_Abstract: return "abstract";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16456,6 +16456,7 @@ Decl *Sema::ActOnObjCContainerStartDefinition(Decl *IDecl) {
|
|||
void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
|
||||
SourceLocation FinalLoc,
|
||||
bool IsFinalSpelledSealed,
|
||||
bool IsAbstract,
|
||||
SourceLocation LBraceLoc) {
|
||||
AdjustDeclIfTemplate(TagD);
|
||||
CXXRecordDecl *Record = cast<CXXRecordDecl>(TagD);
|
||||
|
@ -16465,11 +16466,14 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
|
|||
if (!Record->getIdentifier())
|
||||
return;
|
||||
|
||||
if (FinalLoc.isValid())
|
||||
if (IsAbstract)
|
||||
Record->markAbstract();
|
||||
|
||||
if (FinalLoc.isValid()) {
|
||||
Record->addAttr(FinalAttr::Create(
|
||||
Context, FinalLoc, AttributeCommonInfo::AS_Keyword,
|
||||
static_cast<FinalAttr::Spelling>(IsFinalSpelledSealed)));
|
||||
|
||||
}
|
||||
// C++ [class]p2:
|
||||
// [...] The class-name is also inserted into the scope of the
|
||||
// class itself; this is known as the injected-class-name. For
|
||||
|
|
|
@ -462,6 +462,77 @@ class SealedDestructor { // expected-note {{mark 'SealedDestructor' as 'sealed'
|
|||
virtual ~SealedDestructor() sealed; // expected-warning {{class with destructor marked 'sealed' cannot be inherited from}}
|
||||
};
|
||||
|
||||
// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}}
|
||||
class AbstractClass abstract {
|
||||
int i;
|
||||
};
|
||||
|
||||
// expected-error@+1 {{variable type 'AbstractClass' is an abstract class}}
|
||||
AbstractClass abstractInstance;
|
||||
|
||||
// expected-warning@+4 {{abstract class is marked 'sealed'}}
|
||||
// expected-note@+3 {{'AbstractAndSealedClass' declared here}}
|
||||
// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}}
|
||||
// expected-warning@+1 {{'sealed' keyword is a Microsoft extension}}
|
||||
class AbstractAndSealedClass abstract sealed {}; // Does no really make sense, but allowed
|
||||
|
||||
// expected-error@+1 {{variable type 'AbstractAndSealedClass' is an abstract class}}
|
||||
AbstractAndSealedClass abstractAndSealedInstance;
|
||||
// expected-error@+1 {{base 'AbstractAndSealedClass' is marked 'sealed'}}
|
||||
class InheritFromAbstractAndSealed : AbstractAndSealedClass {};
|
||||
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@+4 {{'final' keyword is a C++11 extension}}
|
||||
// expected-warning@+3 {{'final' keyword is a C++11 extension}}
|
||||
#endif
|
||||
// expected-error@+1 {{class already marked 'final'}}
|
||||
class TooManyVirtSpecifiers1 final final {};
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@+4 {{'final' keyword is a C++11 extension}}
|
||||
#endif
|
||||
// expected-warning@+2 {{'sealed' keyword is a Microsoft extension}}
|
||||
// expected-error@+1 {{class already marked 'sealed'}}
|
||||
class TooManyVirtSpecifiers2 final sealed {};
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@+6 {{'final' keyword is a C++11 extension}}
|
||||
// expected-warning@+5 {{'final' keyword is a C++11 extension}}
|
||||
#endif
|
||||
// expected-warning@+3 {{abstract class is marked 'final'}}
|
||||
// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}}
|
||||
// expected-error@+1 {{class already marked 'final'}}
|
||||
class TooManyVirtSpecifiers3 final abstract final {};
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@+6 {{'final' keyword is a C++11 extension}}
|
||||
#endif
|
||||
// expected-warning@+4 {{abstract class is marked 'final'}}
|
||||
// expected-warning@+3 {{'abstract' keyword is a Microsoft extension}}
|
||||
// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}}
|
||||
// expected-error@+1 {{class already marked 'abstract'}}
|
||||
class TooManyVirtSpecifiers4 abstract final abstract {};
|
||||
|
||||
class Base {
|
||||
virtual void i();
|
||||
};
|
||||
class AbstractFunctionInClass : public Base {
|
||||
// expected-note@+2 {{unimplemented pure virtual method 'f' in 'AbstractFunctionInClass'}}
|
||||
// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}}
|
||||
virtual void f() abstract;
|
||||
// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}}
|
||||
void g() abstract; // expected-error {{'g' is not virtual and cannot be declared pure}}
|
||||
// expected-note@+2 {{unimplemented pure virtual method 'h' in 'AbstractFunctionInClass'}}
|
||||
// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}}
|
||||
virtual void h() abstract = 0; // expected-error {{class member already marked 'abstract'}}
|
||||
#if __cplusplus <= 199711L
|
||||
// expected-warning@+4 {{'override' keyword is a C++11 extension}}
|
||||
#endif
|
||||
// expected-note@+2 {{unimplemented pure virtual method 'i' in 'AbstractFunctionInClass'}}
|
||||
// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}}
|
||||
virtual void i() abstract override;
|
||||
};
|
||||
|
||||
// expected-error@+1 {{variable type 'AbstractFunctionInClass' is an abstract class}}
|
||||
AbstractFunctionInClass abstractFunctionInClassInstance;
|
||||
|
||||
void AfterClassBody() {
|
||||
// expected-warning@+1 {{attribute 'deprecated' is ignored, place it after "struct" to apply attribute to type declaration}}
|
||||
struct D {} __declspec(deprecated);
|
||||
|
|
Loading…
Reference in New Issue