[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:
Oleksandr T.
2025-05-22 17:03:32 +03:00
committed by GitHub
parent 6eb4adf746
commit 50127ac054
20 changed files with 348 additions and 136 deletions

View File

@@ -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
----------------------------------

View File

@@ -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; }

View File

@@ -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);

View File

@@ -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)

View File

@@ -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<

View 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

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -86,6 +86,7 @@ add_clang_library(clangBasic
SanitizerSpecialCaseList.cpp
Sanitizers.cpp
Sarif.cpp
SimpleTypoCorrection.cpp
SourceLocation.cpp
SourceManager.cpp
SourceMgrAdapter.cpp

View 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;
}

View File

@@ -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));

View File

@@ -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();

View File

@@ -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}}
}

View File

@@ -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}}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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,