10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/DeclBase.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/Expr.h"
15#include "clang/AST/ExprCXX.h"
16#include "clang/AST/Stmt.h"
17#include "clang/AST/Type.h"
18#include "clang/ASTMatchers/ASTMatchFinder.h"
19#include "clang/ASTMatchers/ASTMatchers.h"
20#include "clang/Basic/Diagnostic.h"
21#include "clang/Basic/SourceLocation.h"
22#include "clang/Lex/Lexer.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/Support/Casting.h"
29using ast_matchers::BoundNodes;
30using ast_matchers::callee;
31using ast_matchers::callExpr;
32using ast_matchers::classTemplateDecl;
33using ast_matchers::cxxMemberCallExpr;
34using ast_matchers::cxxMethodDecl;
35using ast_matchers::expr;
36using ast_matchers::functionDecl;
37using ast_matchers::hasAncestor;
38using ast_matchers::hasName;
39using ast_matchers::hasParent;
40using ast_matchers::ignoringImplicit;
41using ast_matchers::ignoringParenImpCasts;
42using ast_matchers::MatchFinder;
43using ast_matchers::optionally;
44using ast_matchers::returns;
45using ast_matchers::stmt;
46using ast_matchers::stmtExpr;
47using ast_matchers::unless;
48using ast_matchers::voidType;
50const Expr *
getCondition(
const BoundNodes &Nodes,
const StringRef NodeId) {
51 const auto *If = Nodes.getNodeAs<IfStmt>(NodeId);
55 const auto *For = Nodes.getNodeAs<ForStmt>(NodeId);
57 return For->getCond();
59 const auto *While = Nodes.getNodeAs<WhileStmt>(NodeId);
61 return While->getCond();
63 const auto *Do = Nodes.getNodeAs<DoStmt>(NodeId);
67 const auto *Switch = Nodes.getNodeAs<SwitchStmt>(NodeId);
68 if (Switch !=
nullptr)
69 return Switch->getCond();
77 const auto NonMemberMatcher = expr(ignoringImplicit(ignoringParenImpCasts(
79 hasParent(stmt(optionally(hasParent(stmtExpr().bind(
"stexpr"))))
81 unless(hasAncestor(classTemplateDecl())),
82 callee(functionDecl(hasName(
"empty"), unless(returns(voidType())))))
84 const auto MemberMatcher =
85 expr(ignoringImplicit(ignoringParenImpCasts(cxxMemberCallExpr(
86 hasParent(stmt(optionally(hasParent(stmtExpr().bind(
"stexpr"))))
88 callee(cxxMethodDecl(hasName(
"empty"),
89 unless(returns(voidType()))))))))
92 Finder->addMatcher(MemberMatcher,
this);
93 Finder->addMatcher(NonMemberMatcher,
this);
98 if (Result.Nodes.getNodeAs<Expr>(
"parent"))
101 const auto PParentStmtExpr = Result.Nodes.getNodeAs<Expr>(
"stexpr");
102 const auto ParentCompStmt = Result.Nodes.getNodeAs<CompoundStmt>(
"parent");
103 const auto *ParentCond =
getCondition(Result.Nodes,
"parent");
104 const auto *ParentReturnStmt = Result.Nodes.getNodeAs<ReturnStmt>(
"parent");
106 if (
const auto *MemberCall =
107 Result.Nodes.getNodeAs<CXXMemberCallExpr>(
"empty")) {
109 if (ParentCond == MemberCall->getExprStmt())
113 if (PParentStmtExpr && ParentCompStmt &&
114 ParentCompStmt->body_back() == MemberCall->getExprStmt())
117 if (ParentReturnStmt)
120 SourceLocation MemberLoc = MemberCall->getBeginLoc();
121 SourceLocation ReplacementLoc = MemberCall->getExprLoc();
122 SourceRange ReplacementRange = SourceRange(ReplacementLoc, ReplacementLoc);
124 ASTContext &Context = MemberCall->getRecordDecl()->getASTContext();
125 DeclarationName
Name =
126 Context.DeclarationNames.getIdentifier(&Context.Idents.get(
"clear"));
128 auto Candidates = MemberCall->getRecordDecl()->lookupDependentName(
129 Name, [](
const NamedDecl *ND) {
130 return isa<CXXMethodDecl>(ND) &&
131 llvm::cast<CXXMethodDecl>(ND)->getMinRequiredArguments() ==
133 !llvm::cast<CXXMethodDecl>(ND)->isConst();
136 bool HasClear = !Candidates.empty();
138 const auto *Clear = llvm::cast<CXXMethodDecl>(Candidates.at(0));
139 QualType RangeType = MemberCall->getImplicitObjectArgument()->getType();
140 bool QualifierIncompatible =
141 (!Clear->isVolatile() && RangeType.isVolatileQualified()) ||
142 RangeType.isConstQualified();
143 if (!QualifierIncompatible) {
145 "ignoring the result of 'empty()'; did you mean 'clear()'? ")
146 << FixItHint::CreateReplacement(ReplacementRange,
"clear");
151 diag(MemberLoc,
"ignoring the result of 'empty()'");
153 }
else if (
const auto *NonMemberCall =
154 Result.Nodes.getNodeAs<CallExpr>(
"empty")) {
155 if (ParentCond == NonMemberCall->getExprStmt())
157 if (PParentStmtExpr && ParentCompStmt &&
158 ParentCompStmt->body_back() == NonMemberCall->getExprStmt())
160 if (ParentReturnStmt)
162 if (NonMemberCall->getNumArgs() != 1)
165 SourceLocation NonMemberLoc = NonMemberCall->getExprLoc();
166 SourceLocation NonMemberEndLoc = NonMemberCall->getEndLoc();
168 const Expr *Arg = NonMemberCall->getArg(0);
169 CXXRecordDecl *ArgRecordDecl = Arg->getType()->getAsCXXRecordDecl();
170 if (ArgRecordDecl ==
nullptr)
173 ASTContext &Context = ArgRecordDecl->getASTContext();
174 DeclarationName
Name =
175 Context.DeclarationNames.getIdentifier(&Context.Idents.get(
"clear"));
178 ArgRecordDecl->lookupDependentName(
Name, [](
const NamedDecl *ND) {
179 return isa<CXXMethodDecl>(ND) &&
180 llvm::cast<CXXMethodDecl>(ND)->getMinRequiredArguments() ==
182 !llvm::cast<CXXMethodDecl>(ND)->isConst();
185 bool HasClear = !Candidates.empty();
188 const auto *Clear = llvm::cast<CXXMethodDecl>(Candidates.at(0));
189 bool QualifierIncompatible =
190 (!Clear->isVolatile() && Arg->getType().isVolatileQualified()) ||
191 Arg->getType().isConstQualified();
192 if (!QualifierIncompatible) {
193 std::string ReplacementText =
194 std::string(Lexer::getSourceText(
195 CharSourceRange::getTokenRange(Arg->getSourceRange()),
198 SourceRange ReplacementRange =
199 SourceRange(NonMemberLoc, NonMemberEndLoc);
201 "ignoring the result of '%0'; did you mean 'clear()'?")
202 << llvm::dyn_cast<NamedDecl>(NonMemberCall->getCalleeDecl())
203 ->getQualifiedNameAsString()
204 << FixItHint::CreateReplacement(ReplacementRange, ReplacementText);
209 diag(NonMemberLoc,
"ignoring the result of '%0'")
210 << llvm::dyn_cast<NamedDecl>(NonMemberCall->getCalleeDecl())
211 ->getQualifiedNameAsString();
llvm::SmallString< 256U > Name
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.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
const Expr * getCondition(const BoundNodes &Nodes, const StringRef NodeId)