forked from OSchip/llvm-project
Factor out renaming logic from readability-identifier-naming
Before this patch, readability-identifier-naming contained a significant amount of logic for (a) checking the style of identifiers, followed by (b) renaming/ applying fix-its. This patch factors out (b) into a separate base class so that it can be reused by other checks that want to do renaming. This also cleans up readability-identifier-naming significantly, since now it only needs to be concerned with the interesting details of (a).
This commit is contained in:
parent
c299d1981d
commit
d5c6b8407c
|
@ -22,44 +22,6 @@
|
|||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace llvm {
|
||||
/// Specialisation of DenseMapInfo to allow NamingCheckId objects in DenseMaps
|
||||
template <>
|
||||
struct DenseMapInfo<
|
||||
clang::tidy::readability::IdentifierNamingCheck::NamingCheckId> {
|
||||
using NamingCheckId =
|
||||
clang::tidy::readability::IdentifierNamingCheck::NamingCheckId;
|
||||
|
||||
static inline NamingCheckId getEmptyKey() {
|
||||
return NamingCheckId(
|
||||
clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-1)),
|
||||
"EMPTY");
|
||||
}
|
||||
|
||||
static inline NamingCheckId getTombstoneKey() {
|
||||
return NamingCheckId(
|
||||
clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-2)),
|
||||
"TOMBSTONE");
|
||||
}
|
||||
|
||||
static unsigned getHashValue(NamingCheckId Val) {
|
||||
assert(Val != getEmptyKey() && "Cannot hash the empty key!");
|
||||
assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
|
||||
|
||||
std::hash<NamingCheckId::second_type> SecondHash;
|
||||
return Val.first.getRawEncoding() + SecondHash(Val.second);
|
||||
}
|
||||
|
||||
static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) {
|
||||
if (RHS == getEmptyKey())
|
||||
return LHS == getEmptyKey();
|
||||
if (RHS == getTombstoneKey())
|
||||
return LHS == getTombstoneKey();
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
namespace readability {
|
||||
|
@ -164,7 +126,7 @@ private:
|
|||
|
||||
IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {
|
||||
: RenamerClangTidyCheck(Name, Context) {
|
||||
auto const fromString = [](StringRef Str) {
|
||||
return llvm::StringSwitch<llvm::Optional<CaseType>>(Str)
|
||||
.Case("aNy_CasE", CT_AnyCase)
|
||||
|
@ -233,39 +195,6 @@ void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|||
Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(namedDecl().bind("decl"), this);
|
||||
Finder->addMatcher(usingDecl().bind("using"), this);
|
||||
Finder->addMatcher(declRefExpr().bind("declRef"), this);
|
||||
Finder->addMatcher(cxxConstructorDecl(unless(isImplicit())).bind("classRef"),
|
||||
this);
|
||||
Finder->addMatcher(cxxDestructorDecl(unless(isImplicit())).bind("classRef"),
|
||||
this);
|
||||
Finder->addMatcher(typeLoc().bind("typeLoc"), this);
|
||||
Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this);
|
||||
Finder->addMatcher(
|
||||
functionDecl(unless(cxxMethodDecl(isImplicit())),
|
||||
hasBody(forEachDescendant(memberExpr().bind("memberExpr")))),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
cxxConstructorDecl(
|
||||
unless(isImplicit()),
|
||||
forEachConstructorInitializer(
|
||||
allOf(isWritten(), withInitializer(forEachDescendant(
|
||||
memberExpr().bind("memberExpr")))))),
|
||||
this);
|
||||
Finder->addMatcher(fieldDecl(hasInClassInitializer(
|
||||
forEachDescendant(memberExpr().bind("memberExpr")))),
|
||||
this);
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::registerPPCallbacks(
|
||||
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
|
||||
ModuleExpanderPP->addPPCallbacks(
|
||||
std::make_unique<IdentifierNamingCheckPPCallbacks>(ModuleExpanderPP,
|
||||
this));
|
||||
}
|
||||
|
||||
static bool matchesStyle(StringRef Name,
|
||||
IdentifierNamingCheck::NamingStyle Style) {
|
||||
static llvm::Regex Matchers[] = {
|
||||
|
@ -683,243 +612,47 @@ static StyleKind findStyleKind(
|
|||
return SK_Invalid;
|
||||
}
|
||||
|
||||
static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
|
||||
const IdentifierNamingCheck::NamingCheckId &Decl,
|
||||
SourceRange Range, SourceManager *SourceMgr = nullptr) {
|
||||
// Do nothing if the provided range is invalid.
|
||||
if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid())
|
||||
return;
|
||||
llvm::Optional<RenamerClangTidyCheck::FailureInfo>
|
||||
IdentifierNamingCheck::GetDeclFailureInfo(const NamedDecl *Decl,
|
||||
const SourceManager &SM) const {
|
||||
StyleKind SK = findStyleKind(Decl, NamingStyles);
|
||||
if (SK == SK_Invalid)
|
||||
return None;
|
||||
|
||||
// If we have a source manager, use it to convert to the spelling location for
|
||||
// performing the fix. This is necessary because macros can map the same
|
||||
// spelling location to different source locations, and we only want to fix
|
||||
// the token once, before it is expanded by the macro.
|
||||
SourceLocation FixLocation = Range.getBegin();
|
||||
if (SourceMgr)
|
||||
FixLocation = SourceMgr->getSpellingLoc(FixLocation);
|
||||
if (FixLocation.isInvalid())
|
||||
return;
|
||||
if (!NamingStyles[SK])
|
||||
return None;
|
||||
|
||||
// Try to insert the identifier location in the Usages map, and bail out if it
|
||||
// is already in there
|
||||
auto &Failure = Failures[Decl];
|
||||
if (!Failure.RawUsageLocs.insert(FixLocation.getRawEncoding()).second)
|
||||
return;
|
||||
const NamingStyle &Style = *NamingStyles[SK];
|
||||
StringRef Name = Decl->getName();
|
||||
if (matchesStyle(Name, Style))
|
||||
return None;
|
||||
|
||||
if (!Failure.ShouldFix())
|
||||
return;
|
||||
std::string KindName = fixupWithCase(StyleNames[SK], CT_LowerCase);
|
||||
std::replace(KindName.begin(), KindName.end(), '_', ' ');
|
||||
|
||||
if (!utils::rangeCanBeFixed(Range, SourceMgr))
|
||||
Failure.FixStatus = IdentifierNamingCheck::ShouldFixStatus::InsideMacro;
|
||||
std::string Fixup = fixupWithStyle(Name, Style);
|
||||
if (StringRef(Fixup).equals(Name)) {
|
||||
if (!IgnoreFailedSplit) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< Decl->getBeginLoc().printToString(SM)
|
||||
<< llvm::format(": unable to split words for %s '%s'\n",
|
||||
KindName.c_str(), Name.str().c_str()));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
return FailureInfo{std::move(KindName), std::move(Fixup)};
|
||||
}
|
||||
|
||||
/// Convenience method when the usage to be added is a NamedDecl
|
||||
static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
|
||||
const NamedDecl *Decl, SourceRange Range,
|
||||
SourceManager *SourceMgr = nullptr) {
|
||||
return addUsage(Failures,
|
||||
IdentifierNamingCheck::NamingCheckId(Decl->getLocation(),
|
||||
Decl->getNameAsString()),
|
||||
Range, SourceMgr);
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (const auto *Decl =
|
||||
Result.Nodes.getNodeAs<CXXConstructorDecl>("classRef")) {
|
||||
|
||||
addUsage(NamingCheckFailures, Decl->getParent(),
|
||||
Decl->getNameInfo().getSourceRange());
|
||||
|
||||
for (const auto *Init : Decl->inits()) {
|
||||
if (!Init->isWritten() || Init->isInClassMemberInitializer())
|
||||
continue;
|
||||
if (const auto *FD = Init->getAnyMember())
|
||||
addUsage(NamingCheckFailures, FD,
|
||||
SourceRange(Init->getMemberLocation()));
|
||||
// Note: delegating constructors and base class initializers are handled
|
||||
// via the "typeLoc" matcher.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Decl =
|
||||
Result.Nodes.getNodeAs<CXXDestructorDecl>("classRef")) {
|
||||
|
||||
SourceRange Range = Decl->getNameInfo().getSourceRange();
|
||||
if (Range.getBegin().isInvalid())
|
||||
return;
|
||||
// The first token that will be found is the ~ (or the equivalent trigraph),
|
||||
// we want instead to replace the next token, that will be the identifier.
|
||||
Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());
|
||||
|
||||
addUsage(NamingCheckFailures, Decl->getParent(), Range);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Loc = Result.Nodes.getNodeAs<TypeLoc>("typeLoc")) {
|
||||
NamedDecl *Decl = nullptr;
|
||||
if (const auto &Ref = Loc->getAs<TagTypeLoc>()) {
|
||||
Decl = Ref.getDecl();
|
||||
} else if (const auto &Ref = Loc->getAs<InjectedClassNameTypeLoc>()) {
|
||||
Decl = Ref.getDecl();
|
||||
} else if (const auto &Ref = Loc->getAs<UnresolvedUsingTypeLoc>()) {
|
||||
Decl = Ref.getDecl();
|
||||
} else if (const auto &Ref = Loc->getAs<TemplateTypeParmTypeLoc>()) {
|
||||
Decl = Ref.getDecl();
|
||||
}
|
||||
|
||||
if (Decl) {
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto &Ref = Loc->getAs<TemplateSpecializationTypeLoc>()) {
|
||||
const auto *Decl =
|
||||
Ref.getTypePtr()->getTemplateName().getAsTemplateDecl();
|
||||
|
||||
SourceRange Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc());
|
||||
if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) {
|
||||
if (const auto *TemplDecl = ClassDecl->getTemplatedDecl())
|
||||
addUsage(NamingCheckFailures, TemplDecl, Range);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto &Ref =
|
||||
Loc->getAs<DependentTemplateSpecializationTypeLoc>()) {
|
||||
if (const auto *Decl = Ref.getTypePtr()->getAsTagDecl())
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *Loc =
|
||||
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("nestedNameLoc")) {
|
||||
if (NestedNameSpecifier *Spec = Loc->getNestedNameSpecifier()) {
|
||||
if (NamespaceDecl *Decl = Spec->getAsNamespace()) {
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getLocalSourceRange());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *Decl = Result.Nodes.getNodeAs<UsingDecl>("using")) {
|
||||
for (const auto *Shadow : Decl->shadows()) {
|
||||
addUsage(NamingCheckFailures, Shadow->getTargetDecl(),
|
||||
Decl->getNameInfo().getSourceRange());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declRef")) {
|
||||
SourceRange Range = DeclRef->getNameInfo().getSourceRange();
|
||||
addUsage(NamingCheckFailures, DeclRef->getDecl(), Range,
|
||||
Result.SourceManager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *MemberRef =
|
||||
Result.Nodes.getNodeAs<MemberExpr>("memberExpr")) {
|
||||
SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange();
|
||||
addUsage(NamingCheckFailures, MemberRef->getMemberDecl(), Range,
|
||||
Result.SourceManager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) {
|
||||
if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit())
|
||||
return;
|
||||
|
||||
// Fix type aliases in value declarations
|
||||
if (const auto *Value = Result.Nodes.getNodeAs<ValueDecl>("decl")) {
|
||||
if (const auto *TypePtr = Value->getType().getTypePtrOrNull()) {
|
||||
if (const auto *Typedef = TypePtr->getAs<TypedefType>()) {
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix type aliases in function declarations
|
||||
if (const auto *Value = Result.Nodes.getNodeAs<FunctionDecl>("decl")) {
|
||||
if (const auto *Typedef =
|
||||
Value->getReturnType().getTypePtr()->getAs<TypedefType>()) {
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
}
|
||||
for (unsigned i = 0; i < Value->getNumParams(); ++i) {
|
||||
if (const auto *Typedef = Value->parameters()[i]
|
||||
->getType()
|
||||
.getTypePtr()
|
||||
->getAs<TypedefType>()) {
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore ClassTemplateSpecializationDecl which are creating duplicate
|
||||
// replacements with CXXRecordDecl
|
||||
if (isa<ClassTemplateSpecializationDecl>(Decl))
|
||||
return;
|
||||
|
||||
StyleKind SK = findStyleKind(Decl, NamingStyles);
|
||||
if (SK == SK_Invalid)
|
||||
return;
|
||||
|
||||
if (!NamingStyles[SK])
|
||||
return;
|
||||
|
||||
const NamingStyle &Style = *NamingStyles[SK];
|
||||
StringRef Name = Decl->getName();
|
||||
if (matchesStyle(Name, Style))
|
||||
return;
|
||||
|
||||
std::string KindName = fixupWithCase(StyleNames[SK], CT_LowerCase);
|
||||
std::replace(KindName.begin(), KindName.end(), '_', ' ');
|
||||
|
||||
std::string Fixup = fixupWithStyle(Name, Style);
|
||||
if (StringRef(Fixup).equals(Name)) {
|
||||
if (!IgnoreFailedSplit) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< Decl->getBeginLoc().printToString(*Result.SourceManager)
|
||||
<< llvm::format(": unable to split words for %s '%s'\n",
|
||||
KindName.c_str(), Name.str().c_str()));
|
||||
}
|
||||
} else {
|
||||
NamingCheckFailure &Failure = NamingCheckFailures[NamingCheckId(
|
||||
Decl->getLocation(), Decl->getNameAsString())];
|
||||
SourceRange Range =
|
||||
DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
|
||||
.getSourceRange();
|
||||
|
||||
const IdentifierTable &Idents = Decl->getASTContext().Idents;
|
||||
auto CheckNewIdentifier = Idents.find(Fixup);
|
||||
if (CheckNewIdentifier != Idents.end()) {
|
||||
const IdentifierInfo *Ident = CheckNewIdentifier->second;
|
||||
if (Ident->isKeyword(getLangOpts()))
|
||||
Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword;
|
||||
else if (Ident->hasMacroDefinition())
|
||||
Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition;
|
||||
}
|
||||
|
||||
Failure.Fixup = std::move(Fixup);
|
||||
Failure.KindName = std::move(KindName);
|
||||
addUsage(NamingCheckFailures, Decl, Range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr,
|
||||
const Token &MacroNameTok,
|
||||
const MacroInfo *MI) {
|
||||
llvm::Optional<RenamerClangTidyCheck::FailureInfo>
|
||||
IdentifierNamingCheck::GetMacroFailureInfo(const Token &MacroNameTok,
|
||||
const SourceManager &SM) const {
|
||||
if (!NamingStyles[SK_MacroDefinition])
|
||||
return;
|
||||
return None;
|
||||
|
||||
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
|
||||
const NamingStyle &Style = *NamingStyles[SK_MacroDefinition];
|
||||
if (matchesStyle(Name, Style))
|
||||
return;
|
||||
return None;
|
||||
|
||||
std::string KindName =
|
||||
fixupWithCase(StyleNames[SK_MacroDefinition], CT_LowerCase);
|
||||
|
@ -929,74 +662,22 @@ void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr,
|
|||
if (StringRef(Fixup).equals(Name)) {
|
||||
if (!IgnoreFailedSplit) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< MacroNameTok.getLocation().printToString(SourceMgr)
|
||||
<< MacroNameTok.getLocation().printToString(SM)
|
||||
<< llvm::format(": unable to split words for %s '%s'\n",
|
||||
KindName.c_str(), Name.str().c_str()));
|
||||
}
|
||||
} else {
|
||||
NamingCheckId ID(MI->getDefinitionLoc(), Name);
|
||||
NamingCheckFailure &Failure = NamingCheckFailures[ID];
|
||||
SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
|
||||
|
||||
Failure.Fixup = std::move(Fixup);
|
||||
Failure.KindName = std::move(KindName);
|
||||
addUsage(NamingCheckFailures, ID, Range);
|
||||
return None;
|
||||
}
|
||||
return FailureInfo{std::move(KindName), std::move(Fixup)};
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::expandMacro(const Token &MacroNameTok,
|
||||
const MacroInfo *MI) {
|
||||
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
|
||||
NamingCheckId ID(MI->getDefinitionLoc(), Name);
|
||||
|
||||
auto Failure = NamingCheckFailures.find(ID);
|
||||
if (Failure == NamingCheckFailures.end())
|
||||
return;
|
||||
|
||||
SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
|
||||
addUsage(NamingCheckFailures, ID, Range);
|
||||
}
|
||||
|
||||
void IdentifierNamingCheck::onEndOfTranslationUnit() {
|
||||
for (const auto &Pair : NamingCheckFailures) {
|
||||
const NamingCheckId &Decl = Pair.first;
|
||||
const NamingCheckFailure &Failure = Pair.second;
|
||||
|
||||
if (Failure.KindName.empty())
|
||||
continue;
|
||||
|
||||
if (Failure.ShouldNotify()) {
|
||||
auto Diag =
|
||||
diag(Decl.first,
|
||||
"invalid case style for %0 '%1'%select{|" // Case 0 is empty on
|
||||
// purpose, because we
|
||||
// intent to provide a
|
||||
// fix
|
||||
"; cannot be fixed because '%3' would conflict with a keyword|"
|
||||
"; cannot be fixed because '%3' would conflict with a macro "
|
||||
"definition}2")
|
||||
<< Failure.KindName << Decl.second
|
||||
<< static_cast<int>(Failure.FixStatus) << Failure.Fixup;
|
||||
|
||||
if (Failure.ShouldFix()) {
|
||||
for (const auto &Loc : Failure.RawUsageLocs) {
|
||||
// We assume that the identifier name is made of one token only. This
|
||||
// is always the case as we ignore usages in macros that could build
|
||||
// identifier names by combining multiple tokens.
|
||||
//
|
||||
// For destructors, we already take care of it by remembering the
|
||||
// location of the start of the identifier and not the start of the
|
||||
// tilde.
|
||||
//
|
||||
// Other multi-token identifiers, such as operators are not checked at
|
||||
// all.
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
SourceRange(SourceLocation::getFromRawEncoding(Loc)),
|
||||
Failure.Fixup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenamerClangTidyCheck::DiagInfo
|
||||
IdentifierNamingCheck::GetDiagInfo(const NamingCheckId &ID,
|
||||
const NamingCheckFailure &Failure) const {
|
||||
return DiagInfo{"invalid case style for %0 '%1'",
|
||||
[&](DiagnosticBuilder &diag) {
|
||||
diag << Failure.Info.KindName << ID.second;
|
||||
}};
|
||||
}
|
||||
|
||||
} // namespace readability
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
|
||||
#include "../utils/RenamerClangTidyCheck.h"
|
||||
namespace clang {
|
||||
|
||||
class MacroInfo;
|
||||
|
@ -31,17 +30,12 @@ namespace readability {
|
|||
/// different rules for different kind of identifier. In general, the
|
||||
/// rules are falling back to a more generic rule if the specific case is not
|
||||
/// configured.
|
||||
class IdentifierNamingCheck : public ClangTidyCheck {
|
||||
class IdentifierNamingCheck final : public RenamerClangTidyCheck {
|
||||
public:
|
||||
IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context);
|
||||
~IdentifierNamingCheck();
|
||||
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
|
||||
Preprocessor *ModuleExpanderPP) override;
|
||||
void onEndOfTranslationUnit() override;
|
||||
|
||||
enum CaseType {
|
||||
CT_AnyCase = 0,
|
||||
|
@ -65,66 +59,18 @@ public:
|
|||
std::string Suffix;
|
||||
};
|
||||
|
||||
/// This enum will be used in %select of the diagnostic message.
|
||||
/// Each value below IgnoreFailureThreshold should have an error message.
|
||||
enum class ShouldFixStatus {
|
||||
ShouldFix,
|
||||
ConflictsWithKeyword, /// The fixup will conflict with a language keyword,
|
||||
/// so we can't fix it automatically.
|
||||
ConflictsWithMacroDefinition, /// The fixup will conflict with a macro
|
||||
/// definition, so we can't fix it
|
||||
/// automatically.
|
||||
|
||||
/// Values pass this threshold will be ignored completely
|
||||
/// i.e no message, no fixup.
|
||||
IgnoreFailureThreshold,
|
||||
|
||||
InsideMacro, /// If the identifier was used or declared within a macro we
|
||||
/// won't offer a fixup for safety reasons.
|
||||
};
|
||||
|
||||
/// Holds an identifier name check failure, tracking the kind of the
|
||||
/// identifier, its possible fixup and the starting locations of all the
|
||||
/// identifier usages.
|
||||
struct NamingCheckFailure {
|
||||
std::string KindName;
|
||||
std::string Fixup;
|
||||
|
||||
/// Whether the failure should be fixed or not.
|
||||
///
|
||||
/// ie: if the identifier was used or declared within a macro we won't offer
|
||||
/// a fixup for safety reasons.
|
||||
bool ShouldFix() const { return FixStatus == ShouldFixStatus::ShouldFix; }
|
||||
|
||||
bool ShouldNotify() const {
|
||||
return FixStatus < ShouldFixStatus::IgnoreFailureThreshold;
|
||||
}
|
||||
|
||||
ShouldFixStatus FixStatus = ShouldFixStatus::ShouldFix;
|
||||
|
||||
/// A set of all the identifier usages starting SourceLocation, in
|
||||
/// their encoded form.
|
||||
llvm::DenseSet<unsigned> RawUsageLocs;
|
||||
|
||||
NamingCheckFailure() = default;
|
||||
};
|
||||
|
||||
typedef std::pair<SourceLocation, std::string> NamingCheckId;
|
||||
|
||||
typedef llvm::DenseMap<NamingCheckId, NamingCheckFailure>
|
||||
NamingCheckFailureMap;
|
||||
|
||||
/// Check Macros for style violations.
|
||||
void checkMacro(SourceManager &sourceMgr, const Token &MacroNameTok,
|
||||
const MacroInfo *MI);
|
||||
|
||||
/// Add a usage of a macro if it already has a violation.
|
||||
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
|
||||
|
||||
private:
|
||||
llvm::Optional<FailureInfo>
|
||||
GetDeclFailureInfo(const NamedDecl *Decl,
|
||||
const SourceManager &SM) const override;
|
||||
llvm::Optional<FailureInfo>
|
||||
GetMacroFailureInfo(const Token &MacroNameTok,
|
||||
const SourceManager &SM) const override;
|
||||
DiagInfo GetDiagInfo(const NamingCheckId &ID,
|
||||
const NamingCheckFailure &Failure) const override;
|
||||
|
||||
std::vector<llvm::Optional<NamingStyle>> NamingStyles;
|
||||
bool IgnoreFailedSplit;
|
||||
NamingCheckFailureMap NamingCheckFailures;
|
||||
};
|
||||
|
||||
} // namespace readability
|
||||
|
|
|
@ -13,6 +13,7 @@ add_clang_library(clangTidyUtils
|
|||
LexerUtils.cpp
|
||||
NamespaceAliaser.cpp
|
||||
OptionsUtils.cpp
|
||||
RenamerClangTidyCheck.cpp
|
||||
TransformerClangTidyCheck.cpp
|
||||
TypeTraits.cpp
|
||||
UsingInserter.cpp
|
||||
|
|
|
@ -0,0 +1,422 @@
|
|||
//===--- RenamerClangTidyCheck.cpp - clang-tidy ---------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "RenamerClangTidyCheck.h"
|
||||
#include "ASTUtils.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Lex/PPCallbacks.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
||||
#define DEBUG_TYPE "clang-tidy"
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Specialisation of DenseMapInfo to allow NamingCheckId objects in DenseMaps
|
||||
template <>
|
||||
struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> {
|
||||
using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId;
|
||||
|
||||
static inline NamingCheckId getEmptyKey() {
|
||||
return NamingCheckId(
|
||||
clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-1)),
|
||||
"EMPTY");
|
||||
}
|
||||
|
||||
static inline NamingCheckId getTombstoneKey() {
|
||||
return NamingCheckId(
|
||||
clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-2)),
|
||||
"TOMBSTONE");
|
||||
}
|
||||
|
||||
static unsigned getHashValue(NamingCheckId Val) {
|
||||
assert(Val != getEmptyKey() && "Cannot hash the empty key!");
|
||||
assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
|
||||
|
||||
std::hash<NamingCheckId::second_type> SecondHash;
|
||||
return Val.first.getRawEncoding() + SecondHash(Val.second);
|
||||
}
|
||||
|
||||
static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) {
|
||||
if (RHS == getEmptyKey())
|
||||
return LHS == getEmptyKey();
|
||||
if (RHS == getTombstoneKey())
|
||||
return LHS == getTombstoneKey();
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
namespace tidy {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Callback supplies macros to RenamerClangTidyCheck::checkMacro
|
||||
class RenamerClangTidyCheckPPCallbacks : public PPCallbacks {
|
||||
public:
|
||||
RenamerClangTidyCheckPPCallbacks(Preprocessor *PP,
|
||||
RenamerClangTidyCheck *Check)
|
||||
: PP(PP), Check(Check) {}
|
||||
|
||||
/// MacroDefined calls checkMacro for macros in the main file
|
||||
void MacroDefined(const Token &MacroNameTok,
|
||||
const MacroDirective *MD) override {
|
||||
Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo());
|
||||
}
|
||||
|
||||
/// MacroExpands calls expandMacro for macros in the main file
|
||||
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
|
||||
SourceRange /*Range*/,
|
||||
const MacroArgs * /*Args*/) override {
|
||||
Check->expandMacro(MacroNameTok, MD.getMacroInfo());
|
||||
}
|
||||
|
||||
private:
|
||||
Preprocessor *PP;
|
||||
RenamerClangTidyCheck *Check;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(CheckName, Context) {}
|
||||
RenamerClangTidyCheck::~RenamerClangTidyCheck() = default;
|
||||
|
||||
void RenamerClangTidyCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Finder->addMatcher(namedDecl().bind("decl"), this);
|
||||
Finder->addMatcher(usingDecl().bind("using"), this);
|
||||
Finder->addMatcher(declRefExpr().bind("declRef"), this);
|
||||
Finder->addMatcher(cxxConstructorDecl(unless(isImplicit())).bind("classRef"),
|
||||
this);
|
||||
Finder->addMatcher(cxxDestructorDecl(unless(isImplicit())).bind("classRef"),
|
||||
this);
|
||||
Finder->addMatcher(typeLoc().bind("typeLoc"), this);
|
||||
Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this);
|
||||
Finder->addMatcher(
|
||||
functionDecl(unless(cxxMethodDecl(isImplicit())),
|
||||
hasBody(forEachDescendant(memberExpr().bind("memberExpr")))),
|
||||
this);
|
||||
Finder->addMatcher(
|
||||
cxxConstructorDecl(
|
||||
unless(isImplicit()),
|
||||
forEachConstructorInitializer(
|
||||
allOf(isWritten(), withInitializer(forEachDescendant(
|
||||
memberExpr().bind("memberExpr")))))),
|
||||
this);
|
||||
Finder->addMatcher(fieldDecl(hasInClassInitializer(
|
||||
forEachDescendant(memberExpr().bind("memberExpr")))),
|
||||
this);
|
||||
}
|
||||
|
||||
void RenamerClangTidyCheck::registerPPCallbacks(
|
||||
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
|
||||
ModuleExpanderPP->addPPCallbacks(
|
||||
std::make_unique<RenamerClangTidyCheckPPCallbacks>(ModuleExpanderPP,
|
||||
this));
|
||||
}
|
||||
|
||||
static void addUsage(RenamerClangTidyCheck::NamingCheckFailureMap &Failures,
|
||||
const RenamerClangTidyCheck::NamingCheckId &Decl,
|
||||
SourceRange Range, SourceManager *SourceMgr = nullptr) {
|
||||
// Do nothing if the provided range is invalid.
|
||||
if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid())
|
||||
return;
|
||||
|
||||
// If we have a source manager, use it to convert to the spelling location for
|
||||
// performing the fix. This is necessary because macros can map the same
|
||||
// spelling location to different source locations, and we only want to fix
|
||||
// the token once, before it is expanded by the macro.
|
||||
SourceLocation FixLocation = Range.getBegin();
|
||||
if (SourceMgr)
|
||||
FixLocation = SourceMgr->getSpellingLoc(FixLocation);
|
||||
if (FixLocation.isInvalid())
|
||||
return;
|
||||
|
||||
// Try to insert the identifier location in the Usages map, and bail out if it
|
||||
// is already in there
|
||||
RenamerClangTidyCheck::NamingCheckFailure &Failure = Failures[Decl];
|
||||
if (!Failure.RawUsageLocs.insert(FixLocation.getRawEncoding()).second)
|
||||
return;
|
||||
|
||||
if (!Failure.ShouldFix())
|
||||
return;
|
||||
|
||||
if (!utils::rangeCanBeFixed(Range, SourceMgr))
|
||||
Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro;
|
||||
}
|
||||
|
||||
/// Convenience method when the usage to be added is a NamedDecl
|
||||
static void addUsage(RenamerClangTidyCheck::NamingCheckFailureMap &Failures,
|
||||
const NamedDecl *Decl, SourceRange Range,
|
||||
SourceManager *SourceMgr = nullptr) {
|
||||
return addUsage(Failures,
|
||||
RenamerClangTidyCheck::NamingCheckId(Decl->getLocation(),
|
||||
Decl->getNameAsString()),
|
||||
Range, SourceMgr);
|
||||
}
|
||||
|
||||
void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (const auto *Decl =
|
||||
Result.Nodes.getNodeAs<CXXConstructorDecl>("classRef")) {
|
||||
|
||||
addUsage(NamingCheckFailures, Decl->getParent(),
|
||||
Decl->getNameInfo().getSourceRange());
|
||||
|
||||
for (const auto *Init : Decl->inits()) {
|
||||
if (!Init->isWritten() || Init->isInClassMemberInitializer())
|
||||
continue;
|
||||
if (const FieldDecl *FD = Init->getAnyMember())
|
||||
addUsage(NamingCheckFailures, FD,
|
||||
SourceRange(Init->getMemberLocation()));
|
||||
// Note: delegating constructors and base class initializers are handled
|
||||
// via the "typeLoc" matcher.
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Decl =
|
||||
Result.Nodes.getNodeAs<CXXDestructorDecl>("classRef")) {
|
||||
|
||||
SourceRange Range = Decl->getNameInfo().getSourceRange();
|
||||
if (Range.getBegin().isInvalid())
|
||||
return;
|
||||
// The first token that will be found is the ~ (or the equivalent trigraph),
|
||||
// we want instead to replace the next token, that will be the identifier.
|
||||
Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());
|
||||
|
||||
addUsage(NamingCheckFailures, Decl->getParent(), Range);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Loc = Result.Nodes.getNodeAs<TypeLoc>("typeLoc")) {
|
||||
NamedDecl *Decl = nullptr;
|
||||
if (const auto &Ref = Loc->getAs<TagTypeLoc>())
|
||||
Decl = Ref.getDecl();
|
||||
else if (const auto &Ref = Loc->getAs<InjectedClassNameTypeLoc>())
|
||||
Decl = Ref.getDecl();
|
||||
else if (const auto &Ref = Loc->getAs<UnresolvedUsingTypeLoc>())
|
||||
Decl = Ref.getDecl();
|
||||
else if (const auto &Ref = Loc->getAs<TemplateTypeParmTypeLoc>())
|
||||
Decl = Ref.getDecl();
|
||||
// further TypeLocs handled below
|
||||
|
||||
if (Decl) {
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto &Ref = Loc->getAs<TemplateSpecializationTypeLoc>()) {
|
||||
const TemplateDecl *Decl =
|
||||
Ref.getTypePtr()->getTemplateName().getAsTemplateDecl();
|
||||
|
||||
SourceRange Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc());
|
||||
if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) {
|
||||
if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl())
|
||||
addUsage(NamingCheckFailures, TemplDecl, Range);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto &Ref =
|
||||
Loc->getAs<DependentTemplateSpecializationTypeLoc>()) {
|
||||
if (const TagDecl *Decl = Ref.getTypePtr()->getAsTagDecl())
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *Loc =
|
||||
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("nestedNameLoc")) {
|
||||
if (const NestedNameSpecifier *Spec = Loc->getNestedNameSpecifier()) {
|
||||
if (const NamespaceDecl *Decl = Spec->getAsNamespace()) {
|
||||
addUsage(NamingCheckFailures, Decl, Loc->getLocalSourceRange());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto *Decl = Result.Nodes.getNodeAs<UsingDecl>("using")) {
|
||||
for (const auto *Shadow : Decl->shadows())
|
||||
addUsage(NamingCheckFailures, Shadow->getTargetDecl(),
|
||||
Decl->getNameInfo().getSourceRange());
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declRef")) {
|
||||
SourceRange Range = DeclRef->getNameInfo().getSourceRange();
|
||||
addUsage(NamingCheckFailures, DeclRef->getDecl(), Range,
|
||||
Result.SourceManager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *MemberRef =
|
||||
Result.Nodes.getNodeAs<MemberExpr>("memberExpr")) {
|
||||
SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange();
|
||||
addUsage(NamingCheckFailures, MemberRef->getMemberDecl(), Range,
|
||||
Result.SourceManager);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) {
|
||||
if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit())
|
||||
return;
|
||||
|
||||
// Fix type aliases in value declarations.
|
||||
if (const auto *Value = Result.Nodes.getNodeAs<ValueDecl>("decl")) {
|
||||
if (const Type *TypePtr = Value->getType().getTypePtrOrNull()) {
|
||||
if (const auto *Typedef = TypePtr->getAs<TypedefType>())
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
}
|
||||
}
|
||||
|
||||
// Fix type aliases in function declarations.
|
||||
if (const auto *Value = Result.Nodes.getNodeAs<FunctionDecl>("decl")) {
|
||||
if (const auto *Typedef =
|
||||
Value->getReturnType().getTypePtr()->getAs<TypedefType>())
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
for (unsigned i = 0; i < Value->getNumParams(); ++i) {
|
||||
if (const TypedefType *Typedef = Value->parameters()[i]
|
||||
->getType()
|
||||
.getTypePtr()
|
||||
->getAs<TypedefType>())
|
||||
addUsage(NamingCheckFailures, Typedef->getDecl(),
|
||||
Value->getSourceRange());
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore ClassTemplateSpecializationDecl which are creating duplicate
|
||||
// replacements with CXXRecordDecl.
|
||||
if (isa<ClassTemplateSpecializationDecl>(Decl))
|
||||
return;
|
||||
|
||||
Optional<FailureInfo> MaybeFailure =
|
||||
GetDeclFailureInfo(Decl, *Result.SourceManager);
|
||||
if (!MaybeFailure)
|
||||
return;
|
||||
FailureInfo &Info = *MaybeFailure;
|
||||
NamingCheckFailure &Failure = NamingCheckFailures[NamingCheckId(
|
||||
Decl->getLocation(), Decl->getNameAsString())];
|
||||
SourceRange Range =
|
||||
DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
|
||||
.getSourceRange();
|
||||
|
||||
const IdentifierTable &Idents = Decl->getASTContext().Idents;
|
||||
auto CheckNewIdentifier = Idents.find(Info.Fixup);
|
||||
if (CheckNewIdentifier != Idents.end()) {
|
||||
const IdentifierInfo *Ident = CheckNewIdentifier->second;
|
||||
if (Ident->isKeyword(getLangOpts()))
|
||||
Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword;
|
||||
else if (Ident->hasMacroDefinition())
|
||||
Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition;
|
||||
}
|
||||
|
||||
Failure.Info = std::move(Info);
|
||||
addUsage(NamingCheckFailures, Decl, Range);
|
||||
}
|
||||
}
|
||||
|
||||
void RenamerClangTidyCheck::checkMacro(SourceManager &SourceMgr,
|
||||
const Token &MacroNameTok,
|
||||
const MacroInfo *MI) {
|
||||
Optional<FailureInfo> MaybeFailure =
|
||||
GetMacroFailureInfo(MacroNameTok, SourceMgr);
|
||||
if (!MaybeFailure)
|
||||
return;
|
||||
FailureInfo &Info = *MaybeFailure;
|
||||
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
|
||||
NamingCheckId ID(MI->getDefinitionLoc(), Name);
|
||||
NamingCheckFailure &Failure = NamingCheckFailures[ID];
|
||||
SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
|
||||
|
||||
Failure.Info = std::move(Info);
|
||||
addUsage(NamingCheckFailures, ID, Range);
|
||||
}
|
||||
|
||||
void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok,
|
||||
const MacroInfo *MI) {
|
||||
StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
|
||||
NamingCheckId ID(MI->getDefinitionLoc(), Name);
|
||||
|
||||
auto Failure = NamingCheckFailures.find(ID);
|
||||
if (Failure == NamingCheckFailures.end())
|
||||
return;
|
||||
|
||||
SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
|
||||
addUsage(NamingCheckFailures, ID, Range);
|
||||
}
|
||||
|
||||
static std::string
|
||||
getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus,
|
||||
const std::string &Fixup) {
|
||||
if (Fixup.empty())
|
||||
return "; cannot be fixed automatically";
|
||||
if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ShouldFix)
|
||||
return {};
|
||||
if (FixStatus >=
|
||||
RenamerClangTidyCheck::ShouldFixStatus::IgnoreFailureThreshold)
|
||||
return {};
|
||||
if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithKeyword)
|
||||
return "; cannot be fixed because '" + Fixup +
|
||||
"' would conflict with a keyword";
|
||||
if (FixStatus ==
|
||||
RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithMacroDefinition)
|
||||
return "; cannot be fixed because '" + Fixup +
|
||||
"' would conflict with a macro definition";
|
||||
|
||||
llvm_unreachable("invalid ShouldFixStatus");
|
||||
}
|
||||
|
||||
void RenamerClangTidyCheck::onEndOfTranslationUnit() {
|
||||
for (const auto &Pair : NamingCheckFailures) {
|
||||
const NamingCheckId &Decl = Pair.first;
|
||||
const NamingCheckFailure &Failure = Pair.second;
|
||||
|
||||
if (Failure.Info.KindName.empty())
|
||||
continue;
|
||||
|
||||
if (Failure.ShouldNotify()) {
|
||||
auto DiagInfo = GetDiagInfo(Decl, Failure);
|
||||
auto Diag = diag(Decl.first,
|
||||
DiagInfo.Text + getDiagnosticSuffix(Failure.FixStatus,
|
||||
Failure.Info.Fixup));
|
||||
DiagInfo.ApplyArgs(Diag);
|
||||
|
||||
if (Failure.ShouldFix()) {
|
||||
for (const auto &Loc : Failure.RawUsageLocs) {
|
||||
// We assume that the identifier name is made of one token only. This
|
||||
// is always the case as we ignore usages in macros that could build
|
||||
// identifier names by combining multiple tokens.
|
||||
//
|
||||
// For destructors, we already take care of it by remembering the
|
||||
// location of the start of the identifier and not the start of the
|
||||
// tilde.
|
||||
//
|
||||
// Other multi-token identifiers, such as operators are not checked at
|
||||
// all.
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
SourceRange(SourceLocation::getFromRawEncoding(Loc)),
|
||||
Failure.Info.Fixup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
|
@ -0,0 +1,150 @@
|
|||
//===--- RenamderClangTidyCheck.h - clang-tidy ------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class MacroInfo;
|
||||
|
||||
namespace tidy {
|
||||
|
||||
/// Base class for clang-tidy checks that want to flag declarations and/or
|
||||
/// macros for renaming based on customizable criteria.
|
||||
class RenamerClangTidyCheck : public ClangTidyCheck {
|
||||
public:
|
||||
RenamerClangTidyCheck(StringRef CheckName, ClangTidyContext *Context);
|
||||
~RenamerClangTidyCheck();
|
||||
|
||||
/// Derived classes should not implement any matching logic themselves; this
|
||||
/// class will do the matching and call the derived class'
|
||||
/// GetDeclFailureInfo() and GetMacroFailureInfo() for determining whether a
|
||||
/// given identifier passes or fails the check.
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override final;
|
||||
void
|
||||
check(const ast_matchers::MatchFinder::MatchResult &Result) override final;
|
||||
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
|
||||
Preprocessor *ModuleExpanderPP) override final;
|
||||
void onEndOfTranslationUnit() override final;
|
||||
|
||||
/// This enum will be used in %select of the diagnostic message.
|
||||
/// Each value below IgnoreFailureThreshold should have an error message.
|
||||
enum class ShouldFixStatus {
|
||||
ShouldFix,
|
||||
|
||||
/// The fixup will conflict with a language keyword,
|
||||
/// so we can't fix it automatically.
|
||||
ConflictsWithKeyword,
|
||||
|
||||
/// The fixup will conflict with a macro
|
||||
/// definition, so we can't fix it
|
||||
/// automatically.
|
||||
ConflictsWithMacroDefinition,
|
||||
|
||||
/// Values pass this threshold will be ignored completely
|
||||
/// i.e no message, no fixup.
|
||||
IgnoreFailureThreshold,
|
||||
|
||||
/// If the identifier was used or declared within a macro we
|
||||
/// won't offer a fixup for safety reasons.
|
||||
InsideMacro,
|
||||
};
|
||||
|
||||
/// Information describing a failed check
|
||||
struct FailureInfo {
|
||||
std::string KindName; // Tag or misc info to be used as derived classes need
|
||||
std::string Fixup; // The name that will be proposed as a fix-it hint
|
||||
};
|
||||
|
||||
/// Holds an identifier name check failure, tracking the kind of the
|
||||
/// identifier, its possible fixup and the starting locations of all the
|
||||
/// identifier usages.
|
||||
struct NamingCheckFailure {
|
||||
FailureInfo Info;
|
||||
|
||||
/// Whether the failure should be fixed or not.
|
||||
///
|
||||
/// e.g.: if the identifier was used or declared within a macro we won't
|
||||
/// offer a fixup for safety reasons.
|
||||
bool ShouldFix() const {
|
||||
return FixStatus == ShouldFixStatus::ShouldFix && !Info.Fixup.empty();
|
||||
}
|
||||
|
||||
bool ShouldNotify() const {
|
||||
return FixStatus < ShouldFixStatus::IgnoreFailureThreshold;
|
||||
}
|
||||
|
||||
ShouldFixStatus FixStatus = ShouldFixStatus::ShouldFix;
|
||||
|
||||
/// A set of all the identifier usages starting SourceLocation, in
|
||||
/// their encoded form.
|
||||
llvm::DenseSet<unsigned> RawUsageLocs;
|
||||
|
||||
NamingCheckFailure() = default;
|
||||
};
|
||||
|
||||
using NamingCheckId = std::pair<SourceLocation, std::string>;
|
||||
|
||||
using NamingCheckFailureMap =
|
||||
llvm::DenseMap<NamingCheckId, NamingCheckFailure>;
|
||||
|
||||
/// Check Macros for style violations.
|
||||
void checkMacro(SourceManager &sourceMgr, const Token &MacroNameTok,
|
||||
const MacroInfo *MI);
|
||||
|
||||
/// Add a usage of a macro if it already has a violation.
|
||||
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
|
||||
|
||||
protected:
|
||||
/// Overridden by derived classes, returns information about if and how a Decl
|
||||
/// failed the check. A 'None' result means the Decl did not fail the check.
|
||||
virtual llvm::Optional<FailureInfo>
|
||||
GetDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const = 0;
|
||||
|
||||
/// Overridden by derived classes, returns information about if and how a
|
||||
/// macro failed the check. A 'None' result means the macro did not fail the
|
||||
/// check.
|
||||
virtual llvm::Optional<FailureInfo>
|
||||
GetMacroFailureInfo(const Token &MacroNameTok,
|
||||
const SourceManager &SM) const = 0;
|
||||
|
||||
/// Represents customized diagnostic text and how arguments should be applied.
|
||||
/// Example usage:
|
||||
///
|
||||
/// return DiagInfo{"my %1 very %2 special %3 text",
|
||||
/// [=](DiagnosticBuilder &diag) {
|
||||
/// diag << arg1 << arg2 << arg3;
|
||||
/// }};
|
||||
struct DiagInfo {
|
||||
std::string Text;
|
||||
llvm::unique_function<void(DiagnosticBuilder &)> ApplyArgs;
|
||||
};
|
||||
|
||||
/// Overridden by derived classes, returns a description of the diagnostic
|
||||
/// that should be emitted for the given failure. The base class will then
|
||||
/// further customize the diagnostic by adding info about whether the fix-it
|
||||
/// can be automatically applied or not.
|
||||
virtual DiagInfo GetDiagInfo(const NamingCheckId &ID,
|
||||
const NamingCheckFailure &Failure) const = 0;
|
||||
|
||||
private:
|
||||
NamingCheckFailureMap NamingCheckFailures;
|
||||
};
|
||||
|
||||
} // namespace tidy
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_RENAMERCLANGTIDYCHECK_H
|
Loading…
Reference in New Issue