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.taras...@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.taras...@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)); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits