[clang] [Clang] add typo correction for unknown attribute names (PR #140629)
Oleksandr T. via cfe-commits
cfe-commits at lists.llvm.org
Wed May 21 06:18:52 PDT 2025
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.tarasiuk at outlook.com>
Date: Tue, 20 May 2025 00:38:43 +0300
Subject: [PATCH 1/4] [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 at -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 at -1 {{unknown attribute 'this_attribute_does_not_exist' ignored}}
+// expected-warning at -1 {{unknown attribute 'gnu::this_attribute_does_not_exist' ignored}}
int [[gnu::unused]] attr_on_type;
// expected-error at -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.tarasiuk at outlook.com>
Date: Tue, 20 May 2025 00:54:06 +0300
Subject: [PATCH 2/4] 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())
>From 2634e5a959d01d4e9d5c2f3b1e36789800c0c67d Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 20 May 2025 21:06:03 +0300
Subject: [PATCH 3/4] update attrs spelling list emitter
---
clang/lib/Basic/Attributes.cpp | 10 ++++++++++
clang/utils/TableGen/ClangAttrEmitter.cpp | 16 ++++++++++------
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp
index 0c0a816c78039..c2f3120aed5d0 100644
--- a/clang/lib/Basic/Attributes.cpp
+++ b/clang/lib/Basic/Attributes.cpp
@@ -229,7 +229,17 @@ unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const {
#include "clang/Sema/AttrSpellingListIndex.inc"
}
+#define ATTR_NAME(NAME) NAME,
+static constexpr const char *AttrSpellingList[] = {
#include "clang/Basic/AttributeSpellingList.inc"
+};
+#undef ATTR_NAME
+
+#define ATTR_SCOPE_SCOPE(SCOPE_NAME) SCOPE_NAME,
+static constexpr const char *AttrScopeSpellingList[] = {
+#include "clang/Basic/AttributeSpellingList.inc"
+};
+#undef ATTR_SCOPE_SCOPE
std::optional<std::string>
AttributeCommonInfo::getCorrectedFullName(const TargetInfo &Target,
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 1487a473814d2..89d7134b7fd7e 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -4119,17 +4119,21 @@ void EmitAttributeSpellingList(const RecordKeeper &Records, raw_ostream &OS) {
}
}
- OS << "static constexpr const char *AttrSpellingList[] = {\n";
+ OS << "#ifndef ATTR_NAME" << "\n";
+ OS << "#define ATTR_NAME(NAME) NAME" << "\n";
+ OS << "#endif" << "\n" << "\n";
for (const auto &AttrName : AttrSpellingList) {
- OS << " " << "\"" << AttrName << "\"" << "," << "\n";
+ OS << "ATTR_NAME(\"" << AttrName << "\")\n";
}
- OS << "};" << "\n";
+ OS << "\n";
- OS << "static constexpr const char *AttrScopeSpellingList[] = {\n";
+ OS << "#ifndef ATTR_SCOPE_SCOPE" << "\n";
+ OS << "#define ATTR_SCOPE_SCOPE(SCOPE_NAME) SCOPE_NAME" << "\n";
+ OS << "#endif" << "\n" << "\n";
for (const auto &AttrScopeName : AttrScopeSpellingList) {
- OS << " " << "\"" << AttrScopeName << "\"" << "," << "\n";
+ OS << "ATTR_SCOPE_SCOPE(\"" << AttrScopeName << "\")\n";
}
- OS << "};" << "\n";
+ OS << "\n";
}
static bool isArgVariadic(const Record &R, StringRef AttrName) {
>From 0c1f74c442fb71b5accd7fcb399f1159615b0d94 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 21 May 2025 16:18:07 +0300
Subject: [PATCH 4/4] add copyright headers
---
clang/include/clang/Basic/SimpleTypoCorrection.h | 13 +++++++++++++
clang/lib/Basic/SimpleTypoCorrection.cpp | 13 +++++++++++++
2 files changed, 26 insertions(+)
diff --git a/clang/include/clang/Basic/SimpleTypoCorrection.h b/clang/include/clang/Basic/SimpleTypoCorrection.h
index 4cd104f79aebe..49ef8bd0f3414 100644
--- a/clang/include/clang/Basic/SimpleTypoCorrection.h
+++ b/clang/include/clang/Basic/SimpleTypoCorrection.h
@@ -1,3 +1,16 @@
+//===- SimpleTypoCorrection.h - Basic typo correction utility -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the SimpleTypoCorrection class, which performs basic
+// typo correction using string similarity based on edit distance.
+//
+//===----------------------------------------------------------------------===//
+
#ifndef LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H
#define LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H
diff --git a/clang/lib/Basic/SimpleTypoCorrection.cpp b/clang/lib/Basic/SimpleTypoCorrection.cpp
index 86b86eeea2609..e41abf8609344 100644
--- a/clang/lib/Basic/SimpleTypoCorrection.cpp
+++ b/clang/lib/Basic/SimpleTypoCorrection.cpp
@@ -1,3 +1,16 @@
+//===- SimpleTypoCorrection.cpp - Basic typo correction utility -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the SimpleTypoCorrection class, which performs basic
+// typo correction using string similarity based on edit distance.
+//
+//===----------------------------------------------------------------------===//
+
#include "clang/Basic/SimpleTypoCorrection.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
More information about the cfe-commits
mailing list