Skip to content

Commit 50127ac

Browse files
authored
[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; } ```
1 parent 6eb4adf commit 50127ac

20 files changed

+345
-133
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,8 @@ Improvements to Clang's diagnostics
563563
- Fixed a crash when checking a ``__thread``-specified variable declaration
564564
with a dependent type in C++. (#GH140509)
565565

566+
- Clang now suggests corrections for unknown attribute names.
567+
566568
Improvements to Clang's time-trace
567569
----------------------------------
568570

clang/include/clang/Basic/AttributeCommonInfo.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace clang {
2121

2222
class ASTRecordWriter;
2323
class IdentifierInfo;
24+
class LangOptions;
25+
class TargetInfo;
2426

2527
class AttributeCommonInfo {
2628
public:
@@ -196,6 +198,9 @@ class AttributeCommonInfo {
196198
/// with surrounding underscores removed as appropriate (e.g.
197199
/// __gnu__::__attr__ will be normalized to gnu::attr).
198200
std::string getNormalizedFullName() const;
201+
std::optional<std::string>
202+
getCorrectedFullName(const TargetInfo &Target,
203+
const LangOptions &LangOpts) const;
199204
SourceRange getNormalizedRange() const;
200205

201206
bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; }

clang/include/clang/Basic/Attributes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class TargetInfo;
1919

2020
/// Return the version number associated with the attribute if we
2121
/// recognize and implement the attribute specified by the given information.
22+
int hasAttribute(AttributeCommonInfo::Syntax Syntax, llvm::StringRef ScopeName,
23+
llvm::StringRef AttrName, const TargetInfo &Target,
24+
const LangOptions &LangOpts, bool CheckPlugins);
25+
2226
int hasAttribute(AttributeCommonInfo::Syntax Syntax,
2327
const IdentifierInfo *Scope, const IdentifierInfo *Attr,
2428
const TargetInfo &Target, const LangOptions &LangOpts);

clang/include/clang/Basic/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ clang_tablegen(CXX11AttributeInfo.inc -gen-cxx11-attribute-info
7979
TARGET CXX11AttributeInfo
8080
)
8181

82+
clang_tablegen(AttributeSpellingList.inc -gen-attribute-spelling-list
83+
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
84+
SOURCE Attr.td
85+
TARGET AttributeSpellingList
86+
)
87+
8288
clang_tablegen(Builtins.inc -gen-clang-builtins
8389
SOURCE Builtins.td
8490
TARGET ClangBuiltins)

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ def err_opencl_unknown_type_specifier : Error<
181181

182182
def warn_unknown_attribute_ignored : Warning<
183183
"unknown attribute %0 ignored">, InGroup<UnknownAttributes>;
184+
def warn_unknown_attribute_ignored_suggestion : Warning<
185+
"unknown attribute %0 ignored; did you mean '%1'?">, InGroup<UnknownAttributes>;
184186
def warn_attribute_ignored : Warning<"%0 attribute ignored">,
185187
InGroup<IgnoredAttributes>;
186188
def err_keyword_not_supported_on_target : Error<
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===- SimpleTypoCorrection.h - Basic typo correction utility -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines the SimpleTypoCorrection class, which performs basic
10+
// typo correction using string similarity based on edit distance.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H
15+
#define LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H
16+
17+
#include "clang/Basic/LLVM.h"
18+
#include "llvm/ADT/StringRef.h"
19+
20+
namespace clang {
21+
22+
class IdentifierInfo;
23+
24+
class SimpleTypoCorrection {
25+
StringRef BestCandidate;
26+
StringRef Typo;
27+
28+
const unsigned MaxEditDistance;
29+
unsigned BestEditDistance;
30+
unsigned BestIndex;
31+
unsigned NextIndex;
32+
33+
public:
34+
explicit SimpleTypoCorrection(StringRef Typo)
35+
: BestCandidate(), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
36+
BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
37+
38+
void add(const StringRef Candidate);
39+
void add(const char *Candidate);
40+
void add(const IdentifierInfo *Candidate);
41+
42+
std::optional<StringRef> getCorrection() const;
43+
bool hasCorrection() const;
44+
unsigned getCorrectionIndex() const;
45+
};
46+
} // namespace clang
47+
48+
#endif // LLVM_CLANG_BASIC_SIMPLETYPOCORRECTION_H

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5033,6 +5033,8 @@ class Sema final : public SemaBase {
50335033
/// which might be lying around on it.
50345034
void checkUnusedDeclAttributes(Declarator &D);
50355035

5036+
void DiagnoseUnknownAttribute(const ParsedAttr &AL);
5037+
50365038
/// DeclClonePragmaWeak - clone existing decl (maybe definition),
50375039
/// \#pragma weak needs a non-definition decl and source may not have one.
50385040
NamedDecl *DeclClonePragmaWeak(NamedDecl *ND, const IdentifierInfo *II,

clang/lib/AST/CommentSema.cpp

Lines changed: 26 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/AST/DeclTemplate.h"
1414
#include "clang/Basic/DiagnosticComment.h"
1515
#include "clang/Basic/LLVM.h"
16+
#include "clang/Basic/SimpleTypoCorrection.h"
1617
#include "clang/Basic/SourceManager.h"
1718
#include "clang/Lex/Preprocessor.h"
1819
#include "llvm/ADT/StringSwitch.h"
@@ -975,69 +976,22 @@ unsigned Sema::resolveParmVarReference(StringRef Name,
975976
return ParamCommandComment::InvalidParamIndex;
976977
}
977978

978-
namespace {
979-
class SimpleTypoCorrector {
980-
const NamedDecl *BestDecl;
981-
982-
StringRef Typo;
983-
const unsigned MaxEditDistance;
984-
985-
unsigned BestEditDistance;
986-
unsigned BestIndex;
987-
unsigned NextIndex;
988-
989-
public:
990-
explicit SimpleTypoCorrector(StringRef Typo)
991-
: BestDecl(nullptr), Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
992-
BestEditDistance(MaxEditDistance + 1), BestIndex(0), NextIndex(0) {}
993-
994-
void addDecl(const NamedDecl *ND);
995-
996-
const NamedDecl *getBestDecl() const {
997-
if (BestEditDistance > MaxEditDistance)
998-
return nullptr;
999-
1000-
return BestDecl;
1001-
}
979+
unsigned
980+
Sema::correctTypoInParmVarReference(StringRef Typo,
981+
ArrayRef<const ParmVarDecl *> ParamVars) {
982+
SimpleTypoCorrection STC(Typo);
983+
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
984+
const ParmVarDecl *Param = ParamVars[i];
985+
if (!Param)
986+
continue;
1002987

1003-
unsigned getBestDeclIndex() const {
1004-
assert(getBestDecl());
1005-
return BestIndex;
988+
STC.add(Param->getIdentifier());
1006989
}
1007-
};
1008-
1009-
void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
1010-
unsigned CurrIndex = NextIndex++;
1011-
1012-
const IdentifierInfo *II = ND->getIdentifier();
1013-
if (!II)
1014-
return;
1015990

1016-
StringRef Name = II->getName();
1017-
unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
1018-
if (MinPossibleEditDistance > 0 &&
1019-
Typo.size() / MinPossibleEditDistance < 3)
1020-
return;
991+
if (STC.hasCorrection())
992+
return STC.getCorrectionIndex();
1021993

1022-
unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
1023-
if (EditDistance < BestEditDistance) {
1024-
BestEditDistance = EditDistance;
1025-
BestDecl = ND;
1026-
BestIndex = CurrIndex;
1027-
}
1028-
}
1029-
} // end anonymous namespace
1030-
1031-
unsigned Sema::correctTypoInParmVarReference(
1032-
StringRef Typo,
1033-
ArrayRef<const ParmVarDecl *> ParamVars) {
1034-
SimpleTypoCorrector Corrector(Typo);
1035-
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
1036-
Corrector.addDecl(ParamVars[i]);
1037-
if (Corrector.getBestDecl())
1038-
return Corrector.getBestDeclIndex();
1039-
else
1040-
return ParamCommandComment::InvalidParamIndex;
994+
return ParamCommandComment::InvalidParamIndex;
1041995
}
1042996

1043997
namespace {
@@ -1079,30 +1033,31 @@ bool Sema::resolveTParamReference(
10791033

10801034
namespace {
10811035
void CorrectTypoInTParamReferenceHelper(
1082-
const TemplateParameterList *TemplateParameters,
1083-
SimpleTypoCorrector &Corrector) {
1036+
const TemplateParameterList *TemplateParameters,
1037+
SimpleTypoCorrection &STC) {
10841038
for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
10851039
const NamedDecl *Param = TemplateParameters->getParam(i);
1086-
Corrector.addDecl(Param);
1040+
if (!Param)
1041+
continue;
1042+
1043+
STC.add(Param->getIdentifier());
10871044

10881045
if (const TemplateTemplateParmDecl *TTP =
10891046
dyn_cast<TemplateTemplateParmDecl>(Param))
1090-
CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
1091-
Corrector);
1047+
CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), STC);
10921048
}
10931049
}
10941050
} // end anonymous namespace
10951051

10961052
StringRef Sema::correctTypoInTParamReference(
10971053
StringRef Typo,
10981054
const TemplateParameterList *TemplateParameters) {
1099-
SimpleTypoCorrector Corrector(Typo);
1100-
CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
1101-
if (const NamedDecl *ND = Corrector.getBestDecl()) {
1102-
const IdentifierInfo *II = ND->getIdentifier();
1103-
assert(II && "SimpleTypoCorrector should not return this decl");
1104-
return II->getName();
1105-
}
1055+
SimpleTypoCorrection STC(Typo);
1056+
CorrectTypoInTParamReferenceHelper(TemplateParameters, STC);
1057+
1058+
if (auto CorrectedTParamReference = STC.getCorrection())
1059+
return *CorrectedTParamReference;
1060+
11061061
return StringRef();
11071062
}
11081063

0 commit comments

Comments
 (0)