]> granicus.if.org Git - clang/commitdiff
Add -f[no-]double-square-bracket-attributes as new driver options to control use...
authorAaron Ballman <aaron@aaronballman.com>
Sun, 15 Oct 2017 15:01:42 +0000 (15:01 +0000)
committerAaron Ballman <aaron@aaronballman.com>
Sun, 15 Oct 2017 15:01:42 +0000 (15:01 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@315856 91177308-0d34-0410-b5e6-96231b3b80d8

16 files changed:
include/clang/Basic/Attr.td
include/clang/Basic/Attributes.h
include/clang/Basic/LangOptions.def
include/clang/Driver/Options.td
include/clang/Parse/Parser.h
include/clang/Sema/AttributeList.h
lib/Frontend/CompilerInvocation.cpp
lib/Lex/Lexer.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/AttributeList.cpp
test/Misc/ast-dump-c-attr.c [new file with mode: 0644]
test/Parser/c2x-attributes.c [new file with mode: 0644]
test/Parser/c2x-attributes.m [new file with mode: 0644]
test/Sema/attr-deprecated-c2x.c [new file with mode: 0644]
utils/TableGen/ClangAttrEmitter.cpp

index 17d2be286111729b21c32bf7e1097aa06d329033..493b988031076cb140c5951c3b95b3422546c8ac 100644 (file)
@@ -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.
index ea9e28ae681ae742d542bb0440e48fcd04cb15df..c651abacd482bbe60cf643f1454ce2cc29b7ef15 100644 (file)
@@ -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
 };
index 9b9729654dab1417cf3899d15638df2636c415e5..78a743750e7b684af100859fdaec26db2caf7545 100644 (file)
@@ -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")
index a18366ed79a1ca6b31fcc5c27e13c7f619bbd2db..74d9f5ad5dda927e430409e27eb37da587808ddc 100644 (file)
@@ -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]>,
index 7a71ee3cb48e4ea656814df174ccd3a71218dd7b..826114bc43d6ea3123dd0b15a5042546d7526765 100644 (file)
@@ -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,
index 0383c2c1995dc7ace470219a6ad99a31994c84e7..0580ff88506c65e331547cffcdaaf7ee8a080189 100644 (file)
@@ -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;
   }
index 7114e654dab834ffc1928cd5aa3683cb63461527..9a4fc774f47685fdd5b78a52814762212d29cd6b 100644 (file)
@@ -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);
index b85e0f03dc06727042ab1f77ed59ce3798505948..5132d0e62c26bd13b56c8fdc47c9735d69ea96ec 100644 (file)
@@ -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 {
index 21cdc62a1861b97d003276c1026daf37a854bac8..cc032c8cf3fb946fb2cd81df2a0be6660bf13abe 100644 (file)
@@ -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);
     }
   }
 
index 25bb053afc2dd2ec5ad5d613125b0b27c838c30d..ada67e84d2136c370509a4d1c6ae373095f5d681 100644 (file)
@@ -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)
index 724db456785fb69c2ca9da66abcd87cac81c8238..14d334746f1fdf04fbedc60d108b9b2bc26b1b72 100644 (file)
@@ -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;
 
diff --git a/test/Misc/ast-dump-c-attr.c b/test/Misc/ast-dump-c-attr.c
new file mode 100644 (file)
index 0000000..701df78
--- /dev/null
@@ -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\r
+\r
+int Test1 [[deprecated]];\r
+// CHECK:      VarDecl{{.*}}Test1\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:13> "" ""\r
+\r
+enum [[deprecated("Frobble")]] Test2 {\r
+  Test3 [[deprecated]]\r
+};\r
+// CHECK:      EnumDecl{{.*}}Test2\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:8, col:28> "Frobble" ""\r
+// CHECK-NEXT:   EnumConstantDecl{{.*}}Test3\r
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:11> "" ""\r
+\r
+struct [[deprecated]] Test4 {\r
+  [[deprecated("Frobble")]] int Test5, Test6;\r
+  int Test7 [[deprecated]] : 12;\r
+};\r
+// CHECK:      RecordDecl{{.*}}Test4\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""\r
+// CHECK-NEXT:   FieldDecl{{.*}}Test5\r
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""\r
+// CHECK-NEXT:   FieldDecl{{.*}}Test6\r
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:5, col:25> "Frobble" ""\r
+// CHECK-NEXT:   FieldDecl{{.*}}Test7\r
+// CHECK-NEXT:     IntegerLiteral{{.*}}'int' 12\r
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""\r
+\r
+struct [[deprecated]] Test8;\r
+// CHECK:      RecordDecl{{.*}}Test8\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:10> "" ""\r
+\r
+[[deprecated]] void Test9(int Test10 [[deprecated]]);\r
+// CHECK:      FunctionDecl{{.*}}Test9\r
+// CHECK-NEXT:   ParmVarDecl{{.*}}Test10\r
+// CHECK-NEXT:     DeprecatedAttr 0x{{[^ ]*}} <col:40> "" ""\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:3> "" ""\r
+\r
+void Test11 [[deprecated]](void);\r
+// CHECK:      FunctionDecl{{.*}}Test11\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:15> "" ""\r
+\r
+void Test12(void) [[deprecated]] {}\r
+// CHECK:      FunctionDecl{{.*}}Test12\r
+// CHECK-NEXT:   CompoundStmt\r
+// CHECK-NEXT:   DeprecatedAttr 0x{{[^ ]*}} <col:21> "" ""\r
diff --git a/test/Parser/c2x-attributes.c b/test/Parser/c2x-attributes.c
new file mode 100644 (file)
index 0000000..1be69f1
--- /dev/null
@@ -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}}
diff --git a/test/Parser/c2x-attributes.m b/test/Parser/c2x-attributes.m
new file mode 100644 (file)
index 0000000..f461041
--- /dev/null
@@ -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];
+}
diff --git a/test/Sema/attr-deprecated-c2x.c b/test/Sema/attr-deprecated-c2x.c
new file mode 100644 (file)
index 0000000..2505f12
--- /dev/null
@@ -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}}
+}
index abbef617bb1ddc5e0294494fbb8d7701a59510ae..015cc4dd1fcb7cb31fdcd5756a2a7f4978a26af4 100644 (file)
@@ -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";