[clang] [Clang] raise extension warning for unknown namespaced attributes (PR #120925)
Oleksandr T. via cfe-commits
cfe-commits at lists.llvm.org
Sat May 3 15:18:08 PDT 2025
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/120925
>From 9bb85586f40f659a8bff414511bed9592083efbd Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sun, 4 May 2025 01:13:20 +0300
Subject: [PATCH 1/2] [Clang] raise extension warning for unknown namespaced
attributes
---
clang/docs/ReleaseNotes.rst | 3 +
.../include/clang/Basic/AttributeCommonInfo.h | 11 ++-
clang/include/clang/Basic/Attributes.h | 7 +-
clang/include/clang/Basic/CMakeLists.txt | 6 ++
.../clang/Basic/DiagnosticCommonKinds.td | 8 ++
.../include/clang/Basic/SimpleTypoCorrector.h | 31 +++++++
clang/lib/AST/CommentSema.cpp | 90 ++++++-------------
clang/lib/Basic/Attributes.cpp | 58 ++++++++++--
clang/lib/Basic/CMakeLists.txt | 1 +
clang/lib/Basic/SimpleTypoCorrector.cpp | 47 ++++++++++
clang/lib/Sema/SemaDeclAttr.cpp | 38 ++++++--
clang/test/CXX/module/module.interface/p3.cpp | 2 +-
clang/test/Parser/c2x-attributes.c | 2 +-
clang/test/Parser/cxx0x-attributes.cpp | 6 +-
clang/test/Sema/unknown-attributes.c | 27 ++++++
clang/utils/TableGen/ClangAttrEmitter.cpp | 32 +++++++
clang/utils/TableGen/TableGen.cpp | 6 ++
clang/utils/TableGen/TableGenBackends.h | 2 +
18 files changed, 289 insertions(+), 88 deletions(-)
create mode 100644 clang/include/clang/Basic/SimpleTypoCorrector.h
create mode 100644 clang/lib/Basic/SimpleTypoCorrector.cpp
create mode 100644 clang/test/Sema/unknown-attributes.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bc68bb8b70b3d..7679adb3c022b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -333,6 +333,9 @@ related warnings within the method body.
- Clang now disallows the use of attributes applied before an
``extern template`` declaration (#GH79893).
+- Clang now diagnoses unknown attribute namespaces and
+ provides typo correction for unrecognized namespace and attribute names (#GH120875).
+
Improvements to Clang's diagnostics
-----------------------------------
diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h
index 4af5a8fd1852c..45f3d241cca32 100644
--- a/clang/include/clang/Basic/AttributeCommonInfo.h
+++ b/clang/include/clang/Basic/AttributeCommonInfo.h
@@ -16,11 +16,12 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TokenKinds.h"
-
namespace clang {
class ASTRecordWriter;
class IdentifierInfo;
+class LangOptions;
+class TargetInfo;
class AttributeCommonInfo {
public:
@@ -67,7 +68,7 @@ class AttributeCommonInfo {
IgnoredAttribute,
UnknownAttribute,
};
- enum class Scope { NONE, CLANG, GNU, MSVC, OMP, HLSL, GSL, RISCV };
+ enum class Scope { NONE, CLANG, GNU, MSVC, OMP, HLSL, GSL, RISCV, UNKNOWN };
enum class AttrArgsInfo {
None,
Optional,
@@ -234,6 +235,12 @@ class AttributeCommonInfo {
return SyntaxUsed == AS_ContextSensitiveKeyword;
}
+ bool isUnknownScopeName() const;
+
+ llvm::StringRef correctScopeTypo() const;
+ llvm::StringRef correctAttributeTypo(const TargetInfo &Target,
+ const LangOptions &LangOpts) const;
+
unsigned getAttributeSpellingListIndex() const {
assert((isAttributeSpellingListCalculated() || AttrName) &&
"Spelling cannot be found");
diff --git a/clang/include/clang/Basic/Attributes.h b/clang/include/clang/Basic/Attributes.h
index 99bb668fe32d0..e17845f5c7d39 100644
--- a/clang/include/clang/Basic/Attributes.h
+++ b/clang/include/clang/Basic/Attributes.h
@@ -10,7 +10,7 @@
#define LLVM_CLANG_BASIC_ATTRIBUTES_H
#include "clang/Basic/AttributeCommonInfo.h"
-
+#include "llvm/ADT/StringRef.h"
namespace clang {
class IdentifierInfo;
@@ -19,6 +19,11 @@ class TargetInfo;
/// Return the version number associated with the attribute if we
/// recognize and implement the attribute specified by the given information.
+int hasAttribute(AttributeCommonInfo::Syntax Syntax,
+ const IdentifierInfo *Scope, llvm::StringRef AttrName,
+ const TargetInfo &Target, const LangOptions &LangOpts,
+ bool CheckPlugins);
+
int hasAttribute(AttributeCommonInfo::Syntax Syntax,
const IdentifierInfo *Scope, const IdentifierInfo *Attr,
const TargetInfo &Target, const LangOptions &LangOpts);
diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt
index 265ea1fc06494..d25af5d8cf73a 100644
--- a/clang/include/clang/Basic/CMakeLists.txt
+++ b/clang/include/clang/Basic/CMakeLists.txt
@@ -79,6 +79,12 @@ clang_tablegen(CXX11AttributeInfo.inc -gen-cxx11-attribute-info
TARGET CXX11AttributeInfo
)
+clang_tablegen(AttributeSpellingList.inc -gen-attribute-spelling-list
+ -I ${CMAKE_CURRENT_SOURCE_DIR}/../../
+ SOURCE Attr.td
+ TARGET AttributeSpellingList
+ )
+
clang_tablegen(Builtins.inc -gen-clang-builtins
SOURCE Builtins.td
TARGET ClangBuiltins)
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index f26c906b46447..e515cd71a6a78 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -177,8 +177,16 @@ def err_opencl_unknown_type_specifier : Error<
"%0 does not support the '%1' "
"%select{type qualifier|storage class specifier}2">;
+def warn_unknown_attribute_namespace : Warning<
+ "unknown attribute namespace '%0'; attribute '%0::%1' ignored">,
+ InGroup<UnknownAttributes>;
+def warn_unknown_attribute_namespace_suggestion : Warning<
+ "unknown attribute namespace '%0'; did you mean '%1'?">,
+ InGroup<UnknownAttributes>;
def warn_unknown_attribute_ignored : Warning<
"unknown attribute %0 ignored">, InGroup<UnknownAttributes>;
+def warn_unknown_attribute_ignored_suggestion : Warning<
+ "unknown attribute %0 ignored; did you mean '%1'?">, InGroup<UnknownAttributes>;
def warn_attribute_ignored : Warning<"%0 attribute ignored">,
InGroup<IgnoredAttributes>;
def err_keyword_not_supported_on_target : Error<
diff --git a/clang/include/clang/Basic/SimpleTypoCorrector.h b/clang/include/clang/Basic/SimpleTypoCorrector.h
new file mode 100644
index 0000000000000..ef359270b59cc
--- /dev/null
+++ b/clang/include/clang/Basic/SimpleTypoCorrector.h
@@ -0,0 +1,31 @@
+#ifndef LLVM_CLANG_BASIC_SIMPLETYPOCORRECTOR_H
+#define LLVM_CLANG_BASIC_SIMPLETYPOCORRECTOR_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+class SimpleTypoCorrector {
+ StringRef BestCandidate;
+ StringRef Typo;
+
+ const unsigned MaxEditDistance;
+ unsigned BestEditDistance;
+ unsigned BestIndex;
+ unsigned NextIndex;
+
+public:
+ explicit SimpleTypoCorrector(StringRef Typo)
+ : BestCandidate(), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
+ BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
+
+ void add(const StringRef Candidate);
+ void add(const char * Candidate);
+
+ bool hasCandidate() const;
+ StringRef getBestCandidate() const;
+ unsigned getBestDeclIndex() const;
+};
+} // namespace clang
+
+#endif // LLVM_CLANG_BASIC_SIMPLETYPOCORRECTOR_H
diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp
index bd2206bb8a3bc..80266d7a12137 100644
--- a/clang/lib/AST/CommentSema.cpp
+++ b/clang/lib/AST/CommentSema.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/DiagnosticComment.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SimpleTypoCorrector.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/StringSwitch.h"
@@ -975,69 +976,26 @@ unsigned Sema::resolveParmVarReference(StringRef Name,
return ParamCommandComment::InvalidParamIndex;
}
-namespace {
-class SimpleTypoCorrector {
- const NamedDecl *BestDecl;
-
- StringRef Typo;
- const unsigned MaxEditDistance;
-
- unsigned BestEditDistance;
- unsigned BestIndex;
- unsigned NextIndex;
-
-public:
- explicit SimpleTypoCorrector(StringRef Typo)
- : BestDecl(nullptr), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
- BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
-
- void addDecl(const NamedDecl *ND);
-
- const NamedDecl *getBestDecl() const {
- if (BestEditDistance > MaxEditDistance)
- return nullptr;
-
- return BestDecl;
- }
-
- unsigned getBestDeclIndex() const {
- assert(getBestDecl());
- return BestIndex;
- }
-};
-
-void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
- unsigned CurrIndex = NextIndex++;
-
- const IdentifierInfo *II = ND->getIdentifier();
- if (!II)
- return;
-
- StringRef Name = II->getName();
- unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
- if (MinPossibleEditDistance > 0 &&
- Typo.size() / MinPossibleEditDistance < 3)
- return;
-
- unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
- if (EditDistance < BestEditDistance) {
- BestEditDistance = EditDistance;
- BestDecl = ND;
- BestIndex = CurrIndex;
- }
-}
-} // end anonymous namespace
-
unsigned Sema::correctTypoInParmVarReference(
StringRef Typo,
ArrayRef<const ParmVarDecl *> ParamVars) {
SimpleTypoCorrector Corrector(Typo);
- for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
- Corrector.addDecl(ParamVars[i]);
- if (Corrector.getBestDecl())
+ for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
+ const ParmVarDecl *Param = ParamVars[i];
+ if (!Param)
+ continue;
+
+ const IdentifierInfo *II = Param->getIdentifier();
+ if (!II)
+ continue;
+
+ Corrector.add(II->getName());
+ }
+
+ if (Corrector.hasCandidate())
return Corrector.getBestDeclIndex();
- else
- return ParamCommandComment::InvalidParamIndex;
+
+ return ParamCommandComment::InvalidParamIndex;
}
namespace {
@@ -1083,7 +1041,14 @@ void CorrectTypoInTParamReferenceHelper(
SimpleTypoCorrector &Corrector) {
for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
const NamedDecl *Param = TemplateParameters->getParam(i);
- Corrector.addDecl(Param);
+ if (!Param)
+ continue;
+
+ const IdentifierInfo *II = Param->getIdentifier();
+ if (!II)
+ continue;
+
+ Corrector.add(II->getName());
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(Param))
@@ -1098,12 +1063,7 @@ StringRef Sema::correctTypoInTParamReference(
const TemplateParameterList *TemplateParameters) {
SimpleTypoCorrector Corrector(Typo);
CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
- if (const NamedDecl *ND = Corrector.getBestDecl()) {
- const IdentifierInfo *II = ND->getIdentifier();
- assert(II && "SimpleTypoCorrector should not return this decl");
- return II->getName();
- }
- return StringRef();
+ return Corrector.getBestCandidate();
}
InlineCommandRenderKind Sema::getInlineCommandRenderKind(StringRef Name) const {
diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp
index 6a070a99c8d96..d24c3c4fcd735 100644
--- a/clang/lib/Basic/Attributes.cpp
+++ b/clang/lib/Basic/Attributes.cpp
@@ -15,6 +15,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/ParsedAttrInfo.h"
+#include "clang/Basic/SimpleTypoCorrector.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/StringMap.h"
@@ -32,13 +33,13 @@ static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name,
}
int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
- const IdentifierInfo *Scope, const IdentifierInfo *Attr,
+ const IdentifierInfo *Scope, StringRef AttrName,
const TargetInfo &Target, const LangOptions &LangOpts,
bool CheckPlugins) {
- StringRef Name = Attr->getName();
// Normalize the attribute name, __foo__ becomes foo.
- if (Name.size() >= 4 && Name.starts_with("__") && Name.ends_with("__"))
- Name = Name.substr(2, Name.size() - 4);
+ if (AttrName.size() >= 4 && AttrName.starts_with("__") &&
+ AttrName.ends_with("__"))
+ AttrName = AttrName.substr(2, AttrName.size() - 4);
// Normalize the scope name, but only for gnu and clang attributes.
StringRef ScopeName = Scope ? Scope->getName() : "";
@@ -55,23 +56,31 @@ int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
// Other OpenMP attributes (e.g. [[omp::assume]]) are handled via the
// regular attribute parsing machinery.
if (LangOpts.OpenMP && ScopeName == "omp" &&
- (Name == "directive" || Name == "sequence"))
+ (AttrName == "directive" || AttrName == "sequence"))
return 1;
- int res = hasAttributeImpl(Syntax, Name, ScopeName, Target, LangOpts);
+ int res = hasAttributeImpl(Syntax, AttrName, ScopeName, Target, LangOpts);
if (res)
return res;
if (CheckPlugins) {
// Check if any plugin provides this attribute.
for (auto &Ptr : getAttributePluginInstances())
- if (Ptr->hasSpelling(Syntax, Name))
+ if (Ptr->hasSpelling(Syntax, AttrName))
return 1;
}
return 0;
}
+int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
+ const IdentifierInfo *Scope, const IdentifierInfo *Attr,
+ const TargetInfo &Target, const LangOptions &LangOpts,
+ bool CheckPlugins) {
+ return hasAttribute(Syntax, Scope, Attr->getName(), Target, LangOpts,
+ CheckPlugins);
+}
+
int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax,
const IdentifierInfo *Scope, const IdentifierInfo *Attr,
const TargetInfo &Target, const LangOptions &LangOpts) {
@@ -191,7 +200,8 @@ getScopeFromNormalizedScopeName(StringRef ScopeName) {
.Case("hlsl", AttributeCommonInfo::Scope::HLSL)
.Case("msvc", AttributeCommonInfo::Scope::MSVC)
.Case("omp", AttributeCommonInfo::Scope::OMP)
- .Case("riscv", AttributeCommonInfo::Scope::RISCV);
+ .Case("riscv", AttributeCommonInfo::Scope::RISCV)
+ .Default(AttributeCommonInfo::Scope::UNKNOWN);
}
unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
@@ -206,3 +216,35 @@ unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
#include "clang/Sema/AttrSpellingListIndex.inc"
}
+
+bool AttributeCommonInfo::isUnknownScopeName() const {
+ return getScopeFromNormalizedScopeName(
+ normalizeAttrScopeName(getScopeName(), getSyntax())) ==
+ AttributeCommonInfo::Scope::UNKNOWN;
+}
+
+#include "clang/Basic/AttributeSpellingList.inc"
+
+StringRef AttributeCommonInfo::correctScopeTypo() const {
+ SimpleTypoCorrector Corrector(getScopeName()->getName());
+
+ for (const auto &ArrtScopeName : AttrScopeSpellingList)
+ Corrector.add(ArrtScopeName);
+
+ return Corrector.getBestCandidate();
+}
+
+StringRef
+AttributeCommonInfo::correctAttributeTypo(const TargetInfo &Target,
+ const LangOptions &LangOpts) const {
+ SimpleTypoCorrector Corrector(getAttrName()->getName());
+
+ for (const auto &ArrtName : AttrSpellingList)
+ Corrector.add(ArrtName);
+
+ if (Corrector.hasCandidate() &&
+ hasAttribute(getSyntax(), getScopeName(), Corrector.getBestCandidate(),
+ Target, LangOpts, /*CheckPlugins=*/false))
+ return Corrector.getBestCandidate();
+ return StringRef();
+}
diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt
index 0eacf79f5d478..069650334714b 100644
--- a/clang/lib/Basic/CMakeLists.txt
+++ b/clang/lib/Basic/CMakeLists.txt
@@ -86,6 +86,7 @@ add_clang_library(clangBasic
SanitizerSpecialCaseList.cpp
Sanitizers.cpp
Sarif.cpp
+ SimpleTypoCorrector.cpp
SourceLocation.cpp
SourceManager.cpp
SourceMgrAdapter.cpp
diff --git a/clang/lib/Basic/SimpleTypoCorrector.cpp b/clang/lib/Basic/SimpleTypoCorrector.cpp
new file mode 100644
index 0000000000000..7b0d21642b9b8
--- /dev/null
+++ b/clang/lib/Basic/SimpleTypoCorrector.cpp
@@ -0,0 +1,47 @@
+#include "clang/Basic/SimpleTypoCorrector.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <cassert>
+
+using namespace clang;
+
+void SimpleTypoCorrector::add(const StringRef Candidate) {
+ if (Candidate.empty())
+ return;
+
+ unsigned MinPossibleEditDistance =
+ abs(static_cast<int>(Candidate.size()) - static_cast<int>(Typo.size()));
+
+ if (MinPossibleEditDistance > 0 && Typo.size() / MinPossibleEditDistance < 3)
+ return;
+
+ unsigned EditDistance = Typo.edit_distance(
+ Candidate, /*AllowReplacements*/ true, MaxEditDistance);
+
+ if (EditDistance < BestEditDistance) {
+ BestCandidate = Candidate;
+ BestEditDistance = EditDistance;
+ BestIndex = NextIndex;
+ }
+
+ ++NextIndex;
+}
+
+void SimpleTypoCorrector::add(const char * Candidate) {
+ if (Candidate)
+ add(StringRef(Candidate));
+}
+
+unsigned SimpleTypoCorrector::getBestDeclIndex() const {
+ assert(getBestCandidate().size() > 0);
+ return BestIndex;
+}
+
+StringRef SimpleTypoCorrector::getBestCandidate() const {
+ return (BestEditDistance <= MaxEditDistance) ? BestCandidate : StringRef();
+}
+
+bool SimpleTypoCorrector::hasCandidate() const {
+ return BestEditDistance <= MaxEditDistance;
+}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 413999b95b998..76b8254b35a04 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -28,6 +28,7 @@
#include "clang/Basic/DarwinSDKInfo.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SimpleTypoCorrector.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -6847,13 +6848,36 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
// though they were unknown attributes.
if (AL.getKind() == ParsedAttr::UnknownAttribute ||
!AL.existsInTarget(S.Context.getTargetInfo())) {
- S.Diag(AL.getLoc(),
- AL.isRegularKeywordAttribute()
- ? (unsigned)diag::err_keyword_not_supported_on_target
- : AL.isDeclspecAttribute()
- ? (unsigned)diag::warn_unhandled_ms_attribute_ignored
- : (unsigned)diag::warn_unknown_attribute_ignored)
- << AL << AL.getRange();
+ if (AL.isUnknownScopeName()) {
+ SourceLocation BeginLoc = AL.getScopeLoc();
+ SourceLocation EndLoc = AL.getRange().getEnd();
+ StringRef CorrectedScopeName = AL.correctScopeTypo();
+ if (CorrectedScopeName.size())
+ S.Diag(BeginLoc, diag::warn_unknown_attribute_namespace_suggestion)
+ << AL.getScopeName()->getName() << CorrectedScopeName;
+ else
+ S.Diag(BeginLoc, diag::warn_unknown_attribute_namespace)
+ << AL.getScopeName()->getName() << AL.getAttrName()->getName()
+ << SourceRange(BeginLoc, EndLoc);
+
+ } else {
+ if (AL.isRegularKeywordAttribute()) {
+ S.Diag(AL.getLoc(), diag::err_keyword_not_supported_on_target)
+ << AL << AL.getRange();
+ } else if (AL.isDeclspecAttribute()) {
+ S.Diag(AL.getLoc(), diag::warn_unhandled_ms_attribute_ignored)
+ << AL << AL.getRange();
+ } else {
+ StringRef CorrectedAttrName = AL.correctAttributeTypo(
+ S.Context.getTargetInfo(), S.Context.getLangOpts());
+ if (CorrectedAttrName.size())
+ S.Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored_suggestion)
+ << AL << CorrectedAttrName << AL.getRange();
+ else
+ S.Diag(AL.getLoc(), diag::warn_unknown_attribute_ignored)
+ << AL << AL.getRange();
+ }
+ }
return;
}
diff --git a/clang/test/CXX/module/module.interface/p3.cpp b/clang/test/CXX/module/module.interface/p3.cpp
index 32819b2dccb11..18758edaf1be7 100644
--- a/clang/test/CXX/module/module.interface/p3.cpp
+++ b/clang/test/CXX/module/module.interface/p3.cpp
@@ -40,7 +40,7 @@ export { // No diagnostic after P2615R1 DR
extern "C++" {} // No diagnostic after P2615R1 DR
}
export [[]]; // No diagnostic after P2615R1 DR
-export [[example::attr]]; // expected-warning {{unknown attribute 'attr'}}
+export [[example::attr]]; // expected-warning {{unknown attribute namespace 'example'; attribute 'example::attr' ignored}}
// [...] shall not declare a name with internal linkage
export static int a; // expected-error {{declaration of 'a' with internal linkage cannot be exported}}
diff --git a/clang/test/Parser/c2x-attributes.c b/clang/test/Parser/c2x-attributes.c
index be039e40f98ef..b5f502c5790d3 100644
--- a/clang/test/Parser/c2x-attributes.c
+++ b/clang/test/Parser/c2x-attributes.c
@@ -133,7 +133,7 @@ void f11(void) {
}
[[attr]] void f12(void); // expected-warning {{unknown attribute 'attr' ignored}}
-[[vendor::attr]] void f13(void); // expected-warning {{unknown attribute 'attr' ignored}}
+[[vendor::attr]] void f13(void); // expected-warning {{unknown attribute namespace 'vendor'; attribute 'vendor::attr' ignored}}
// Ensure that asm statements properly handle double colons.
void test_asm(void) {
diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp
index fad3010c98b9c..7a0a8b989851f 100644
--- a/clang/test/Parser/cxx0x-attributes.cpp
+++ b/clang/test/Parser/cxx0x-attributes.cpp
@@ -46,7 +46,7 @@ int & [[noreturn]] ref_attr_3 = after_attr; // expected-error {{'noreturn' attri
int && [[]] rref_attr = 0;
int array_attr [1] [[]];
alignas(8) int aligned_attr;
-[[test::valid(for 42 [very] **** '+' symbols went on a trip and had a "good"_time; the end.)]] int garbage_attr; // expected-warning {{unknown attribute 'valid' ignored}}
+[[test::valid(for 42 [very] **** '+' symbols went on a trip and had a "good"_time; the end.)]] int garbage_attr; // expected-warning {{unknown attribute namespace 'test'; attribute 'test::valid' ignored}}
[[,,,static, class, namespace,, inline, constexpr, mutable,, bitand, bitor::compl(!.*_ Cx.!U^*R),,,]] int more_garbage_attr; // expected-warning {{unknown attribute 'static' ignored}} \
// expected-warning {{unknown attribute 'class' ignored}} \
// expected-warning {{unknown attribute 'namespace' ignored}} \
@@ -54,7 +54,7 @@ alignas(8) int aligned_attr;
// expected-warning {{unknown attribute 'constexpr' ignored}} \
// expected-warning {{unknown attribute 'mutable' ignored}} \
// expected-warning {{unknown attribute 'bitand' ignored}} \
- // expected-warning {{unknown attribute 'compl' ignored}}
+ // expected-warning {{unknown attribute namespace 'bitor'; attribute 'bitor::compl' ignored}}
[[u8"invalid!"]] int invalid_string_attr; // expected-error {{expected ']'}}
void fn_attr () [[]];
void noexcept_fn_attr () noexcept [[]];
@@ -269,7 +269,7 @@ template <int... Is> void variadic_nttp() {
void baz [[clang::no_sanitize(Is...)]] (); // expected-error {{expected string literal as argument of 'no_sanitize' attribute}}
void bor [[clang::annotate("A", "V" ...)]] (); // expected-error {{pack expansion does not contain any unexpanded parameter packs}}
void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // expected-error {{'annotate' attribute requires parameter 1 to be a constant expression}} expected-note {{subexpression not valid in a constant expression}}
- void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute 'foo' ignored}}
+ void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute namespace 'unknown'; attribute 'unknown::foo' ignored}}
void faz [[clang::annotate("C", (Is + ...))]] (); // expected-warning {{pack fold expression is a C++17 extension}}
void far [[clang::annotate("D", Is...)]] ();
void foz [[clang::annotate("E", 1, 2, 3, Is...)]] ();
diff --git a/clang/test/Sema/unknown-attributes.c b/clang/test/Sema/unknown-attributes.c
new file mode 100644
index 0000000000000..067b28cfb6059
--- /dev/null
+++ b/clang/test/Sema/unknown-attributes.c
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -Wunknown-attributes -fsyntax-only -verify %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -Wunknown-attributes -verify %s
+
+[[foo::a]] // expected-warning {{unknown attribute namespace 'foo'; attribute 'foo::a' ignored}}
+int f1(void) {
+ return 0;
+}
+
+[[clan::deprecated]] // expected-warning {{unknown attribute namespace 'clan'; did you mean 'clang'?}}
+int f2(void) {
+ return 0;
+}
+
+[[clang::anntate("f3")]] // expected-warning {{unknown attribute 'anntate' ignored; did you mean 'annotate'?}}
+int f3(void) {
+ return 0;
+}
+
+[[deprcated]] // expected-warning {{unknown attribute 'deprcated' ignored; did you mean 'deprecated'?}}
+int f4(void) {
+ return 0;
+}
+
+[[hnu::deprcated]] // expected-warning {{unknown attribute namespace 'hnu'; did you mean 'gnu'?}}
+int f5(void) {
+ return 0;
+}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 00df99d04d873..897a7eb5f00b3 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -4107,6 +4107,38 @@ void EmitClangAttrParsedAttrList(const RecordKeeper &Records, raw_ostream &OS) {
}
}
+void EmitAttributeSpellingList(const RecordKeeper &Records, raw_ostream &OS) {
+ emitSourceFileHeader("List of attribute names", OS, Records);
+
+ std::set<StringRef> AttrSpellingList;
+ std::set<StringRef> AttrScopeSpellingList;
+
+ for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
+ const Record &Attr = *A;
+
+ std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attr);
+ for (const auto &S : Spellings) {
+ if (S.nameSpace().empty()) {
+ AttrSpellingList.insert(S.name());
+ } else {
+ AttrScopeSpellingList.insert(S.nameSpace());
+ }
+ }
+ }
+
+ OS << "static constexpr const char * AttrSpellingList[] = {\n";
+ for (const auto &AttrName : AttrSpellingList) {
+ OS << " " << "\"" << AttrName << "\"" << "," << "\n";
+ }
+ OS << "};" << "\n";
+
+ OS << "static constexpr const char * AttrScopeSpellingList[] = {\n";
+ for (const auto &AttrScopeName : AttrScopeSpellingList) {
+ OS << " " << "\"" << AttrScopeName << "\"" << "," << "\n";
+ }
+ OS << "};" << "\n";
+}
+
static bool isArgVariadic(const Record &R, StringRef AttrName) {
return createArgument(R, AttrName)->isVariadic();
}
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index a2c6f002f7359..9ab6e7d1e61d7 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -73,6 +73,7 @@ enum ActionType {
GenClangOpenCLBuiltinHeader,
GenClangOpenCLBuiltinTests,
GenCXX11AttributeInfo,
+ GenAttributeSpellingList,
GenArmNeon,
GenArmFP16,
GenArmBF16,
@@ -240,6 +241,8 @@ cl::opt<ActionType> Action(
"Generate OpenCL builtin declaration tests"),
clEnumValN(GenCXX11AttributeInfo, "gen-cxx11-attribute-info",
"Generate CXX11 attributes info"),
+ clEnumValN(GenAttributeSpellingList, "gen-attribute-spelling-list",
+ "Generate attribute spelling list"),
clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"),
clEnumValN(GenArmFP16, "gen-arm-fp16", "Generate arm_fp16.h for clang"),
clEnumValN(GenArmBF16, "gen-arm-bf16", "Generate arm_bf16.h for clang"),
@@ -351,6 +354,9 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
case GenCXX11AttributeInfo:
EmitCXX11AttributeInfo(Records, OS);
break;
+ case GenAttributeSpellingList:
+ EmitAttributeSpellingList(Records, OS);
+ break;
case GenClangAttrImpl:
EmitClangAttrImpl(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index 54031147d38e1..79b1f66d0e49e 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -51,6 +51,8 @@ void EmitClangAttrSubjectMatchRulesParserStringSwitches(
const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCXX11AttributeInfo(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
+void EmitAttributeSpellingList(const llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS);
void EmitClangAttrClass(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangAttrImpl(const llvm::RecordKeeper &Records,
>From 7bf85a26ba97ddcfe3e71958c033776787a76353 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Sun, 4 May 2025 01:17:57 +0300
Subject: [PATCH 2/2] fix formatting
---
clang/include/clang/Basic/SimpleTypoCorrector.h | 2 +-
clang/lib/Basic/SimpleTypoCorrector.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/SimpleTypoCorrector.h b/clang/include/clang/Basic/SimpleTypoCorrector.h
index ef359270b59cc..85c5602546b49 100644
--- a/clang/include/clang/Basic/SimpleTypoCorrector.h
+++ b/clang/include/clang/Basic/SimpleTypoCorrector.h
@@ -20,7 +20,7 @@ class SimpleTypoCorrector {
BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
void add(const StringRef Candidate);
- void add(const char * Candidate);
+ void add(const char *Candidate);
bool hasCandidate() const;
StringRef getBestCandidate() const;
diff --git a/clang/lib/Basic/SimpleTypoCorrector.cpp b/clang/lib/Basic/SimpleTypoCorrector.cpp
index 7b0d21642b9b8..0f7a91476b8e8 100644
--- a/clang/lib/Basic/SimpleTypoCorrector.cpp
+++ b/clang/lib/Basic/SimpleTypoCorrector.cpp
@@ -28,7 +28,7 @@ void SimpleTypoCorrector::add(const StringRef Candidate) {
++NextIndex;
}
-void SimpleTypoCorrector::add(const char * Candidate) {
+void SimpleTypoCorrector::add(const char *Candidate) {
if (Candidate)
add(StringRef(Candidate));
}
More information about the cfe-commits
mailing list