https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/140629
>From 5b8f0635fdddc87ea326a3b1b47191ab9bf23c0f Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Tue, 20 May 2025 00:38:43 +0300 Subject: [PATCH 1/2] [Clang] add typo correction for unknown attribute names --- clang/docs/ReleaseNotes.rst | 2 + .../include/clang/Basic/AttributeCommonInfo.h | 5 + clang/include/clang/Basic/Attributes.h | 4 + clang/include/clang/Basic/CMakeLists.txt | 6 + .../clang/Basic/DiagnosticCommonKinds.td | 2 + .../clang/Basic/SimpleTypoCorrection.h | 35 ++++ clang/include/clang/Sema/Sema.h | 2 + clang/lib/AST/CommentSema.cpp | 97 +++-------- clang/lib/Basic/Attributes.cpp | 153 ++++++++++++------ clang/lib/Basic/CMakeLists.txt | 1 + clang/lib/Basic/SimpleTypoCorrection.cpp | 52 ++++++ clang/lib/Sema/SemaDeclAttr.cpp | 18 ++- clang/lib/Sema/SemaType.cpp | 4 +- clang/test/Parser/cxx0x-attributes.cpp | 2 +- clang/test/Sema/attr-c2x.c | 4 +- clang/test/Sema/unknown-attributes.c | 16 +- clang/test/SemaCXX/cxx11-gnu-attrs.cpp | 2 +- clang/utils/TableGen/ClangAttrEmitter.cpp | 27 ++++ clang/utils/TableGen/TableGen.cpp | 6 + clang/utils/TableGen/TableGenBackends.h | 2 + 20 files changed, 307 insertions(+), 133 deletions(-) create mode 100644 clang/include/clang/Basic/SimpleTypoCorrection.h create mode 100644 clang/lib/Basic/SimpleTypoCorrection.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ac9baf229b489..deee00128c1fa 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -561,6 +561,8 @@ Improvements to Clang's diagnostics - Fixed a crash when checking a ``__thread``-specified variable declaration with a dependent type in C++. (#GH140509) +- Clang now suggests corrections for unknown attribute names. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 6db7b53317e7d..b4b8345b4ed40 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -21,6 +21,8 @@ namespace clang { class ASTRecordWriter; class IdentifierInfo; +class LangOptions; +class TargetInfo; class AttributeCommonInfo { public: @@ -196,6 +198,9 @@ class AttributeCommonInfo { /// with surrounding underscores removed as appropriate (e.g. /// __gnu__::__attr__ will be normalized to gnu::attr). std::string getNormalizedFullName() const; + std::optional<std::string> + getCorrectedFullName(const TargetInfo &Target, + const LangOptions &LangOpts) const; SourceRange getNormalizedRange() const; bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; } diff --git a/clang/include/clang/Basic/Attributes.h b/clang/include/clang/Basic/Attributes.h index 99bb668fe32d0..9cf6fb3d89019 100644 --- a/clang/include/clang/Basic/Attributes.h +++ b/clang/include/clang/Basic/Attributes.h @@ -19,6 +19,10 @@ 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, llvm::StringRef ScopeName, + 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..1873878e2e46b 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 e4d94fefbbf3d..0bd8a423c393e 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -181,6 +181,8 @@ def err_opencl_unknown_type_specifier : Error< 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/SimpleTypoCorrection.h b/clang/include/clang/Basic/SimpleTypoCorrection.h new file mode 100644 index 0000000000000..4cd104f79aebe --- /dev/null +++ b/clang/include/clang/Basic/SimpleTypoCorrection.h @@ -0,0 +1,35 @@ +#ifndef LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H +#define LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { + +class IdentifierInfo; + +class SimpleTypoCorrection { + StringRef BestCandidate; + StringRef Typo; + + const unsigned MaxEditDistance; + unsigned BestEditDistance; + unsigned BestIndex; + unsigned NextIndex; + +public: + explicit SimpleTypoCorrection(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); + void add(const IdentifierInfo *Candidate); + + std::optional<StringRef> getCorrection() const; + bool hasCorrection() const; + unsigned getCorrectionIndex() const; +}; +} // namespace clang + +#endif // LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5ec67087aeea4..d7a3a81065d33 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5033,6 +5033,8 @@ class Sema final : public SemaBase { /// which might be lying around on it. void checkUnusedDeclAttributes(Declarator &D); + void DiagnoseUnknownAttribute(const ParsedAttr &AL); + /// DeclClonePragmaWeak - clone existing decl (maybe definition), /// \#pragma weak needs a non-definition decl and source may not have one. NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II, diff --git a/clang/lib/AST/CommentSema.cpp b/clang/lib/AST/CommentSema.cpp index bd2206bb8a3bc..fb745fc560d2f 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/SimpleTypoCorrection.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/StringSwitch.h" @@ -975,69 +976,22 @@ 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 +Sema::correctTypoInParmVarReference(StringRef Typo, + ArrayRef<const ParmVarDecl *> ParamVars) { + SimpleTypoCorrection STC(Typo); + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { + const ParmVarDecl *Param = ParamVars[i]; + if (!Param) + continue; - unsigned getBestDeclIndex() const { - assert(getBestDecl()); - return BestIndex; + STC.add(Param->getIdentifier()); } -}; - -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; + if (STC.hasCorrection()) + return STC.getCorrectionIndex(); - 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()) - return Corrector.getBestDeclIndex(); - else - return ParamCommandComment::InvalidParamIndex; + return ParamCommandComment::InvalidParamIndex; } namespace { @@ -1079,16 +1033,18 @@ bool Sema::resolveTParamReference( namespace { void CorrectTypoInTParamReferenceHelper( - const TemplateParameterList *TemplateParameters, - SimpleTypoCorrector &Corrector) { + const TemplateParameterList *TemplateParameters, + SimpleTypoCorrection &STC) { for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { const NamedDecl *Param = TemplateParameters->getParam(i); - Corrector.addDecl(Param); + if (!Param) + continue; + + STC.add(Param->getIdentifier()); if (const TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(Param)) - CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), - Corrector); + CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC); } } } // end anonymous namespace @@ -1096,13 +1052,12 @@ void CorrectTypoInTParamReferenceHelper( StringRef Sema::correctTypoInTParamReference( StringRef Typo, 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(); - } + SimpleTypoCorrection STC(Typo); + CorrectTypoInTParamReferenceHelper(TemplateParameters, STC); + + if (auto CorrectedTParamReference = STC.getCorrection()) + return *CorrectedTParamReference; + return StringRef(); } diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index 8ff5cc54ccc93..0c0a816c78039 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/SimpleTypoCorrection.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringMap.h" @@ -22,30 +23,37 @@ using namespace clang; -static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name, - StringRef ScopeName, const TargetInfo &Target, - const LangOptions &LangOpts) { +static StringRef canonicalizeScopeName(StringRef Name) { + // Normalize the scope name, but only for gnu and clang attributes. + if (Name == "__gnu__") + return "gnu"; -#include "clang/Basic/AttrHasAttributeImpl.inc" + if (Name == "_Clang") + return "clang"; - return 0; + return Name; } -int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, - const IdentifierInfo *Scope, const IdentifierInfo *Attr, - const TargetInfo &Target, const LangOptions &LangOpts, - bool CheckPlugins) { - StringRef Name = Attr->getName(); +static StringRef canonicalizeAttrName(StringRef Name) { // Normalize the attribute name, __foo__ becomes foo. if (Name.size() >= 4 && Name.starts_with("__") && Name.ends_with("__")) - Name = Name.substr(2, Name.size() - 4); + return Name.substr(2, Name.size() - 4); - // Normalize the scope name, but only for gnu and clang attributes. - StringRef ScopeName = Scope ? Scope->getName() : ""; - if (ScopeName == "__gnu__") - ScopeName = "gnu"; - else if (ScopeName == "_Clang") - ScopeName = "clang"; + return Name; +} + +static int hasAttributeImpl(AttributeCommonInfo::Syntax Syntax, StringRef Name, + StringRef ScopeName, const TargetInfo &Target, + const LangOptions &LangOpts) { +#include "clang/Basic/AttrHasAttributeImpl.inc" + return 0; +} + +int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, StringRef ScopeName, + StringRef Name, const TargetInfo &Target, + const LangOptions &LangOpts, bool CheckPlugins) { + ScopeName = canonicalizeScopeName(ScopeName); + Name = canonicalizeAttrName(Name); // As a special case, look for the omp::sequence and omp::directive // attributes. We support those, but not through the typical attribute @@ -72,6 +80,14 @@ int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, 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 ? Scope->getName() : "", Attr->getName(), + Target, LangOpts, CheckPlugins); +} + int clang::hasAttribute(AttributeCommonInfo::Syntax Syntax, const IdentifierInfo *Scope, const IdentifierInfo *Attr, const TargetInfo &Target, const LangOptions &LangOpts) { @@ -90,25 +106,25 @@ const char *attr::getSubjectMatchRuleSpelling(attr::SubjectMatchRule Rule) { } static StringRef -normalizeAttrScopeName(const IdentifierInfo *Scope, +normalizeAttrScopeName(StringRef ScopeName, AttributeCommonInfo::Syntax SyntaxUsed) { - if (!Scope) - return ""; - - // Normalize the "__gnu__" scope name to be "gnu" and the "_Clang" scope name - // to be "clang". - StringRef ScopeName = Scope->getName(); if (SyntaxUsed == AttributeCommonInfo::AS_CXX11 || - SyntaxUsed == AttributeCommonInfo::AS_C23) { - if (ScopeName == "__gnu__") - ScopeName = "gnu"; - else if (ScopeName == "_Clang") - ScopeName = "clang"; - } + SyntaxUsed == AttributeCommonInfo::AS_C23) + return canonicalizeScopeName(ScopeName); + return ScopeName; } -static StringRef normalizeAttrName(const IdentifierInfo *Name, +static StringRef +normalizeAttrScopeName(const IdentifierInfo *ScopeName, + AttributeCommonInfo::Syntax SyntaxUsed) { + if (ScopeName) + return normalizeAttrScopeName(ScopeName->getName(), SyntaxUsed); + + return ""; +} + +static StringRef normalizeAttrName(StringRef AttrName, StringRef NormalizedScopeName, AttributeCommonInfo::Syntax SyntaxUsed) { // Normalize the attribute name, __foo__ becomes foo. This is only allowable @@ -119,10 +135,9 @@ static StringRef normalizeAttrName(const IdentifierInfo *Name, SyntaxUsed == AttributeCommonInfo::AS_C23) && (NormalizedScopeName.empty() || NormalizedScopeName == "gnu" || NormalizedScopeName == "clang")); - StringRef AttrName = Name->getName(); - if (ShouldNormalize && AttrName.size() >= 4 && AttrName.starts_with("__") && - AttrName.ends_with("__")) - AttrName = AttrName.slice(2, AttrName.size() - 2); + + if (ShouldNormalize) + return canonicalizeAttrName(AttrName); return AttrName; } @@ -137,16 +152,11 @@ bool AttributeCommonInfo::isClangScope() const { #include "clang/Sema/AttrParsedAttrKinds.inc" -static SmallString<64> normalizeName(const IdentifierInfo *Name, - const IdentifierInfo *Scope, +static SmallString<64> normalizeName(StringRef AttrName, StringRef ScopeName, AttributeCommonInfo::Syntax SyntaxUsed) { - StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed); - StringRef AttrName = normalizeAttrName(Name, ScopeName, SyntaxUsed); - - std::string StrAttrName = AttrName.str(); - if (SyntaxUsed == AttributeCommonInfo::AS_HLSLAnnotation) - StrAttrName = AttrName.lower(); - + std::string StrAttrName = SyntaxUsed == AttributeCommonInfo::AS_HLSLAnnotation + ? AttrName.lower() + : AttrName.str(); SmallString<64> FullName = ScopeName; if (!ScopeName.empty()) { assert(SyntaxUsed == AttributeCommonInfo::AS_CXX11 || @@ -154,10 +164,18 @@ static SmallString<64> normalizeName(const IdentifierInfo *Name, FullName += "::"; } FullName += StrAttrName; - return FullName; } +static SmallString<64> normalizeName(const IdentifierInfo *Name, + const IdentifierInfo *Scope, + AttributeCommonInfo::Syntax SyntaxUsed) { + StringRef ScopeName = normalizeAttrScopeName(Scope, SyntaxUsed); + StringRef AttrName = + normalizeAttrName(Name->getName(), ScopeName, SyntaxUsed); + return normalizeName(AttrName, ScopeName, SyntaxUsed); +} + AttributeCommonInfo::Kind AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, const IdentifierInfo *ScopeName, @@ -167,8 +185,8 @@ AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name, AttributeCommonInfo::AttrArgsInfo AttributeCommonInfo::getCXX11AttrArgsInfo(const IdentifierInfo *Name) { - StringRef AttrName = - normalizeAttrName(Name, /*NormalizedScopeName*/ "", Syntax::AS_CXX11); + StringRef AttrName = normalizeAttrName( + Name->getName(), /*NormalizedScopeName*/ "", Syntax::AS_CXX11); #define CXX11_ATTR_ARGS_INFO return llvm::StringSwitch<AttributeCommonInfo::AttrArgsInfo>(AttrName) #include "clang/Basic/CXX11AttributeInfo.inc" @@ -203,10 +221,47 @@ unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const { // attribute spell list index matching code. auto Syntax = static_cast<AttributeCommonInfo::Syntax>(getSyntax()); StringRef ScopeName = normalizeAttrScopeName(getScopeName(), Syntax); - StringRef Name = normalizeAttrName(getAttrName(), ScopeName, Syntax); - + StringRef Name = + normalizeAttrName(getAttrName()->getName(), ScopeName, Syntax); AttributeCommonInfo::Scope ComputedScope = getScopeFromNormalizedScopeName(ScopeName); #include "clang/Sema/AttrSpellingListIndex.inc" } + +#include "clang/Basic/AttributeSpellingList.inc" + +std::optional<std::string> +AttributeCommonInfo::getCorrectedFullName(const TargetInfo &Target, + const LangOptions &LangOpts) const { + StringRef ScopeName = normalizeAttrScopeName(getScopeName(), getSyntax()); + if (ScopeName.size() > 0 && + llvm::none_of(AttrScopeSpellingList, + [&](const char *S) { return S == ScopeName; })) { + SimpleTypoCorrection STC(ScopeName); + for (const auto &Scope : AttrScopeSpellingList) + STC.add(Scope); + + if (auto CorrectedScopeName = STC.getCorrection()) + ScopeName = *CorrectedScopeName; + } + + StringRef AttrName = + normalizeAttrName(getAttrName()->getName(), ScopeName, getSyntax()); + if (llvm::none_of(AttrSpellingList, + [&](const char *A) { return A == AttrName; })) { + SimpleTypoCorrection STC(AttrName); + for (const auto &Attr : AttrSpellingList) + STC.add(Attr); + + if (auto CorrectedAttrName = STC.getCorrection()) + AttrName = *CorrectedAttrName; + } + + if (hasAttribute(getSyntax(), ScopeName, AttrName, Target, LangOpts, + /*CheckPlugins=*/true)) + return static_cast<std::string>( + normalizeName(AttrName, ScopeName, getSyntax())); + + return std::nullopt; +} diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index 0eacf79f5d478..f8a31c890ac4d 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 + SimpleTypoCorrection.cpp SourceLocation.cpp SourceManager.cpp SourceMgrAdapter.cpp diff --git a/clang/lib/Basic/SimpleTypoCorrection.cpp b/clang/lib/Basic/SimpleTypoCorrection.cpp new file mode 100644 index 0000000000000..c98b89d9f080d --- /dev/null +++ b/clang/lib/Basic/SimpleTypoCorrection.cpp @@ -0,0 +1,52 @@ +#include "clang/Basic/SimpleTypoCorrection.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +using namespace clang; + +void SimpleTypoCorrection::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 SimpleTypoCorrection::add(const char *Candidate) { + if (Candidate) + add(StringRef(Candidate)); +} + +void SimpleTypoCorrection::add(const IdentifierInfo *Candidate) { + if (Candidate) + add(Candidate->getName()); +} + +unsigned SimpleTypoCorrection::getCorrectionIndex() const { + return BestIndex; +} + +std::optional<StringRef> SimpleTypoCorrection::getCorrection() const { + if (hasCorrection()) + return BestCandidate; + return std::nullopt; +} + +bool SimpleTypoCorrection::hasCorrection() const { + return BestEditDistance <= MaxEditDistance; +} diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 4d7f0455444f1..c7840fb199ff6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6867,9 +6867,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, : diag::warn_unhandled_ms_attribute_ignored) << AL.getAttrName() << AL.getRange(); } else { - S.Diag(AL.getNormalizedRange().getBegin(), - diag::warn_unknown_attribute_ignored) - << "'" + AL.getNormalizedFullName() + "'" << AL.getNormalizedRange(); + S.DiagnoseUnknownAttribute(AL); } return; } @@ -7865,6 +7863,20 @@ void Sema::checkUnusedDeclAttributes(Declarator &D) { ::checkUnusedDeclAttributes(*this, D.getTypeObject(i).getAttrs()); } +void Sema::DiagnoseUnknownAttribute(const ParsedAttr &AL) { + std::string NormalizedFullName = '\'' + AL.getNormalizedFullName() + '\''; + if (auto CorrectedFullName = + AL.getCorrectedFullName(Context.getTargetInfo(), getLangOpts())) { + Diag(AL.getNormalizedRange().getBegin(), + diag::warn_unknown_attribute_ignored_suggestion) + << NormalizedFullName << *CorrectedFullName << AL.getNormalizedRange(); + } else { + Diag(AL.getNormalizedRange().getBegin(), + diag::warn_unknown_attribute_ignored) + << NormalizedFullName << AL.getNormalizedRange(); + } +} + NamedDecl *Sema::DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II, SourceLocation Loc) { assert(isa<FunctionDecl>(ND) || isa<VarDecl>(ND)); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 9ed2326f151a3..550c7a36205b1 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8788,9 +8788,7 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, case ParsedAttr::UnknownAttribute: if (attr.isStandardAttributeSyntax()) { - state.getSema().Diag(attr.getLoc(), - diag::warn_unknown_attribute_ignored) - << attr << attr.getRange(); + state.getSema().DiagnoseUnknownAttribute(attr); // Mark the attribute as invalid so we don't emit the same diagnostic // multiple times. attr.setInvalid(); diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp index 6e0904e94fecc..372a373a49ec5 100644 --- a/clang/test/Parser/cxx0x-attributes.cpp +++ b/clang/test/Parser/cxx0x-attributes.cpp @@ -344,7 +344,7 @@ enum class [[]] EvenMoreSecrets {}; namespace arguments { void f[[gnu::format(printf, 1, 2)]](const char*, ...); - void g() [[unknown::foo(ignore arguments for unknown attributes, even with symbols!)]]; // expected-warning {{unknown attribute 'foo' ignored}} + void g() [[unknown::foo(ignore arguments for unknown attributes, even with symbols!)]]; // expected-warning {{unknown attribute 'unknown::foo' ignored}} [[deprecated("with argument")]] int i; // expected-warning@-1 {{use of the 'deprecated' attribute is a C++14 extension}} } diff --git a/clang/test/Sema/attr-c2x.c b/clang/test/Sema/attr-c2x.c index c774cc68eb890..eff90d639c0c0 100644 --- a/clang/test/Sema/attr-c2x.c +++ b/clang/test/Sema/attr-c2x.c @@ -39,5 +39,5 @@ void bar(void) { [[__gnu__::__hot__]] void hot_func4(void); // Note how not all GCC attributes are supported in C. -[[gnu::abi_tag("")]] void abi_func(void); // expected-warning {{unknown attribute 'abi_tag' ignored}} -struct S s [[gnu::init_priority(1)]]; // expected-warning {{unknown attribute 'init_priority' ignored}} +[[gnu::abi_tag("")]] void abi_func(void); // expected-warning {{unknown attribute 'gnu::abi_tag' ignored}} +struct S s [[gnu::init_priority(1)]]; // expected-warning {{unknown attribute 'gnu::init_priority' ignored}} diff --git a/clang/test/Sema/unknown-attributes.c b/clang/test/Sema/unknown-attributes.c index 1f6708fc7219f..a701650c9e056 100644 --- a/clang/test/Sema/unknown-attributes.c +++ b/clang/test/Sema/unknown-attributes.c @@ -1,12 +1,22 @@ // RUN: %clang_cc1 -Wunknown-attributes -fsyntax-only -verify %s -// RUN: %clang_cc1 -x c++ -fsyntax-only -Wunknown-attributes -verify %s +// RUN: %clang_cc1 -x c++ -Wunknown-attributes -fsyntax-only -verify %s -[[foo::a]] // expected-warning {{unknown attribute 'foo::a' ignored}} +[[gmu::deprected]] // expected-warning {{unknown attribute 'gmu::deprected' ignored; did you mean 'gnu::deprecated'?}} int f1(void) { return 0; } -[[clan::deprecated]] // expected-warning {{unknown attribute 'clan::deprecated' ignored}} +[[gmu::deprecated]] // expected-warning {{unknown attribute 'gmu::deprecated' ignored; did you mean 'gnu::deprecated'?}} int f2(void) { return 0; } + +[[gnu::deprected]] // expected-warning {{unknown attribute 'gnu::deprected' ignored; did you mean 'gnu::deprecated'?}} +int f3(void) { + return 0; +} + +[[deprected]] // expected-warning {{unknown attribute 'deprected' ignored; did you mean 'deprecated'?}} +int f4(void) { + return 0; +} diff --git a/clang/test/SemaCXX/cxx11-gnu-attrs.cpp b/clang/test/SemaCXX/cxx11-gnu-attrs.cpp index a91cb278b4d24..b8d914a6791a9 100644 --- a/clang/test/SemaCXX/cxx11-gnu-attrs.cpp +++ b/clang/test/SemaCXX/cxx11-gnu-attrs.cpp @@ -3,7 +3,7 @@ // Error cases. [[gnu::this_attribute_does_not_exist]] int unknown_attr; -// expected-warning@-1 {{unknown attribute 'this_attribute_does_not_exist' ignored}} +// expected-warning@-1 {{unknown attribute 'gnu::this_attribute_does_not_exist' ignored}} int [[gnu::unused]] attr_on_type; // expected-error@-1 {{'unused' attribute cannot be applied to types}} int *[[gnu::unused]] attr_on_ptr; diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 9684ec9520e5a..1487a473814d2 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -4105,6 +4105,33 @@ 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")) { + for (const auto &S : GetFlattenedSpellings(*A)) { + AttrSpellingList.insert(S.name()); + if (S.nameSpace().size()) + 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 2709b7be5965aad58b3b63db7099df88140426f1 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Tue, 20 May 2025 00:54:06 +0300 Subject: [PATCH 2/2] fix formatting --- clang/lib/Basic/SimpleTypoCorrection.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/Basic/SimpleTypoCorrection.cpp b/clang/lib/Basic/SimpleTypoCorrection.cpp index c98b89d9f080d..86b86eeea2609 100644 --- a/clang/lib/Basic/SimpleTypoCorrection.cpp +++ b/clang/lib/Basic/SimpleTypoCorrection.cpp @@ -37,9 +37,7 @@ void SimpleTypoCorrection::add(const IdentifierInfo *Candidate) { add(Candidate->getName()); } -unsigned SimpleTypoCorrection::getCorrectionIndex() const { - return BestIndex; -} +unsigned SimpleTypoCorrection::getCorrectionIndex() const { return BestIndex; } std::optional<StringRef> SimpleTypoCorrection::getCorrection() const { if (hasCorrection()) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits