mirror of
https://github.com/intel/llvm.git
synced 2026-01-15 12:25:46 +08:00
[Clang] add typo correction for unknown attribute names (#140629)
This patch enhances Clang's diagnosis for unknown attributes by
providing typo correction suggestions for known attributes.
```cpp
[[gmu::deprected]] // expected-warning {{unknown attribute 'gmu::deprected' ignored; did you mean 'gnu::deprecated'?}}
int f1(void) {
return 0;
}
[[deprected]] // expected-warning {{unknown attribute 'deprected' ignored; did you mean 'deprecated'?}}
int f2(void) {
return 0;
}
```
This commit is contained in:
@@ -563,6 +563,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
|
||||
----------------------------------
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace clang {
|
||||
|
||||
class ASTRecordWriter;
|
||||
class IdentifierInfo;
|
||||
class LangOptions;
|
||||
class TargetInfo;
|
||||
|
||||
class AttributeCommonInfo {
|
||||
public:
|
||||
@@ -196,6 +198,9 @@ public:
|
||||
/// 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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<
|
||||
|
||||
48
clang/include/clang/Basic/SimpleTypoCorrection.h
Normal file
48
clang/include/clang/Basic/SimpleTypoCorrection.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//===- 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
|
||||
|
||||
#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
|
||||
@@ -5033,6 +5033,8 @@ public:
|
||||
/// 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,
|
||||
|
||||
@@ -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 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,
|
||||
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;
|
||||
SimpleTypoCorrection STC(Typo);
|
||||
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
|
||||
const ParmVarDecl *Param = ParamVars[i];
|
||||
if (!Param)
|
||||
continue;
|
||||
|
||||
STC.add(Param->getIdentifier());
|
||||
}
|
||||
|
||||
if (STC.hasCorrection())
|
||||
return STC.getCorrectionIndex();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 StringRef canonicalizeScopeName(StringRef Name) {
|
||||
// Normalize the scope name, but only for gnu and clang attributes.
|
||||
if (Name == "__gnu__")
|
||||
return "gnu";
|
||||
|
||||
if (Name == "_Clang")
|
||||
return "clang";
|
||||
|
||||
return Name;
|
||||
}
|
||||
|
||||
static StringRef canonicalizeAttrName(StringRef Name) {
|
||||
// Normalize the attribute name, __foo__ becomes foo.
|
||||
if (Name.size() >= 4 && Name.starts_with("__") && Name.ends_with("__"))
|
||||
return Name.substr(2, Name.size() - 4);
|
||||
|
||||
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,
|
||||
const IdentifierInfo *Scope, const IdentifierInfo *Attr,
|
||||
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);
|
||||
|
||||
// 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";
|
||||
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,57 @@ 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"
|
||||
}
|
||||
|
||||
#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,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ add_clang_library(clangBasic
|
||||
SanitizerSpecialCaseList.cpp
|
||||
Sanitizers.cpp
|
||||
Sarif.cpp
|
||||
SimpleTypoCorrection.cpp
|
||||
SourceLocation.cpp
|
||||
SourceManager.cpp
|
||||
SourceMgrAdapter.cpp
|
||||
|
||||
63
clang/lib/Basic/SimpleTypoCorrection.cpp
Normal file
63
clang/lib/Basic/SimpleTypoCorrection.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
//===- 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"
|
||||
#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;
|
||||
}
|
||||
@@ -6869,9 +6869,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;
|
||||
}
|
||||
@@ -7867,6 +7865,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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}}
|
||||
}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -4125,6 +4125,37 @@ 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 << "#ifndef ATTR_NAME" << "\n";
|
||||
OS << "#define ATTR_NAME(NAME) NAME" << "\n";
|
||||
OS << "#endif" << "\n" << "\n";
|
||||
for (const auto &AttrName : AttrSpellingList) {
|
||||
OS << "ATTR_NAME(\"" << AttrName << "\")\n";
|
||||
}
|
||||
OS << "\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 << "ATTR_SCOPE_SCOPE(\"" << AttrScopeName << "\")\n";
|
||||
}
|
||||
OS << "\n";
|
||||
}
|
||||
|
||||
static bool isArgVariadic(const Record &R, StringRef AttrName) {
|
||||
return createArgument(R, AttrName)->isVariadic();
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ enum ActionType {
|
||||
GenClangOpenCLBuiltinHeader,
|
||||
GenClangOpenCLBuiltinTests,
|
||||
GenCXX11AttributeInfo,
|
||||
GenAttributeSpellingList,
|
||||
GenArmNeon,
|
||||
GenArmFP16,
|
||||
GenArmBF16,
|
||||
@@ -243,6 +244,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"),
|
||||
@@ -363,6 +366,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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user