11#include "clang/AST/CXXInheritance.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Basic/CharInfo.h"
14#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Lex/PPCallbacks.h"
16#include "clang/Lex/Preprocessor.h"
17#include "llvm/ADT/DenseMapInfo.h"
18#include "llvm/ADT/PointerIntPair.h"
21#define DEBUG_TYPE "clang-tidy"
33 return NamingCheckId(DenseMapInfo<clang::SourceLocation>::getEmptyKey(),
38 return NamingCheckId(DenseMapInfo<clang::SourceLocation>::getTombstoneKey(),
43 assert(Val != getEmptyKey() &&
"Cannot hash the empty key!");
44 assert(Val != getTombstoneKey() &&
"Cannot hash the tombstone key!");
46 std::hash<NamingCheckId::second_type> SecondHash;
47 return DenseMapInfo<clang::SourceLocation>::getHashValue(Val.first) +
48 SecondHash(Val.second);
52 if (RHS == getEmptyKey())
53 return LHS == getEmptyKey();
54 if (RHS == getTombstoneKey())
55 return LHS == getTombstoneKey();
67class RenamerClangTidyCheckPPCallbacks :
public PPCallbacks {
69 RenamerClangTidyCheckPPCallbacks(Preprocessor *PP,
70 RenamerClangTidyCheck *Check)
71 : PP(PP), Check(Check) {}
74 void MacroDefined(
const Token &MacroNameTok,
75 const MacroDirective *MD)
override {
76 if (MD->getMacroInfo()->isBuiltinMacro())
78 if (PP->getSourceManager().isWrittenInBuiltinFile(
79 MacroNameTok.getLocation()))
81 if (PP->getSourceManager().isWrittenInCommandLineFile(
82 MacroNameTok.getLocation()))
84 Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo());
88 void MacroExpands(
const Token &MacroNameTok,
const MacroDefinition &MD,
90 const MacroArgs * )
override {
91 Check->expandMacro(MacroNameTok, MD.getMacroInfo());
96 RenamerClangTidyCheck *Check;
104 AggressiveDependentMemberLookup(
105 Options.getLocalOrGlobal(
"AggressiveDependentMemberLookup", false)) {}
110 AggressiveDependentMemberLookup);
114 Finder->addMatcher(namedDecl().bind(
"decl"),
this);
115 Finder->addMatcher(usingDecl().bind(
"using"),
this);
116 Finder->addMatcher(declRefExpr().bind(
"declRef"),
this);
117 Finder->addMatcher(cxxConstructorDecl(unless(isImplicit())).bind(
"classRef"),
119 Finder->addMatcher(cxxDestructorDecl(unless(isImplicit())).bind(
"classRef"),
121 Finder->addMatcher(typeLoc().bind(
"typeLoc"),
this);
122 Finder->addMatcher(nestedNameSpecifierLoc().bind(
"nestedNameLoc"),
this);
123 auto MemberRestrictions =
124 unless(forFunction(anyOf(isDefaulted(), isImplicit())));
125 Finder->addMatcher(memberExpr(MemberRestrictions).bind(
"memberExpr"),
this);
127 cxxDependentScopeMemberExpr(MemberRestrictions).bind(
"depMemberExpr"),
132 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
133 ModuleExpanderPP->addPPCallbacks(
134 std::make_unique<RenamerClangTidyCheckPPCallbacks>(ModuleExpanderPP,
142 if (Method->size_overridden_methods() != 1)
145 Method = *Method->begin_overridden_methods();
146 assert(Method &&
"Overridden method shouldn't be null");
147 unsigned NumOverrides = Method->size_overridden_methods();
148 if (NumOverrides == 0)
150 if (NumOverrides > 1)
157 SourceManager *SourceMgr) {
159 if (
Range.isInvalid())
166 SourceLocation FixLocation =
Range.getBegin();
168 FixLocation = SourceMgr->getSpellingLoc(FixLocation);
169 if (FixLocation.isInvalid())
175 NamingCheckFailures[
Decl];
182 if (SourceMgr && SourceMgr->isWrittenInScratchSpace(FixLocation))
190 SourceManager *SourceMgr) {
191 if (
const auto *Method = dyn_cast<CXXMethodDecl>(
Decl)) {
195 Decl = cast<NamedDecl>(
Decl->getCanonicalDecl());
197 Decl->getNameAsString()),
201const NamedDecl *
findDecl(
const RecordDecl &RecDecl, StringRef DeclName) {
202 for (
const Decl *D : RecDecl.decls()) {
203 if (
const auto *ND = dyn_cast<NamedDecl>(D)) {
204 if (ND->getDeclName().isIdentifier() && ND->getName().equals(DeclName))
213 llvm::PointerIntPair<const NamedDecl *, 1, bool> Data;
216 explicit NameLookup(
const NamedDecl *ND) : Data(ND, false) {}
217 explicit NameLookup(std::nullopt_t) : Data(nullptr, true) {}
218 explicit NameLookup(std::nullptr_t) : Data(nullptr, false) {}
219 NameLookup() : NameLookup(nullptr) {}
221 bool hasMultipleResolutions()
const {
return Data.getInt(); }
222 const NamedDecl *getDecl()
const {
223 assert(!hasMultipleResolutions() &&
"Found multiple decls");
224 return Data.getPointer();
226 operator bool()
const {
return !hasMultipleResolutions(); }
227 const NamedDecl *operator*()
const {
return getDecl(); }
237 bool AggressiveTemplateLookup) {
238 if (!
Parent.hasDefinition())
239 return NameLookup(
nullptr);
241 return NameLookup(InClassRef);
242 const NamedDecl *Found =
nullptr;
244 for (CXXBaseSpecifier Base :
Parent.bases()) {
245 const auto *Record = Base.getType()->getAsCXXRecordDecl();
246 if (!Record && AggressiveTemplateLookup) {
247 if (
const auto *TST =
248 Base.getType()->getAs<TemplateSpecializationType>()) {
249 if (
const auto *TD = llvm::dyn_cast_or_null<ClassTemplateDecl>(
250 TST->getTemplateName().getAsTemplateDecl()))
251 Record = TD->getTemplatedDecl();
266 return NameLookup(std::nullopt);
268 return NameLookup(Found);
272 if (
const auto *
Decl =
273 Result.Nodes.getNodeAs<CXXConstructorDecl>(
"classRef")) {
276 Result.SourceManager);
278 for (
const auto *Init :
Decl->inits()) {
279 if (!Init->isWritten() || Init->isInClassMemberInitializer())
281 if (
const FieldDecl *FD = Init->getAnyMember())
282 addUsage(FD, SourceRange(Init->getMemberLocation()),
283 Result.SourceManager);
290 if (
const auto *
Decl =
291 Result.Nodes.getNodeAs<CXXDestructorDecl>(
"classRef")) {
293 SourceRange
Range =
Decl->getNameInfo().getSourceRange();
294 if (
Range.getBegin().isInvalid())
298 Range.setBegin(CharSourceRange::getTokenRange(
Range).getEnd());
304 if (
const auto *
Loc = Result.Nodes.getNodeAs<TypeLoc>(
"typeLoc")) {
305 UnqualTypeLoc Unqual =
Loc->getUnqualifiedLoc();
306 NamedDecl *
Decl =
nullptr;
307 if (
const auto &Ref = Unqual.getAs<TagTypeLoc>())
308 Decl = Ref.getDecl();
309 else if (
const auto &Ref = Unqual.getAs<InjectedClassNameTypeLoc>())
310 Decl = Ref.getDecl();
311 else if (
const auto &Ref = Unqual.getAs<UnresolvedUsingTypeLoc>())
312 Decl = Ref.getDecl();
313 else if (
const auto &Ref = Unqual.getAs<TemplateTypeParmTypeLoc>())
314 Decl = Ref.getDecl();
322 if (
const auto &Ref =
Loc->getAs<TemplateSpecializationTypeLoc>()) {
323 const TemplateDecl *
Decl =
324 Ref.getTypePtr()->getTemplateName().getAsTemplateDecl();
326 SourceRange
Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc());
327 if (
const auto *ClassDecl = dyn_cast<TemplateDecl>(
Decl)) {
328 if (
const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl())
334 if (
const auto &Ref =
335 Loc->getAs<DependentTemplateSpecializationTypeLoc>()) {
336 if (
const TagDecl *
Decl = Ref.getTypePtr()->getAsTagDecl())
342 if (
const auto *
Loc =
343 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nestedNameLoc")) {
344 if (
const NestedNameSpecifier *Spec =
Loc->getNestedNameSpecifier()) {
345 if (
const NamespaceDecl *
Decl = Spec->getAsNamespace()) {
352 if (
const auto *
Decl = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
353 for (
const auto *Shadow :
Decl->shadows())
354 addUsage(Shadow->getTargetDecl(),
Decl->getNameInfo().getSourceRange(),
355 Result.SourceManager);
359 if (
const auto *
DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>(
"declRef")) {
360 SourceRange
Range =
DeclRef->getNameInfo().getSourceRange();
365 if (
const auto *MemberRef =
366 Result.Nodes.getNodeAs<MemberExpr>(
"memberExpr")) {
367 SourceRange
Range = MemberRef->getMemberNameInfo().getSourceRange();
368 addUsage(MemberRef->getMemberDecl(),
Range, Result.SourceManager);
372 if (
const auto *DepMemberRef =
373 Result.Nodes.getNodeAs<CXXDependentScopeMemberExpr>(
375 QualType BaseType = DepMemberRef->isArrow()
376 ? DepMemberRef->getBaseType()->getPointeeType()
377 : DepMemberRef->getBaseType();
378 if (BaseType.isNull())
380 const CXXRecordDecl *Base = BaseType.getTypePtr()->getAsCXXRecordDecl();
383 DeclarationName DeclName = DepMemberRef->getMemberNameInfo().getName();
384 if (!DeclName.isIdentifier())
386 StringRef DependentName = DeclName.getAsIdentifierInfo()->getName();
389 *Base, DependentName, AggressiveDependentMemberLookup)) {
391 addUsage(*Resolved, DepMemberRef->getMemberNameInfo().getSourceRange(),
392 Result.SourceManager);
397 if (
const auto *
Decl = Result.Nodes.getNodeAs<NamedDecl>(
"decl")) {
399 if (
const auto *UsingNS = dyn_cast<UsingDirectiveDecl>(
Decl))
400 addUsage(UsingNS->getNominatedNamespaceAsWritten(),
401 UsingNS->getIdentLocation(), Result.SourceManager);
403 if (!
Decl->getIdentifier() ||
Decl->getName().empty() ||
Decl->isImplicit())
406 const auto *Canonical = cast<NamedDecl>(
Decl->getCanonicalDecl());
407 if (Canonical !=
Decl) {
408 addUsage(Canonical,
Decl->getLocation(), Result.SourceManager);
413 if (
const auto *Value = Result.Nodes.getNodeAs<ValueDecl>(
"decl")) {
414 if (
const Type *TypePtr = Value->getType().getTypePtrOrNull()) {
415 if (
const auto *Typedef = TypePtr->getAs<TypedefType>())
416 addUsage(Typedef->getDecl(), Value->getSourceRange(),
417 Result.SourceManager);
422 if (
const auto *Value = Result.Nodes.getNodeAs<FunctionDecl>(
"decl")) {
423 if (
const auto *Typedef =
424 Value->getReturnType().getTypePtr()->getAs<TypedefType>())
425 addUsage(Typedef->getDecl(), Value->getSourceRange(),
426 Result.SourceManager);
427 for (
const ParmVarDecl *Param : Value->parameters()) {
428 if (
const TypedefType *Typedef =
429 Param->getType().getTypePtr()->getAs<TypedefType>())
430 addUsage(Typedef->getDecl(), Value->getSourceRange(),
431 Result.SourceManager);
436 if (
const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>(
"decl")) {
438 addUsage(Overridden, Method->getLocation());
445 if (isa<ClassTemplateSpecializationDecl>(
Decl))
448 std::optional<FailureInfo> MaybeFailure =
454 Decl->getLocation(),
Decl->getNameAsString())];
456 DeclarationNameInfo(
Decl->getDeclName(),
Decl->getLocation())
459 const IdentifierTable &Idents =
Decl->getASTContext().Idents;
460 auto CheckNewIdentifier = Idents.find(
Info.Fixup);
461 if (CheckNewIdentifier != Idents.end()) {
462 const IdentifierInfo *Ident = CheckNewIdentifier->second;
465 else if (Ident->hasMacroDefinition())
467 }
else if (!isValidAsciiIdentifier(
Info.Fixup)) {
477 const Token &MacroNameTok,
478 const MacroInfo *MI) {
479 std::optional<FailureInfo> MaybeFailure =
484 StringRef
Name = MacroNameTok.getIdentifierInfo()->getName();
487 SourceRange
Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
489 if (!isValidAsciiIdentifier(
Info.Fixup))
497 const MacroInfo *MI) {
498 StringRef
Name = MacroNameTok.getIdentifierInfo()->getName();
501 auto Failure = NamingCheckFailures.find(ID);
502 if (Failure == NamingCheckFailures.end())
505 SourceRange
Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
511 const std::string &Fixup) {
514 return "; cannot be fixed automatically";
521 return "; cannot be fixed because '" + Fixup +
522 "' would conflict with a keyword";
525 return "; cannot be fixed because '" + Fixup +
526 "' would conflict with a macro definition";
527 llvm_unreachable(
"invalid ShouldFixStatus");
531 for (
const auto &Pair : NamingCheckFailures) {
557 Diag << FixItHint::CreateReplacement(SourceRange(
Loc),
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
const DeclRefExpr * DeclRef
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) final
Override this to register PPCallbacks in the preprocessor.
void checkMacro(SourceManager &SourceMgr, const Token &MacroNameTok, const MacroInfo *MI)
Check Macros for style violations.
void onEndOfTranslationUnit() final
virtual DiagInfo getDiagInfo(const NamingCheckId &ID, const NamingCheckFailure &Failure) const =0
Overridden by derived classes, returns a description of the diagnostic that should be emitted for the...
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Derived classes should not implement any matching logic themselves; this class will do the matching a...
std::pair< SourceLocation, std::string > NamingCheckId
RenamerClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI)
Add a usage of a macro if it already has a violation.
ShouldFixStatus
This enum will be used in select of the diagnostic message.
@ IgnoreFailureThreshold
Values pass this threshold will be ignored completely i.e no message, no fixup.
@ ConflictsWithMacroDefinition
The fixup will conflict with a macro definition, so we can't fix it automatically.
@ ConflictsWithKeyword
The fixup will conflict with a language keyword, so we can't fix it automatically.
@ InsideMacro
If the identifier was used or declared within a macro we won't offer a fixup for safety reasons.
@ FixInvalidIdentifier
The fixup results in an identifier that is not a valid c/c++ identifier.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Derived classes that override this function should call this method from the overridden method.
virtual std::optional< FailureInfo > getMacroFailureInfo(const Token &MacroNameTok, const SourceManager &SM) const =0
Overridden by derived classes, returns information about if and how a macro failed the check.
virtual std::optional< FailureInfo > getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const =0
Overridden by derived classes, returns information about if and how a Decl failed the check.
void addUsage(const RenamerClangTidyCheck::NamingCheckId &Decl, SourceRange Range, SourceManager *SourceMgr=nullptr)
bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM)
NameLookup findDeclInBases(const CXXRecordDecl &Parent, StringRef DeclName, bool AggressiveTemplateLookup)
Returns a decl matching the DeclName in Parent or one of its base classes.
static std::string getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus, const std::string &Fixup)
static const CXXMethodDecl * getOverrideMethod(const CXXMethodDecl *Method)
Returns the function that Method is overridding.
const NamedDecl * findDecl(const RecordDecl &RecDecl, StringRef DeclName)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap
Represents customized diagnostic text and how arguments should be applied.
llvm::unique_function< void(DiagnosticBuilder &)> ApplyArgs
Information describing a failed check.
Holds an identifier name check failure, tracking the kind of the identifier, its possible fixup and t...
ShouldFixStatus FixStatus
llvm::DenseSet< SourceLocation > RawUsageLocs
A set of all the identifier usages starting SourceLocation.
bool shouldNotify() const
bool shouldFix() const
Whether the failure should be fixed or not.
static NamingCheckId getTombstoneKey()
static NamingCheckId getEmptyKey()
static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS)
static unsigned getHashValue(NamingCheckId Val)
clang::tidy::RenamerClangTidyCheck::NamingCheckId NamingCheckId