From 1caab743906f2fccfe1ec4250a4ff2e7cab567b9 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 14 Apr 2014 16:03:22 +0000 Subject: [PATCH] Properly diagnose standard C++ attributes which have optional argument lists when the arguments are elided. eg) [[deprecated()]] // error [[deprecated]] // OK [[deprecated("")]] // OK [[gnu::deprecated()]] // OK git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@206186 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticParseKinds.td | 4 ++- include/clang/Parse/Parser.h | 14 ++++---- lib/Parse/ParseDecl.cpp | 6 ++-- lib/Parse/ParseDeclCXX.cpp | 38 ++++++++++++++++----- test/Parser/attributes.c | 3 +- test/Parser/cxx0x-attributes.cpp | 7 ++++ 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 04f4e5e1fc..86078027df 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -526,7 +526,9 @@ def warn_cxx98_compat_attribute : Warning< "C++11 attribute syntax is incompatible with C++98">, InGroup, DefaultIgnore; def err_cxx11_attribute_forbids_arguments : Error< - "attribute '%0' cannot have an argument list">; + "attribute %0 cannot have an argument list">; +def err_attribute_requires_arguements : Error< + "attribute %0 requires a nonempty argument list">; def err_cxx11_attribute_forbids_ellipsis : Error< "attribute '%0' cannot be used as an attribute pack">; def err_cxx11_attribute_repeated : Error< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 2d71a0403d..5c72b91bde 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -2012,13 +2012,13 @@ private: /// \brief Parses syntax-generic attribute arguments for attributes which are /// known to the implementation, and adds them to the given ParsedAttributes - /// list with the given attribute syntax. - void ParseAttributeArgsCommon(IdentifierInfo *AttrName, - SourceLocation AttrNameLoc, - ParsedAttributes &Attrs, SourceLocation *EndLoc, - IdentifierInfo *ScopeName, - SourceLocation ScopeLoc, - AttributeList::Syntax Syntax); + /// list with the given attribute syntax. Returns the number of arguments + /// parsed for the attribute. + unsigned + ParseAttributeArgsCommon(IdentifierInfo *AttrName, SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, SourceLocation *EndLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, + AttributeList::Syntax Syntax); void MaybeParseGNUAttributes(Declarator &D, LateParsedAttrList *LateAttrs = 0) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 331c5e42fd..720a68c5ba 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -261,7 +261,7 @@ void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName, 0, AttrNameLoc, 0, 0, AttributeList::AS_GNU); } -void Parser::ParseAttributeArgsCommon( +unsigned Parser::ParseAttributeArgsCommon( IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, AttributeList::Syntax Syntax) { @@ -302,7 +302,7 @@ void Parser::ParseAttributeArgsCommon( ExprResult ArgExpr(ParseAssignmentExpression()); if (ArgExpr.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); - return; + return 0; } ArgExprs.push_back(ArgExpr.release()); // Eat the comma, move to the next argument @@ -318,6 +318,8 @@ void Parser::ParseAttributeArgsCommon( if (EndLoc) *EndLoc = RParen; + + return static_cast(ArgExprs.size()); } /// Parse the arguments to a parameterized GNU attribute or diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index a04b33c627..94d46eff1d 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -3194,6 +3194,7 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName, switch (AttributeList::getKind(AttrName, ScopeName, AttributeList::AS_CXX11)) { case AttributeList::AT_CarriesDependency: + case AttributeList::AT_Deprecated: case AttributeList::AT_FallThrough: case AttributeList::AT_CXX11NoReturn: { return true; @@ -3225,6 +3226,7 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, IdentifierInfo *ScopeName, SourceLocation ScopeLoc) { assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list"); + SourceLocation LParenLoc = Tok.getLocation(); // If the attribute isn't known, we will not attempt to parse any // arguments. @@ -3241,9 +3243,32 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, // behaviors. ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, AttributeList::AS_CXX11, 0); - else - ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, - ScopeLoc, AttributeList::AS_CXX11); + else { + unsigned NumArgs = + ParseAttributeArgsCommon(AttrName, AttrNameLoc, Attrs, EndLoc, + ScopeName, ScopeLoc, AttributeList::AS_CXX11); + + const AttributeList *Attr = Attrs.getList(); + if (Attr && IsBuiltInOrStandardCXX11Attribute(AttrName, ScopeName)) { + // If the attribute is a standard or built-in attribute and we are + // parsing an argument list, we need to determine whether this attribute + // was allowed to have an argument list (such as [[deprecated]]), and how + // many arguments were parsed (so we can diagnose on [[deprecated()]]). + if (Attr->getMaxArgs() && !NumArgs) { + // The attribute was allowed to have arguments, but none were provided + // even though the attribute parsed successfully. This is an error. + Diag(LParenLoc, diag::err_attribute_requires_arguements) << AttrName; + return false; + } else if (!Attr->getMaxArgs()) { + // The attribute parsed successfully, but was not allowed to have any + // arguments. It doesn't matter whether any were provided -- the + // presence of the argument list (even if empty) is diagnosed. + Diag(LParenLoc, diag::err_cxx11_attribute_forbids_arguments) + << AttrName; + return false; + } + } + } return true; } @@ -3324,14 +3349,9 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, << AttrName << SourceRange(SeenAttrs[AttrName]); // Parse attribute arguments - if (Tok.is(tok::l_paren)) { - if (StandardAttr) - Diag(Tok.getLocation(), diag::err_cxx11_attribute_forbids_arguments) - << AttrName->getName(); - + if (Tok.is(tok::l_paren)) AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, attrs, endLoc, ScopeName, ScopeLoc); - } if (!AttrParsed) attrs.addNew(AttrName, diff --git a/test/Parser/attributes.c b/test/Parser/attributes.c index 8d7e5fe503..3d69c72c32 100644 --- a/test/Parser/attributes.c +++ b/test/Parser/attributes.c @@ -94,5 +94,4 @@ void testFundef5() __attribute__(()) { } __attribute__((pure)) int testFundef6(int a) { return a; } - - +void deprecatedTestFun(void) __attribute__((deprecated())); diff --git a/test/Parser/cxx0x-attributes.cpp b/test/Parser/cxx0x-attributes.cpp index 1b9e477004..777a40a4e8 100644 --- a/test/Parser/cxx0x-attributes.cpp +++ b/test/Parser/cxx0x-attributes.cpp @@ -322,3 +322,10 @@ namespace GccASan { [[gnu::no_address_safety_analysis]] void f3(); [[gnu::no_sanitize_address]] void f4(); } + +namespace { + [[deprecated]] void bar(); + [[deprecated("hello")]] void baz(); + [[deprecated()]] void foo(); // expected-error {{attribute 'deprecated' requires a nonempty argument list}} + [[gnu::deprecated()]] void quux(); +} -- 2.40.0