Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -137,6 +137,9 @@ def warn_unknown_attribute_ignored : Warning< "unknown attribute %0 ignored">, InGroup; +def warn_unknown_attribute_namespace_ignored : Warning< + warn_unknown_attribute_ignored.Text>, InGroup; + def err_use_of_tag_name_without_tag : Error< "must use '%1' tag to refer to type %0%select{| in this scope}2">; Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -572,7 +572,9 @@ def NSReturnsMismatch : DiagGroup<"nsreturns-mismatch">; def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">; -def UnknownAttributes : DiagGroup<"unknown-attributes">; +def UnknownAttributeNamespaces : DiagGroup<"unknown-attribute-namespaces">; +def UnknownAttributes + : DiagGroup<"unknown-attributes", [UnknownAttributeNamespaces]>; def IgnoredAttributes : DiagGroup<"ignored-attributes">; def Attributes : DiagGroup<"attributes", [UnknownAttributes, IgnoredAttributes]>; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2983,6 +2983,10 @@ bool diagnoseArgIndependentDiagnoseIfAttrs(const NamedDecl *ND, SourceLocation Loc); + /// Emits a diagnostic about an attribute that is unknown. + void diagnoseUnknownAttribute(const ParsedAttr &A); + + /// Returns whether the given function's address can be taken or not, /// optionally emitting a diagnostic if the address can't be taken. /// Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -1633,7 +1633,7 @@ if (!AL.isCXX11Attribute() && !AL.isC2xAttribute()) continue; if (AL.getKind() == ParsedAttr::UnknownAttribute) - Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored) << AL.getName(); + Actions.diagnoseUnknownAttribute(AL); else { Diag(AL.getLoc(), DiagID) << AL.getName(); AL.setInvalid(); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/MathExtras.h" using namespace clang; @@ -2067,7 +2068,7 @@ bool Sema::CheckAttrTarget(const ParsedAttr &AL) { // Check whether the attribute is valid on the current target. if (!AL.existsInTarget(Context.getTargetInfo())) { - Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored) << AL; + diagnoseUnknownAttribute(AL); AL.setInvalid(); return true; } @@ -6585,11 +6586,7 @@ // though they were unknown attributes. if (AL.getKind() == ParsedAttr::UnknownAttribute || !AL.existsInTarget(S.Context.getTargetInfo())) { - S.Diag(AL.getLoc(), - AL.isDeclspecAttribute() - ? (unsigned)diag::warn_unhandled_ms_attribute_ignored - : (unsigned)diag::warn_unknown_attribute_ignored) - << AL; + S.diagnoseUnknownAttribute(AL); return; } @@ -7412,13 +7409,11 @@ if (AL.getKind() == ParsedAttr::IgnoredAttribute) continue; - if (AL.getKind() == ParsedAttr::UnknownAttribute) { - S.Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored) + if (AL.getKind() == ParsedAttr::UnknownAttribute) + S.diagnoseUnknownAttribute(AL); + else + S.Diag(AL.getLoc(), diag::warn_attribute_not_on_decl) << AL << AL.getRange(); - } else { - S.Diag(AL.getLoc(), diag::warn_attribute_not_on_decl) << AL - << AL.getRange(); - } } } @@ -8617,3 +8612,27 @@ EmitAvailabilityWarning(*this, Result, D, OffendingDecl, Message, Locs, UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); } + +void Sema::diagnoseUnknownAttribute(const ParsedAttr &A) { + // We emit a few different "unknown attribute" diagnostics, depending on the + // circumstances, such as a scoped attribute with an unknown namespace, etc. + unsigned Warning = diag::warn_unknown_attribute_ignored; + if (A.isDeclspecAttribute()) { + Warning = diag::warn_unhandled_ms_attribute_ignored; + } else if (A.hasScope()) { + if (llvm::StringSwitch(A.getScopeName()->getName()) +#define ATTR(A) +#define ATTR_NAMESPACE(A) .Case(#A, false) +#include "clang/Basic/AttrList.inc" + .Default(true)) + Warning = diag::warn_unknown_attribute_namespace_ignored; + } else if (A.isC2xAttribute() || A.isCXX11Attribute()) { + // We use the namespaced attribute warning for a C2x or C++11 attribute + // without a scope. This ensures that a user who does + // -Wno-unknown-attributes -Wunknown-attribute-namespaces still gets a + // diagnostic for attributes that could have a scope but do not (e.g., + // [[unknown_attribute]]). + Warning = diag::warn_unknown_attribute_namespace_ignored; + } + Diag(A.getLoc(), Warning) << A << A.getRange(); +} Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -2348,10 +2348,10 @@ for (const ParsedAttr &AL : Attributes) { if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute) continue; - Diag(AL.getLoc(), AL.getKind() == ParsedAttr::UnknownAttribute - ? (unsigned)diag::warn_unknown_attribute_ignored - : (unsigned)diag::err_base_specifier_attribute) - << AL.getName(); + if (AL.getKind() == ParsedAttr::UnknownAttribute) + diagnoseUnknownAttribute(AL); + else + Diag(AL.getLoc(), diag::err_base_specifier_attribute) << AL; } TypeSourceInfo *TInfo = nullptr; Index: lib/Sema/SemaStmtAttr.cpp =================================================================== --- lib/Sema/SemaStmtAttr.cpp +++ lib/Sema/SemaStmtAttr.cpp @@ -329,10 +329,7 @@ SourceRange Range) { switch (A.getKind()) { case ParsedAttr::UnknownAttribute: - S.Diag(A.getLoc(), A.isDeclspecAttribute() - ? (unsigned)diag::warn_unhandled_ms_attribute_ignored - : (unsigned)diag::warn_unknown_attribute_ignored) - << A.getName(); + S.diagnoseUnknownAttribute(A); return nullptr; case ParsedAttr::AT_FallThrough: return handleFallThroughAttr(S, St, A, Range); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -7441,9 +7441,7 @@ case ParsedAttr::UnknownAttribute: if (attr.isCXX11Attribute() && TAL == TAL_DeclChunk) - state.getSema().Diag(attr.getLoc(), - diag::warn_unknown_attribute_ignored) - << attr.getName(); + state.getSema().diagnoseUnknownAttribute(attr); break; case ParsedAttr::IgnoredAttribute: Index: test/SemaCXX/attr-cxx0x.cpp =================================================================== --- test/SemaCXX/attr-cxx0x.cpp +++ test/SemaCXX/attr-cxx0x.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -pedantic -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -pedantic -DUNKNOWN_ATTRS -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -pedantic -Wno-unknown-attributes -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -pedantic -Wno-unknown-attributes -Wunknown-attribute-namespaces -DUNKNOWN_NAMESPACE -std=c++11 %s int align_illegal alignas(3); //expected-error {{requested alignment is not a power of 2}} char align_big alignas(int); @@ -46,7 +48,25 @@ static_assert(alignof(int(int)) >= 1, "alignof(function) not positive"); // expected-error{{invalid application of 'alignof' to a function type}} -[[__carries_dependency__]] // expected-warning{{unknown attribute '__carries_dependency__' ignored}} +#if defined(UNKNOWN_ATTRS) || defined(UNKNOWN_NAMESPACE) +// expected-warning@+2 {{unknown attribute '__carries_dependency__' ignored}} +#endif +[[__carries_dependency__]] void func(void); alignas(4) auto PR19252 = 0; + +#if defined(UNKNOWN_ATTRS) || defined(UNKNOWN_NAMESPACE) +// expected-warning@+2 {{unknown attribute 'frobble' ignored}} +#endif +[[frobble]] int unknown1; + +#if defined(UNKNOWN_ATTRS) || defined(UNKNOWN_NAMESPACE) +// expected-warning@+2 {{unknown attribute 'bobble' ignored}} +#endif +[[frobble::bobble]] int unknown2; + +#ifdef UNKNOWN_ATTRS +// expected-warning@+2 {{unknown attribute 'unknown_attribute' ignored}} +#endif +[[gsl::unknown_attribute]] int unknown3; Index: utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- utils/TableGen/ClangAttrEmitter.cpp +++ utils/TableGen/ClangAttrEmitter.cpp @@ -17,7 +17,6 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/ErrorHandling.h" @@ -2551,6 +2550,16 @@ ::emitAttrList(OS, Descriptor.MacroName, Attrs); } + void addAttrNamespacesTo(std::set &S) const { + for (const Record *R : Attrs) { + std::vector Spellings = GetFlattenedSpellings(*R); + for (const auto &Spell : Spellings) { + if (!Spell.nameSpace().empty()) + S.insert(Spell.nameSpace()); + } + } + } + void classifyAttrOnRoot(Record *Attr) { bool result = classifyAttr(Attr); assert(result && "failed to classify on root"); (void) result; @@ -2621,6 +2630,19 @@ #endif } + void emitAttrNamespaces(raw_ostream &OS) const { + // Gather the unique set of attribute namespaces Clang knows about. We + // want this set to be ordered so we can do efficient lookups. + std::set Namespaces; + for (const auto &Class : Classes) { + Class->addAttrNamespacesTo(Namespaces); + } + // Emit the list of namespaces. + for (const auto &Namespace : Namespaces) { + OS << "ATTR_NAMESPACE(" << Namespace << ")\n"; + } + } + void emitDefaultDefines(raw_ostream &OS) const { for (auto &Class : Classes) { Class->emitDefaultDefines(OS); @@ -2682,6 +2704,7 @@ // Add defaulting macro definitions. Hierarchy.emitDefaultDefines(OS); emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr); + emitDefaultDefine(OS, "ATTR_NAMESPACE", nullptr); std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector PragmaAttrs; @@ -2702,6 +2725,7 @@ // Emit the ad hoc groups. emitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); + Hierarchy.emitAttrNamespaces(OS); // Emit the attribute ranges. OS << "#ifdef ATTR_RANGE\n"; @@ -2711,6 +2735,7 @@ Hierarchy.emitUndefs(OS); OS << "#undef PRAGMA_SPELLING_ATTR\n"; + OS << "#undef ATTR_NAMESPACE\n"; } // Emits the enumeration list for attributes.