forked from OSchip/llvm-project
Add -f[no-]double-square-bracket-attributes as new driver options to control use of [[]] attributes in all language modes. This is the initial implementation of WG14 N2165, which is a proposal to add [[]] attributes to C2x, but also allows you to enable these attributes in C++98, or disable them in C++11 or later.
llvm-svn: 315856
This commit is contained in:
parent
30f30d37fb
commit
606093a53b
|
@ -210,6 +210,10 @@ class CXX11<string namespace, string name, int version = 1>
|
|||
string Namespace = namespace;
|
||||
int Version = version;
|
||||
}
|
||||
class C2x<string namespace, string name> : Spelling<name, "C2x"> {
|
||||
string Namespace = namespace;
|
||||
}
|
||||
|
||||
class Keyword<string name> : Spelling<name, "Keyword">;
|
||||
class Pragma<string namespace, string name> : Spelling<name, "Pragma"> {
|
||||
string Namespace = namespace;
|
||||
|
@ -958,7 +962,7 @@ def RenderScriptKernel : Attr {
|
|||
|
||||
def Deprecated : InheritableAttr {
|
||||
let Spellings = [GCC<"deprecated">, Declspec<"deprecated">,
|
||||
CXX11<"","deprecated", 201309>];
|
||||
CXX11<"","deprecated", 201309>, C2x<"", "deprecated">];
|
||||
let Args = [StringArgument<"Message", 1>,
|
||||
// An optional string argument that enables us to provide a
|
||||
// Fix-It.
|
||||
|
|
|
@ -26,6 +26,8 @@ enum class AttrSyntax {
|
|||
Microsoft,
|
||||
// Is the identifier known as a C++-style attribute?
|
||||
CXX,
|
||||
// Is the identifier known as a C-style attribute?
|
||||
C,
|
||||
// Is the identifier known as a pragma attribute?
|
||||
Pragma
|
||||
};
|
||||
|
|
|
@ -137,6 +137,8 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
|
|||
LANGOPT(CoroutinesTS , 1, 0, "C++ coroutines TS")
|
||||
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
|
||||
|
||||
LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
|
||||
|
||||
BENIGN_LANGOPT(ThreadsafeStatics , 1, 1, "thread-safe static initializers")
|
||||
LANGOPT(POSIXThreads , 1, 0, "POSIX thread support")
|
||||
LANGOPT(Blocks , 1, 0, "blocks extension to C")
|
||||
|
|
|
@ -606,6 +606,13 @@ def fastf : Flag<["-"], "fastf">, Group<f_Group>;
|
|||
def fast : Flag<["-"], "fast">, Group<f_Group>;
|
||||
def fasynchronous_unwind_tables : Flag<["-"], "fasynchronous-unwind-tables">, Group<f_Group>;
|
||||
|
||||
def fdouble_square_bracket_attributes : Flag<[ "-" ], "fdouble-square-bracket-attributes">,
|
||||
Group<f_Group>, Flags<[DriverOption, CC1Option]>,
|
||||
HelpText<"Enable '[[]]' attributes in all C and C++ language modes">;
|
||||
def fno_double_square_bracket_attributes : Flag<[ "-" ], "fno-fdouble-square-bracket-attributes">,
|
||||
Group<f_Group>, Flags<[DriverOption]>,
|
||||
HelpText<"Disable '[[]]' attributes in all C and C++ language modes">;
|
||||
|
||||
def fautolink : Flag <["-"], "fautolink">, Group<f_Group>;
|
||||
def fno_autolink : Flag <["-"], "fno-autolink">, Group<f_Group>,
|
||||
Flags<[DriverOption, CC1Option]>,
|
||||
|
|
|
@ -2163,18 +2163,25 @@ public:
|
|||
private:
|
||||
void ParseBlockId(SourceLocation CaretLoc);
|
||||
|
||||
// Check for the start of a C++11 attribute-specifier-seq in a context where
|
||||
// an attribute is not allowed.
|
||||
/// Are [[]] attributes enabled?
|
||||
bool standardAttributesAllowed() const {
|
||||
const LangOptions &LO = getLangOpts();
|
||||
return LO.DoubleSquareBracketAttributes;
|
||||
}
|
||||
|
||||
// Check for the start of an attribute-specifier-seq in a context where an
|
||||
// attribute is not allowed.
|
||||
bool CheckProhibitedCXX11Attribute() {
|
||||
assert(Tok.is(tok::l_square));
|
||||
if (!getLangOpts().CPlusPlus11 || NextToken().isNot(tok::l_square))
|
||||
if (!standardAttributesAllowed() || NextToken().isNot(tok::l_square))
|
||||
return false;
|
||||
return DiagnoseProhibitedCXX11Attribute();
|
||||
}
|
||||
|
||||
bool DiagnoseProhibitedCXX11Attribute();
|
||||
void CheckMisplacedCXX11Attribute(ParsedAttributesWithRange &Attrs,
|
||||
SourceLocation CorrectLocation) {
|
||||
if (!getLangOpts().CPlusPlus11)
|
||||
if (!standardAttributesAllowed())
|
||||
return;
|
||||
if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
|
||||
Tok.isNot(tok::kw_alignas))
|
||||
|
@ -2194,17 +2201,18 @@ private:
|
|||
}
|
||||
void DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs);
|
||||
|
||||
// Forbid C++11 attributes that appear on certain syntactic
|
||||
// locations which standard permits but we don't supported yet,
|
||||
// for example, attributes appertain to decl specifiers.
|
||||
// Forbid C++11 and C2x attributes that appear on certain syntactic locations
|
||||
// which standard permits but we don't supported yet, for example, attributes
|
||||
// appertain to decl specifiers.
|
||||
void ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs,
|
||||
unsigned DiagID);
|
||||
|
||||
/// \brief Skip C++11 attributes and return the end location of the last one.
|
||||
/// \brief Skip C++11 and C2x attributes and return the end location of the
|
||||
/// last one.
|
||||
/// \returns SourceLocation() if there are no attributes.
|
||||
SourceLocation SkipCXX11Attributes();
|
||||
|
||||
/// \brief Diagnose and skip C++11 attributes that appear in syntactic
|
||||
/// \brief Diagnose and skip C++11 and C2x attributes that appear in syntactic
|
||||
/// locations where attributes are not allowed.
|
||||
void DiagnoseAndSkipCXX11Attributes();
|
||||
|
||||
|
@ -2254,7 +2262,7 @@ private:
|
|||
AttributeList::Syntax Syntax);
|
||||
|
||||
void MaybeParseCXX11Attributes(Declarator &D) {
|
||||
if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
|
||||
if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
|
||||
ParsedAttributesWithRange attrs(AttrFactory);
|
||||
SourceLocation endLoc;
|
||||
ParseCXX11Attributes(attrs, &endLoc);
|
||||
|
@ -2263,7 +2271,7 @@ private:
|
|||
}
|
||||
void MaybeParseCXX11Attributes(ParsedAttributes &attrs,
|
||||
SourceLocation *endLoc = nullptr) {
|
||||
if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
|
||||
if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
|
||||
ParsedAttributesWithRange attrsWithRange(AttrFactory);
|
||||
ParseCXX11Attributes(attrsWithRange, endLoc);
|
||||
attrs.takeAllFrom(attrsWithRange);
|
||||
|
@ -2272,8 +2280,8 @@ private:
|
|||
void MaybeParseCXX11Attributes(ParsedAttributesWithRange &attrs,
|
||||
SourceLocation *endLoc = nullptr,
|
||||
bool OuterMightBeMessageSend = false) {
|
||||
if (getLangOpts().CPlusPlus11 &&
|
||||
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
|
||||
if (standardAttributesAllowed() &&
|
||||
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend))
|
||||
ParseCXX11Attributes(attrs, endLoc);
|
||||
}
|
||||
|
||||
|
@ -2281,8 +2289,8 @@ private:
|
|||
SourceLocation *EndLoc = nullptr);
|
||||
void ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
|
||||
SourceLocation *EndLoc = nullptr);
|
||||
/// \brief Parses a C++-style attribute argument list. Returns true if this
|
||||
/// results in adding an attribute to the ParsedAttributes list.
|
||||
/// \brief Parses a C++11 (or C2x)-style attribute argument list. Returns true
|
||||
/// if this results in adding an attribute to the ParsedAttributes list.
|
||||
bool ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
|
||||
SourceLocation AttrNameLoc,
|
||||
ParsedAttributes &Attrs, SourceLocation *EndLoc,
|
||||
|
|
|
@ -100,6 +100,8 @@ public:
|
|||
AS_GNU,
|
||||
/// [[...]]
|
||||
AS_CXX11,
|
||||
/// [[...]]
|
||||
AS_C2x,
|
||||
/// __declspec(...)
|
||||
AS_Declspec,
|
||||
/// [uuid("...")] class Foo
|
||||
|
@ -378,6 +380,9 @@ public:
|
|||
bool isCXX11Attribute() const {
|
||||
return SyntaxUsed == AS_CXX11 || isAlignasAttribute();
|
||||
}
|
||||
bool isC2xAttribute() const {
|
||||
return SyntaxUsed == AS_C2x;
|
||||
}
|
||||
bool isKeywordAttribute() const {
|
||||
return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword;
|
||||
}
|
||||
|
|
|
@ -2138,6 +2138,12 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
|
|||
&& Opts.OpenCLVersion >= 200);
|
||||
Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional);
|
||||
Opts.CoroutinesTS = Args.hasArg(OPT_fcoroutines_ts);
|
||||
|
||||
// Enable [[]] attributes in C++11 by default.
|
||||
Opts.DoubleSquareBracketAttributes =
|
||||
Args.hasFlag(OPT_fdouble_square_bracket_attributes,
|
||||
OPT_fno_double_square_bracket_attributes, Opts.CPlusPlus11);
|
||||
|
||||
Opts.ModulesTS = Args.hasArg(OPT_fmodules_ts);
|
||||
Opts.Modules = Args.hasArg(OPT_fmodules) || Opts.ModulesTS;
|
||||
Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse);
|
||||
|
|
|
@ -3612,7 +3612,9 @@ LexNextToken:
|
|||
if (LangOpts.Digraphs && Char == '>') {
|
||||
Kind = tok::r_square; // ':>' -> ']'
|
||||
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
|
||||
} else if (LangOpts.CPlusPlus && Char == ':') {
|
||||
} else if ((LangOpts.CPlusPlus ||
|
||||
LangOpts.DoubleSquareBracketAttributes) &&
|
||||
Char == ':') {
|
||||
Kind = tok::coloncolon;
|
||||
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
|
||||
} else {
|
||||
|
|
|
@ -1562,7 +1562,7 @@ void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
|
|||
void Parser::ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs,
|
||||
unsigned DiagID) {
|
||||
for (AttributeList *Attr = Attrs.getList(); Attr; Attr = Attr->getNext()) {
|
||||
if (!Attr->isCXX11Attribute())
|
||||
if (!Attr->isCXX11Attribute() && !Attr->isC2xAttribute())
|
||||
continue;
|
||||
if (Attr->getKind() == AttributeList::UnknownAttribute)
|
||||
Diag(Attr->getLoc(), diag::warn_unknown_attribute_ignored)
|
||||
|
@ -2925,7 +2925,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
|
||||
case tok::l_square:
|
||||
case tok::kw_alignas:
|
||||
if (!getLangOpts().CPlusPlus11 || !isCXX11AttributeSpecifier())
|
||||
if (!standardAttributesAllowed() || !isCXX11AttributeSpecifier())
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
ProhibitAttributes(attrs);
|
||||
|
@ -3778,7 +3778,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
|
|||
/// semicolon.
|
||||
///
|
||||
/// struct-declaration:
|
||||
/// specifier-qualifier-list struct-declarator-list
|
||||
/// [C2x] attributes-specifier-seq[opt]
|
||||
/// specifier-qualifier-list struct-declarator-list
|
||||
/// [GNU] __extension__ struct-declaration
|
||||
/// [GNU] specifier-qualifier-list
|
||||
/// struct-declarator-list:
|
||||
|
@ -3802,6 +3803,11 @@ void Parser::ParseStructDeclaration(
|
|||
return ParseStructDeclaration(DS, FieldsCallback);
|
||||
}
|
||||
|
||||
// Parse leading attributes.
|
||||
ParsedAttributesWithRange Attrs(AttrFactory);
|
||||
MaybeParseCXX11Attributes(Attrs);
|
||||
DS.takeAttributesFrom(Attrs);
|
||||
|
||||
// Parse the common specifier-qualifiers-list piece.
|
||||
ParseSpecifierQualifierList(DS);
|
||||
|
||||
|
@ -4412,11 +4418,12 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
|
|||
ParsedAttributesWithRange attrs(AttrFactory);
|
||||
MaybeParseGNUAttributes(attrs);
|
||||
ProhibitAttributes(attrs); // GNU-style attributes are prohibited.
|
||||
if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier()) {
|
||||
Diag(Tok.getLocation(), getLangOpts().CPlusPlus1z
|
||||
? diag::warn_cxx14_compat_ns_enum_attribute
|
||||
: diag::ext_ns_enum_attribute)
|
||||
<< 1 /*enumerator*/;
|
||||
if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
|
||||
if (getLangOpts().CPlusPlus)
|
||||
Diag(Tok.getLocation(), getLangOpts().CPlusPlus1z
|
||||
? diag::warn_cxx14_compat_ns_enum_attribute
|
||||
: diag::ext_ns_enum_attribute)
|
||||
<< 1 /*enumerator*/;
|
||||
ParseCXX11Attributes(attrs);
|
||||
}
|
||||
|
||||
|
@ -5025,7 +5032,7 @@ void Parser::ParseTypeQualifierListOpt(
|
|||
DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed,
|
||||
bool IdentifierRequired,
|
||||
Optional<llvm::function_ref<void()>> CodeCompletionHandler) {
|
||||
if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) &&
|
||||
if (standardAttributesAllowed() && (AttrReqs & AR_CXX11AttributesParsed) &&
|
||||
isCXX11AttributeSpecifier()) {
|
||||
ParsedAttributesWithRange attrs(AttrFactory);
|
||||
ParseCXX11Attributes(attrs);
|
||||
|
@ -5962,7 +5969,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
SmallVector<SourceRange, 2> DynamicExceptionRanges;
|
||||
ExprResult NoexceptExpr;
|
||||
CachedTokens *ExceptionSpecTokens = nullptr;
|
||||
ParsedAttributes FnAttrs(AttrFactory);
|
||||
ParsedAttributesWithRange FnAttrs(AttrFactory);
|
||||
TypeResult TrailingReturnType;
|
||||
|
||||
/* LocalEndLoc is the end location for the local FunctionTypeLoc.
|
||||
|
@ -5983,6 +5990,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
RParenLoc = Tracker.getCloseLocation();
|
||||
LocalEndLoc = RParenLoc;
|
||||
EndLoc = RParenLoc;
|
||||
|
||||
// If there are attributes following the identifier list, parse them and
|
||||
// prohibit them.
|
||||
MaybeParseCXX11Attributes(FnAttrs);
|
||||
ProhibitAttributes(FnAttrs);
|
||||
} else {
|
||||
if (Tok.isNot(tok::r_paren))
|
||||
ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo,
|
||||
|
@ -6089,6 +6101,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
|||
TrailingReturnType = ParseTrailingReturnType(Range);
|
||||
EndLoc = Range.getEnd();
|
||||
}
|
||||
} else if (standardAttributesAllowed()) {
|
||||
MaybeParseCXX11Attributes(FnAttrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3814,7 +3814,7 @@ IdentifierInfo *Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc) {
|
|||
}
|
||||
|
||||
static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
|
||||
IdentifierInfo *ScopeName) {
|
||||
IdentifierInfo *ScopeName) {
|
||||
switch (AttributeList::getKind(AttrName, ScopeName,
|
||||
AttributeList::AS_CXX11)) {
|
||||
case AttributeList::AT_CarriesDependency:
|
||||
|
@ -3853,11 +3853,14 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
|
|||
SourceLocation ScopeLoc) {
|
||||
assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
|
||||
SourceLocation LParenLoc = Tok.getLocation();
|
||||
const LangOptions &LO = getLangOpts();
|
||||
AttributeList::Syntax Syntax =
|
||||
LO.CPlusPlus ? AttributeList::AS_CXX11 : AttributeList::AS_C2x;
|
||||
|
||||
// If the attribute isn't known, we will not attempt to parse any
|
||||
// arguments.
|
||||
if (!hasAttribute(AttrSyntax::CXX, ScopeName, AttrName,
|
||||
getTargetInfo(), getLangOpts())) {
|
||||
if (!hasAttribute(LO.CPlusPlus ? AttrSyntax::CXX : AttrSyntax::C, ScopeName,
|
||||
AttrName, getTargetInfo(), getLangOpts())) {
|
||||
// Eat the left paren, then skip to the ending right paren.
|
||||
ConsumeParen();
|
||||
SkipUntil(tok::r_paren);
|
||||
|
@ -3868,7 +3871,7 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
|
|||
// GNU-scoped attributes have some special cases to handle GNU-specific
|
||||
// behaviors.
|
||||
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
|
||||
ScopeLoc, AttributeList::AS_CXX11, nullptr);
|
||||
ScopeLoc, Syntax, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3877,11 +3880,11 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
|
|||
if (ScopeName && ScopeName->getName() == "clang")
|
||||
NumArgs =
|
||||
ParseClangAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
|
||||
ScopeLoc, AttributeList::AS_CXX11);
|
||||
ScopeLoc, Syntax);
|
||||
else
|
||||
NumArgs =
|
||||
ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc,
|
||||
ScopeName, ScopeLoc, AttributeList::AS_CXX11);
|
||||
ScopeName, ScopeLoc, Syntax);
|
||||
|
||||
const AttributeList *Attr = Attrs.getList();
|
||||
if (Attr && IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) {
|
||||
|
@ -3907,7 +3910,7 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
|
|||
return true;
|
||||
}
|
||||
|
||||
/// ParseCXX11AttributeSpecifier - Parse a C++11 attribute-specifier.
|
||||
/// ParseCXX11AttributeSpecifier - Parse a C++11 or C2x attribute-specifier.
|
||||
///
|
||||
/// [C++11] attribute-specifier:
|
||||
/// '[' '[' attribute-list ']' ']'
|
||||
|
@ -3939,8 +3942,8 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
|
|||
return;
|
||||
}
|
||||
|
||||
assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)
|
||||
&& "Not a C++11 attribute list");
|
||||
assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square) &&
|
||||
"Not a double square bracket attribute list");
|
||||
|
||||
Diag(Tok.getLocation(), diag::warn_cxx98_compat_attribute);
|
||||
|
||||
|
@ -4016,10 +4019,12 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
|
|||
ScopeName, ScopeLoc);
|
||||
|
||||
if (!AttrParsed)
|
||||
attrs.addNew(AttrName,
|
||||
SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc,
|
||||
AttrLoc),
|
||||
ScopeName, ScopeLoc, nullptr, 0, AttributeList::AS_CXX11);
|
||||
attrs.addNew(
|
||||
AttrName,
|
||||
SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, AttrLoc),
|
||||
ScopeName, ScopeLoc, nullptr, 0,
|
||||
getLangOpts().CPlusPlus ? AttributeList::AS_CXX11
|
||||
: AttributeList::AS_C2x);
|
||||
|
||||
if (TryConsumeToken(tok::ellipsis))
|
||||
Diag(Tok, diag::err_cxx11_attribute_forbids_ellipsis)
|
||||
|
@ -4034,13 +4039,13 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
|
|||
SkipUntil(tok::r_square);
|
||||
}
|
||||
|
||||
/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq.
|
||||
/// ParseCXX11Attributes - Parse a C++11 or C2x attribute-specifier-seq.
|
||||
///
|
||||
/// attribute-specifier-seq:
|
||||
/// attribute-specifier-seq[opt] attribute-specifier
|
||||
void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
|
||||
SourceLocation *endLoc) {
|
||||
assert(getLangOpts().CPlusPlus11);
|
||||
assert(standardAttributesAllowed());
|
||||
|
||||
SourceLocation StartLoc = Tok.getLocation(), Loc;
|
||||
if (!endLoc)
|
||||
|
|
|
@ -114,7 +114,8 @@ static StringRef normalizeAttrName(StringRef AttrName, StringRef ScopeName,
|
|||
// Normalize the attribute name, __foo__ becomes foo. This is only allowable
|
||||
// for GNU attributes.
|
||||
bool IsGNU = SyntaxUsed == AttributeList::AS_GNU ||
|
||||
(SyntaxUsed == AttributeList::AS_CXX11 && ScopeName == "gnu");
|
||||
((SyntaxUsed == AttributeList::AS_CXX11 ||
|
||||
SyntaxUsed == AttributeList::AS_C2x) && ScopeName == "gnu");
|
||||
if (IsGNU && AttrName.size() >= 4 && AttrName.startswith("__") &&
|
||||
AttrName.endswith("__"))
|
||||
AttrName = AttrName.slice(2, AttrName.size() - 2);
|
||||
|
@ -135,7 +136,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name,
|
|||
|
||||
// Ensure that in the case of C++11 attributes, we look for '::foo' if it is
|
||||
// unscoped.
|
||||
if (ScopeName || SyntaxUsed == AS_CXX11)
|
||||
if (ScopeName || SyntaxUsed == AS_CXX11 || SyntaxUsed == AS_C2x)
|
||||
FullName += "::";
|
||||
FullName += AttrName;
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-pc-linux -fdouble-square-bracket-attributes -Wno-deprecated-declarations -ast-dump -ast-dump-filter Test %s | FileCheck --strict-whitespace %s
|
||||
|
||||
int Test1 [[deprecated]];
|
||||
// CHECK: VarDecl{{.*}}Test1
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:13> "" ""
|
||||
|
||||
enum [[deprecated("Frobble")]] Test2 {
|
||||
Test3 [[deprecated]]
|
||||
};
|
||||
// CHECK: EnumDecl{{.*}}Test2
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:8, col:28> "Frobble" ""
|
||||
// CHECK-NEXT: EnumConstantDecl{{.*}}Test3
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:11> "" ""
|
||||
|
||||
struct [[deprecated]] Test4 {
|
||||
[[deprecated("Frobble")]] int Test5, Test6;
|
||||
int Test7 [[deprecated]] : 12;
|
||||
};
|
||||
// CHECK: RecordDecl{{.*}}Test4
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""
|
||||
// CHECK-NEXT: FieldDecl{{.*}}Test5
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""
|
||||
// CHECK-NEXT: FieldDecl{{.*}}Test6
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""
|
||||
// CHECK-NEXT: FieldDecl{{.*}}Test7
|
||||
// CHECK-NEXT: IntegerLiteral{{.*}}'int' 12
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""
|
||||
|
||||
struct [[deprecated]] Test8;
|
||||
// CHECK: RecordDecl{{.*}}Test8
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""
|
||||
|
||||
[[deprecated]] void Test9(int Test10 [[deprecated]]);
|
||||
// CHECK: FunctionDecl{{.*}}Test9
|
||||
// CHECK-NEXT: ParmVarDecl{{.*}}Test10
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:40> "" ""
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:3> "" ""
|
||||
|
||||
void Test11 [[deprecated]](void);
|
||||
// CHECK: FunctionDecl{{.*}}Test11
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""
|
||||
|
||||
void Test12(void) [[deprecated]] {}
|
||||
// CHECK: FunctionDecl{{.*}}Test12
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
// CHECK-NEXT: DeprecatedAttr 0x{{[^ ]*}} <col:21> "" ""
|
|
@ -0,0 +1,122 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -fdouble-square-bracket-attributes -verify %s
|
||||
|
||||
enum [[]] E {
|
||||
One [[]],
|
||||
Two,
|
||||
Three [[]]
|
||||
};
|
||||
|
||||
enum [[]] { Four };
|
||||
[[]] enum E2 { Five }; // expected-error {{an attribute list cannot appear here}}
|
||||
|
||||
// FIXME: this diagnostic can be improved.
|
||||
enum { [[]] Six }; // expected-error {{expected identifier}}
|
||||
|
||||
// FIXME: this diagnostic can be improved.
|
||||
enum E3 [[]] { Seven }; // expected-error {{expected identifier or '('}}
|
||||
|
||||
struct [[]] S1 {
|
||||
int i [[]];
|
||||
int [[]] j;
|
||||
int k[10] [[]];
|
||||
int l[[]][10];
|
||||
[[]] int m, n;
|
||||
int o [[]] : 12;
|
||||
};
|
||||
|
||||
[[]] struct S2 { int a; }; // expected-error {{an attribute list cannot appear here}}
|
||||
struct S3 [[]] { int a; }; // expected-error {{an attribute list cannot appear here}}
|
||||
|
||||
union [[]] U {
|
||||
double d [[]];
|
||||
[[]] int i;
|
||||
};
|
||||
|
||||
[[]] union U2 { double d; }; // expected-error {{an attribute list cannot appear here}}
|
||||
union U3 [[]] { double d; }; // expected-error {{an attribute list cannot appear here}}
|
||||
|
||||
struct [[]] IncompleteStruct;
|
||||
union [[]] IncompleteUnion;
|
||||
enum [[]] IncompleteEnum;
|
||||
enum __attribute__((deprecated)) IncompleteEnum2;
|
||||
|
||||
[[]] void f1(void);
|
||||
void [[]] f2(void);
|
||||
void f3 [[]] (void);
|
||||
void f4(void) [[]];
|
||||
|
||||
void f5(int i [[]], [[]] int j, int [[]] k);
|
||||
|
||||
void f6(a, b) [[]] int a; int b; { // expected-error {{an attribute list cannot appear here}}
|
||||
}
|
||||
|
||||
// FIXME: technically, an attribute list cannot appear here, but we currently
|
||||
// parse it as part of the return type of the function, which is reasonable
|
||||
// behavior given that we *don't* want to parse it as part of the K&R parameter
|
||||
// declarations. It is disallowed to avoid a parsing ambiguity we already
|
||||
// handle well.
|
||||
int (*f7(a, b))(int, int) [[]] int a; int b; {
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[]] int a, b;
|
||||
int c [[]], d [[]];
|
||||
|
||||
void f8(void) [[]] {
|
||||
[[]] int i, j;
|
||||
int k, l [[]];
|
||||
}
|
||||
|
||||
[[]] void f9(void) {
|
||||
int i[10] [[]];
|
||||
int (*fp1)(void)[[]];
|
||||
int (*fp2 [[]])(void);
|
||||
|
||||
int * [[]] *ipp;
|
||||
}
|
||||
|
||||
void f10(int j[static 10] [[]], int k[*] [[]]);
|
||||
|
||||
void f11(void) {
|
||||
[[]] {}
|
||||
[[]] if (1) {}
|
||||
|
||||
[[]] switch (1) {
|
||||
[[]] case 1: [[]] break;
|
||||
[[]] default: break;
|
||||
}
|
||||
|
||||
goto foo;
|
||||
[[]] foo: (void)1;
|
||||
|
||||
[[]] for (;;);
|
||||
[[]] while (1);
|
||||
[[]] do [[]] { } while(1);
|
||||
|
||||
[[]] (void)1;
|
||||
|
||||
[[]];
|
||||
|
||||
(void)sizeof(int [4][[]]);
|
||||
(void)sizeof(struct [[]] S3 { int a [[]]; });
|
||||
|
||||
[[]] return;
|
||||
}
|
||||
|
||||
[[attr]] void f12(void); // expected-warning {{unknown attribute 'attr' ignored}}
|
||||
[[vendor::attr]] void f13(void); // expected-warning {{unknown attribute 'attr' ignored}}
|
||||
|
||||
// Ensure that asm statements properly handle double colons.
|
||||
void test_asm(void) {
|
||||
asm("ret" :::);
|
||||
asm("foo" :: "r" (xx)); // expected-error {{use of undeclared identifier 'xx'}}
|
||||
}
|
||||
|
||||
// Do not allow 'using' to introduce vendor attribute namespaces.
|
||||
[[using vendor: attr1, attr2]] void f14(void); // expected-error {{expected ']'}} \
|
||||
// expected-warning {{unknown attribute 'vendor' ignored}} \
|
||||
// expected-warning {{unknown attribute 'using' ignored}}
|
||||
|
||||
struct [[]] S4 *s; // expected-error {{an attribute list cannot appear here}}
|
||||
struct S5 {};
|
||||
int c = sizeof(struct [[]] S5); // expected-error {{an attribute list cannot appear here}}
|
|
@ -0,0 +1,21 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -fdouble-square-bracket-attributes -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
enum __attribute__((deprecated)) E1 : int; // ok
|
||||
enum [[deprecated]] E2 : int;
|
||||
|
||||
@interface Base
|
||||
@end
|
||||
|
||||
@interface S : Base
|
||||
- (void) bar;
|
||||
@end
|
||||
|
||||
@interface T : Base
|
||||
- (S *) foo;
|
||||
@end
|
||||
|
||||
|
||||
void f(T *t) {
|
||||
[[]][[t foo] bar];
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// RUN: %clang_cc1 %s -verify -fsyntax-only -fdouble-square-bracket-attributes
|
||||
|
||||
int f() [[deprecated]]; // expected-note 2 {{'f' has been explicitly marked deprecated here}}
|
||||
void g() [[deprecated]];// expected-note {{'g' has been explicitly marked deprecated here}}
|
||||
void g();
|
||||
|
||||
extern int var [[deprecated]]; // expected-note 2 {{'var' has been explicitly marked deprecated here}}
|
||||
|
||||
int a() {
|
||||
int (*ptr)() = f; // expected-warning {{'f' is deprecated}}
|
||||
f(); // expected-warning {{'f' is deprecated}}
|
||||
|
||||
// test if attributes propagate to functions
|
||||
g(); // expected-warning {{'g' is deprecated}}
|
||||
|
||||
return var; // expected-warning {{'var' is deprecated}}
|
||||
}
|
||||
|
||||
// test if attributes propagate to variables
|
||||
extern int var;
|
||||
int w() {
|
||||
return var; // expected-warning {{'var' is deprecated}}
|
||||
}
|
||||
|
||||
int old_fn() [[deprecated]];// expected-note {{'old_fn' has been explicitly marked deprecated here}}
|
||||
int old_fn();
|
||||
int (*fn_ptr)() = old_fn; // expected-warning {{'old_fn' is deprecated}}
|
||||
|
||||
int old_fn() {
|
||||
return old_fn()+1; // no warning, deprecated functions can use deprecated symbols.
|
||||
}
|
||||
|
||||
struct foo {
|
||||
int x [[deprecated]]; // expected-note 3 {{'x' has been explicitly marked deprecated here}}
|
||||
};
|
||||
|
||||
void test1(struct foo *F) {
|
||||
++F->x; // expected-warning {{'x' is deprecated}}
|
||||
struct foo f1 = { .x = 17 }; // expected-warning {{'x' is deprecated}}
|
||||
struct foo f2 = { 17 }; // expected-warning {{'x' is deprecated}}
|
||||
}
|
||||
|
||||
typedef struct foo foo_dep [[deprecated]]; // expected-note {{'foo_dep' has been explicitly marked deprecated here}}
|
||||
foo_dep *test2; // expected-warning {{'foo_dep' is deprecated}}
|
||||
|
||||
struct [[deprecated, // expected-note {{'bar_dep' has been explicitly marked deprecated here}}
|
||||
invalid_attribute]] bar_dep ; // expected-warning {{unknown attribute 'invalid_attribute' ignored}}
|
||||
|
||||
struct bar_dep *test3; // expected-warning {{'bar_dep' is deprecated}}
|
||||
|
||||
[[deprecated("this is the message")]] int i; // expected-note {{'i' has been explicitly marked deprecated here}}
|
||||
void test4(void) {
|
||||
i = 12; // expected-warning {{'i' is deprecated: this is the message}}
|
||||
}
|
|
@ -58,7 +58,7 @@ public:
|
|||
|
||||
assert(V != "GCC" && "Given a GCC spelling, which means this hasn't been"
|
||||
"flattened!");
|
||||
if (V == "CXX11" || V == "Pragma")
|
||||
if (V == "CXX11" || V == "C2x" || V == "Pragma")
|
||||
NS = Spelling.getValueAsString("Namespace");
|
||||
bool Unset;
|
||||
K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset);
|
||||
|
@ -1326,7 +1326,7 @@ writePrettyPrintFunction(Record &R,
|
|||
if (Variety == "GNU") {
|
||||
Prefix = " __attribute__((";
|
||||
Suffix = "))";
|
||||
} else if (Variety == "CXX11") {
|
||||
} else if (Variety == "CXX11" || Variety == "C2x") {
|
||||
Prefix = " [[";
|
||||
Suffix = "]]";
|
||||
std::string Namespace = Spellings[I].nameSpace();
|
||||
|
@ -2716,10 +2716,14 @@ static void GenerateHasAttrSpellingStringSwitch(
|
|||
// If this is the C++11 variety, also add in the LangOpts test.
|
||||
if (Variety == "CXX11")
|
||||
Test += " && LangOpts.CPlusPlus11";
|
||||
else if (Variety == "C2x")
|
||||
Test += " && LangOpts.DoubleSquareBracketAttributes";
|
||||
} else if (Variety == "CXX11")
|
||||
// C++11 mode should be checked against LangOpts, which is presumed to be
|
||||
// present in the caller.
|
||||
Test = "LangOpts.CPlusPlus11";
|
||||
else if (Variety == "C2x")
|
||||
Test = "LangOpts.DoubleSquareBracketAttributes";
|
||||
|
||||
std::string TestStr =
|
||||
!Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1";
|
||||
|
@ -2740,7 +2744,7 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|||
// and declspecs. Then generate a big switch statement for each of them.
|
||||
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
||||
std::vector<Record *> Declspec, Microsoft, GNU, Pragma;
|
||||
std::map<std::string, std::vector<Record *>> CXX;
|
||||
std::map<std::string, std::vector<Record *>> CXX, C2x;
|
||||
|
||||
// Walk over the list of all attributes, and split them out based on the
|
||||
// spelling variety.
|
||||
|
@ -2756,6 +2760,8 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|||
Microsoft.push_back(R);
|
||||
else if (Variety == "CXX11")
|
||||
CXX[SI.nameSpace()].push_back(R);
|
||||
else if (Variety == "C2x")
|
||||
C2x[SI.nameSpace()].push_back(R);
|
||||
else if (Variety == "Pragma")
|
||||
Pragma.push_back(R);
|
||||
}
|
||||
|
@ -2775,20 +2781,25 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
|
|||
OS << "case AttrSyntax::Pragma:\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma");
|
||||
OS << "case AttrSyntax::CXX: {\n";
|
||||
// C++11-style attributes are further split out based on the Scope.
|
||||
for (auto I = CXX.cbegin(), E = CXX.cend(); I != E; ++I) {
|
||||
if (I != CXX.begin())
|
||||
OS << " else ";
|
||||
if (I->first.empty())
|
||||
OS << "if (!Scope || Scope->getName() == \"\") {\n";
|
||||
else
|
||||
OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first);
|
||||
OS << "}";
|
||||
}
|
||||
OS << "\n}\n";
|
||||
auto fn = [&OS](const char *Spelling, const char *Variety,
|
||||
const std::map<std::string, std::vector<Record *>> &List) {
|
||||
OS << "case AttrSyntax::" << Variety << ": {\n";
|
||||
// C++11-style attributes are further split out based on the Scope.
|
||||
for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) {
|
||||
if (I != List.cbegin())
|
||||
OS << " else ";
|
||||
if (I->first.empty())
|
||||
OS << "if (!Scope || Scope->getName() == \"\") {\n";
|
||||
else
|
||||
OS << "if (Scope->getName() == \"" << I->first << "\") {\n";
|
||||
OS << " return llvm::StringSwitch<int>(Name)\n";
|
||||
GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first);
|
||||
OS << "}";
|
||||
}
|
||||
OS << "\n}\n";
|
||||
};
|
||||
fn("CXX11", "CXX", CXX);
|
||||
fn("C2x", "C", C2x);
|
||||
OS << "}\n";
|
||||
}
|
||||
|
||||
|
@ -2809,10 +2820,11 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) {
|
|||
<< StringSwitch<unsigned>(Spellings[I].variety())
|
||||
.Case("GNU", 0)
|
||||
.Case("CXX11", 1)
|
||||
.Case("Declspec", 2)
|
||||
.Case("Microsoft", 3)
|
||||
.Case("Keyword", 4)
|
||||
.Case("Pragma", 5)
|
||||
.Case("C2x", 2)
|
||||
.Case("Declspec", 3)
|
||||
.Case("Microsoft", 4)
|
||||
.Case("Keyword", 5)
|
||||
.Case("Pragma", 6)
|
||||
.Default(0)
|
||||
<< " && Scope == \"" << Spellings[I].nameSpace() << "\")\n"
|
||||
<< " return " << I << ";\n";
|
||||
|
@ -3505,7 +3517,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
|
|||
|
||||
std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
|
||||
std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11,
|
||||
Keywords, Pragma;
|
||||
Keywords, Pragma, C2x;
|
||||
std::set<std::string> Seen;
|
||||
for (const auto *A : Attrs) {
|
||||
const Record &Attr = *A;
|
||||
|
@ -3543,6 +3555,10 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
|
|||
Matches = &CXX11;
|
||||
Spelling += S.nameSpace();
|
||||
Spelling += "::";
|
||||
} else if (Variety == "C2x") {
|
||||
Matches = &C2x;
|
||||
Spelling += S.nameSpace();
|
||||
Spelling += "::";
|
||||
} else if (Variety == "GNU")
|
||||
Matches = &GNU;
|
||||
else if (Variety == "Declspec")
|
||||
|
@ -3581,6 +3597,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
|
|||
StringMatcher("Name", Microsoft, OS).Emit();
|
||||
OS << " } else if (AttributeList::AS_CXX11 == Syntax) {\n";
|
||||
StringMatcher("Name", CXX11, OS).Emit();
|
||||
OS << " } else if (AttributeList::AS_C2x == Syntax) {\n";
|
||||
StringMatcher("Name", C2x, OS).Emit();
|
||||
OS << " } else if (AttributeList::AS_Keyword == Syntax || ";
|
||||
OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n";
|
||||
StringMatcher("Name", Keywords, OS).Emit();
|
||||
|
@ -3666,10 +3684,11 @@ static void WriteCategoryHeader(const Record *DocCategory,
|
|||
enum SpellingKind {
|
||||
GNU = 1 << 0,
|
||||
CXX11 = 1 << 1,
|
||||
Declspec = 1 << 2,
|
||||
Microsoft = 1 << 3,
|
||||
Keyword = 1 << 4,
|
||||
Pragma = 1 << 5
|
||||
C2x = 1 << 2,
|
||||
Declspec = 1 << 3,
|
||||
Microsoft = 1 << 4,
|
||||
Keyword = 1 << 5,
|
||||
Pragma = 1 << 6
|
||||
};
|
||||
|
||||
static void WriteDocumentation(RecordKeeper &Records,
|
||||
|
@ -3716,6 +3735,7 @@ static void WriteDocumentation(RecordKeeper &Records,
|
|||
SpellingKind Kind = StringSwitch<SpellingKind>(I.variety())
|
||||
.Case("GNU", GNU)
|
||||
.Case("CXX11", CXX11)
|
||||
.Case("C2x", C2x)
|
||||
.Case("Declspec", Declspec)
|
||||
.Case("Microsoft", Microsoft)
|
||||
.Case("Keyword", Keyword)
|
||||
|
@ -3725,7 +3745,7 @@ static void WriteDocumentation(RecordKeeper &Records,
|
|||
SupportedSpellings |= Kind;
|
||||
|
||||
std::string Name;
|
||||
if (Kind == CXX11 && !I.nameSpace().empty())
|
||||
if ((Kind == CXX11 || Kind == C2x) && !I.nameSpace().empty())
|
||||
Name = I.nameSpace() + "::";
|
||||
Name += I.name();
|
||||
|
||||
|
@ -3754,13 +3774,15 @@ static void WriteDocumentation(RecordKeeper &Records,
|
|||
|
||||
// List what spelling syntaxes the attribute supports.
|
||||
OS << ".. csv-table:: Supported Syntaxes\n";
|
||||
OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\",";
|
||||
OS << " :header: \"GNU\", \"C++11\", \"C2x\", \"__declspec\", \"Keyword\",";
|
||||
OS << " \"Pragma\", \"Pragma clang attribute\"\n\n";
|
||||
OS << " \"";
|
||||
if (SupportedSpellings & GNU) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & CXX11) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & C2x) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & Declspec) OS << "X";
|
||||
OS << "\",\"";
|
||||
if (SupportedSpellings & Keyword) OS << "X";
|
||||
|
|
Loading…
Reference in New Issue