From: Aaron Ballman Date: Wed, 9 Mar 2016 16:48:08 +0000 (+0000) Subject: Implement support for [[maybe_unused]] in C++1z that is based off existing support... X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0bcd6c1b18d23e0d47d0a9bbc8084547faa9786f;p=platform%2Fupstream%2Fllvm.git Implement support for [[maybe_unused]] in C++1z that is based off existing support for unused, and treat it as an extension pre-C++1z. This also means extending the existing unused attribute so that it can be placed on an enum and enumerator, in addition to the other subjects. llvm-svn: 263025 --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 21078e0..f50cb29 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1484,11 +1484,11 @@ def ObjCRequiresPropertyDefs : InheritableAttr { } def Unused : InheritableAttr { - let Spellings = [GCC<"unused">]; - let Subjects = SubjectList<[Var, ObjCIvar, Type, Label, Field, ObjCMethod, - FunctionLike], WarnDiag, - "ExpectedVariableFunctionOrLabel">; - let Documentation = [Undocumented]; + let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">]; + let Subjects = SubjectList<[Var, ObjCIvar, Type, Enum, EnumConstant, Label, + Field, ObjCMethod, FunctionLike], WarnDiag, + "ExpectedForMaybeUnused">; + let Documentation = [WarnMaybeUnusedDocs]; } def Used : InheritableAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2bf8a82..70a2e5b 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -729,6 +729,32 @@ When one method overrides another, the overriding method can be more widely avai }]; } +def WarnMaybeUnusedDocs : Documentation { + let Category = DocCatVariable; + let Heading = "maybe_unused, unused, gnu::unused"; + let Content = [{ +When passing the ``-Wunused`` flag to Clang, entities that are unused by the +program may be diagnosed. The ``[[maybe_unused]]`` (or +``__attribute__((unused))``) attribute can be used to silence such diagnostics +when the entity cannot be removed. For instance, a local variable may exist +solely for use in an ``assert()`` statement, which makes the local variable +unused when ``NDEBUG`` is defined. + +The attribute may be applied to the declaration of a class, a typedef, a +variable, a function or method, a function parameter, an enumeration, an +enumerator, a non-static data member, or a label. + +.. code-block: c++ + #include + + [[maybe_unused]] void f([[maybe_unused]] bool thing1, + [[maybe_unused]] bool thing2) { + [[maybe_unused]] bool b = thing1 && thing2; + assert(b); + } + }]; +} + def WarnUnusedResultsDocs : Documentation { let Category = DocCatFunction; let Heading = "nodiscard, warn_unused_result, clang::warn_unused_result, gnu::warn_unused_result"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 45de16b..239144d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2468,8 +2468,7 @@ def warn_attribute_wrong_decl_type : Warning< "variables and functions|functions and methods|parameters|" "functions, methods and blocks|functions, methods, and classes|" "functions, methods, and parameters|classes|enums|variables|methods|" - "variables, functions and labels|fields and global variables|structs|" - "variables and typedefs|thread-local variables|" + "fields and global variables|structs|variables and typedefs|thread-local variables|" "variables and fields|variables, data members and tag types|" "types and namespaces|Objective-C interfaces|methods and properties|" "struct or union|struct, union or class|types|" @@ -2478,7 +2477,8 @@ def warn_attribute_wrong_decl_type : Warning< "functions and global variables|structs, unions, and typedefs|structs and typedefs|" "interface or protocol declarations|kernel functions|non-K&R-style functions|" "variables, enums, fields and typedefs|functions, methods, enums, and classes|" - "structs, classes, variables, functions, and inline namespaces}1">, + "structs, classes, variables, functions, and inline namespaces|" + "variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index a3e656e..c59f3b9 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -871,7 +871,6 @@ enum AttributeDeclKind { ExpectedEnum, ExpectedVariable, ExpectedMethod, - ExpectedVariableFunctionOrLabel, ExpectedFieldOrGlobalVar, ExpectedStruct, ExpectedVariableOrTypedef, @@ -896,7 +895,8 @@ enum AttributeDeclKind { ExpectedFunctionWithProtoType, ExpectedVariableEnumFieldOrTypedef, ExpectedFunctionMethodEnumOrClass, - ExpectedStructClassVariableFunctionOrInlineNamespace + ExpectedStructClassVariableFunctionOrInlineNamespace, + ExpectedForMaybeUnused }; } // end namespace clang diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 354e405..8a28f65 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3637,6 +3637,8 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName, return true; case AttributeList::AT_WarnUnusedResult: return !ScopeName && AttrName->getName().equals("nodiscard"); + case AttributeList::AT_Unused: + return !ScopeName && AttrName->getName().equals("maybe_unused"); default: return false; } diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 4b7aa2b..7f523c4 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -459,7 +459,8 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, if (VD->isUsed()) Diag(PragmaLoc, diag::warn_used_but_marked_unused) << Name; - VD->addAttr(UnusedAttr::CreateImplicit(Context, IdTok.getLocation())); + VD->addAttr(UnusedAttr::CreateImplicit(Context, UnusedAttr::GNU_unused, + IdTok.getLocation())); } void Sema::AddCFAuditedAttribute(Decl *D) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f8cec75..0cae52a 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1807,6 +1807,28 @@ static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } +static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + bool IsCXX1zAttr = Attr.isCXX11Attribute() && !Attr.getScopeName(); + + if (IsCXX1zAttr && isa(D)) { + // The C++1z spelling of this attribute cannot be applied to a static data + // member per [dcl.attr.unused]p2. + if (cast(D)->isStaticDataMember()) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedForMaybeUnused; + return; + } + } + + // If this is spelled as the standard C++1z attribute, but not in C++1z, warn + // about using it as an extension. + if (!S.getLangOpts().CPlusPlus1z && IsCXX1zAttr) + S.Diag(Attr.getLoc(), diag::ext_cxx1z_attr) << Attr.getName(); + + D->addAttr(::new (S.Context) UnusedAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +} + static void handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { uint32_t priority = ConstructorAttr::DefaultPriority; if (Attr.getNumArgs() && @@ -5545,7 +5567,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleSimpleAttribute(S, D, Attr); break; case AttributeList::AT_Unused: - handleSimpleAttribute(S, D, Attr); + handleUnusedAttr(S, D, Attr); break; case AttributeList::AT_ReturnsTwice: handleSimpleAttribute(S, D, Attr); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4b88175..54dcced 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -76,10 +76,14 @@ bool Sema::CanUseDecl(NamedDecl *D) { static void DiagnoseUnusedOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc) { // Warn if this is used but marked unused. - if (D->hasAttr()) { - const Decl *DC = cast_or_null(S.getCurObjCLexicalContext()); - if (DC && !DC->hasAttr()) - S.Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName(); + if (const auto *A = D->getAttr()) { + // [[maybe_unused]] should not diagnose uses, but __attribute__((unused)) + // should diagnose them. + if (A->getSemanticSpelling() != UnusedAttr::CXX11_maybe_unused) { + const Decl *DC = cast_or_null(S.getCurObjCLexicalContext()); + if (DC && !DC->hasAttr()) + S.Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName(); + } } } diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p1.cpp new file mode 100644 index 0000000..8da2ca7 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p1.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused -std=c++1z -verify %s + +struct [[maybe_unused]] S1 {}; // ok +struct [[maybe_unused maybe_unused]] S2 {}; // expected-error {{attribute 'maybe_unused' cannot appear multiple times in an attribute specifier}} +struct [[maybe_unused("Wrong")]] S3 {}; // expected-error {{'maybe_unused' cannot have an argument list}} diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p2.cpp new file mode 100644 index 0000000..b539ca4 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p2.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused -std=c++1z -verify %s + +struct [[maybe_unused]] S { + int I [[maybe_unused]]; + static int SI [[maybe_unused]]; // expected-warning {{'maybe_unused' attribute only applies to variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members}} +}; + +enum [[maybe_unused]] E1 { + EnumVal [[maybe_unused]] +}; + +[[maybe_unused]] void unused_func([[maybe_unused]] int parm) { + typedef int maybe_unused_int [[maybe_unused]]; + [[maybe_unused]] int I; +} + +namespace [[maybe_unused]] N {} // expected-warning {{'maybe_unused' attribute only applies to}} diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp new file mode 100644 index 0000000..a627d83 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused -Wused-but-marked-unused -std=c++1z -Wc++1z-extensions -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wunused -Wused-but-marked-unused -std=c++11 -Wc++1z-extensions -verify -DEXT %s + +static_assert(__has_cpp_attribute(maybe_unused) == 201603, ""); + +struct [[maybe_unused]] S {}; + +void f() { + int x; // expected-warning {{unused variable}} + typedef int I; // expected-warning {{unused typedef 'I'}} + + // Should not warn about these due to not being used. + [[maybe_unused]] int y; + typedef int maybe_unused_int [[maybe_unused]]; + + // Should not warn about these uses. + S s; + maybe_unused_int test; + y = 12; +} + +#ifdef EXT +// expected-warning@6 {{use of the 'maybe_unused' attribute is a C++1z extension}} +// expected-warning@13 {{use of the 'maybe_unused' attribute is a C++1z extension}} +// expected-warning@14 {{use of the 'maybe_unused' attribute is a C++1z extension}} +#endif diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p4.cpp new file mode 100644 index 0000000..d4a2759 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p4.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused -std=c++1z -verify %s +// expected-no-diagnostics + +void f(); +[[maybe_unused]] void f(); + +void f() { +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 5163402..fc354b2 100644 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -639,7 +639,7 @@ as the draft C++1z standard evolves.

[[maybe_unused]] attribute P0212R1 - No + SVN Aggregate initialization of classes with base classes