10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 #include "clang/Tooling/FixIt.h"
14 #include "llvm/ADT/SmallVector.h"
20 namespace readability {
30 static const DeclRefExpr *
findUsage(
const Stmt *Node, int64_t DeclIdentifier) {
33 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
34 if (
DeclRef->getDecl()->getID() == DeclIdentifier)
37 for (
const Stmt *ChildNode : Node->children()) {
38 if (
const DeclRefExpr *Result =
findUsage(ChildNode, DeclIdentifier))
45 static const DeclRefExpr *
47 const llvm::ArrayRef<int64_t> &DeclIdentifiers) {
50 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
51 if (llvm::is_contained(DeclIdentifiers,
DeclRef->getDecl()->getID()))
54 for (
const Stmt *ChildNode : Node->children()) {
55 if (
const DeclRefExpr *Result =
64 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
67 if (InitDeclStmt->isSingleDecl()) {
68 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
69 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
70 return findUsage(If->getElse(), InitDecl->getID());
72 llvm::SmallVector<int64_t, 4> DeclIdentifiers;
73 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
74 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
75 DeclIdentifiers.push_back(ChildDecl->getID());
81 if (
const VarDecl *CondVar = If->getConditionVariable())
82 return findUsage(If->getElse(), CondVar->getID());
87 if (isa<DeclStmt>(Node))
89 if (
const auto *Compound = dyn_cast<CompoundStmt>(Node))
90 return llvm::any_of(Compound->body(), [](
const Stmt *SubNode) {
91 return isa<DeclStmt>(SubNode);
97 const Stmt *Else, SourceLocation ElseLoc) {
98 auto Remap = [&](SourceLocation
Loc) {
99 return Context.getSourceManager().getExpansionLoc(
Loc);
101 auto TokLen = [&](SourceLocation
Loc) {
102 return Lexer::MeasureTokenLength(
Loc, Context.getSourceManager(),
103 Context.getLangOpts());
106 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
107 Diag << tooling::fixit::createRemoval(ElseLoc);
108 SourceLocation LBrace = CS->getLBracLoc();
109 SourceLocation RBrace = CS->getRBracLoc();
110 SourceLocation RangeStart =
111 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
112 SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
114 llvm::StringRef Repl = Lexer::getSourceText(
115 CharSourceRange::getTokenRange(RangeStart, RangeEnd),
116 Context.getSourceManager(), Context.getLangOpts());
117 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
119 SourceLocation ElseExpandedLoc = Remap(ElseLoc);
120 SourceLocation EndLoc = Remap(Else->getEndLoc());
122 llvm::StringRef Repl = Lexer::getSourceText(
123 CharSourceRange::getTokenRange(
124 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
125 Context.getSourceManager(), Context.getLangOpts());
126 Diag << tooling::fixit::createReplacement(
127 SourceRange(ElseExpandedLoc, EndLoc), Repl);
131 ElseAfterReturnCheck::ElseAfterReturnCheck(StringRef
Name,
144 const auto InterruptsControlFlow =
147 expr(ignoringImplicit(cxxThrowExpr().bind(
ThrowStr)))));
150 forEach(ifStmt(unless(isConstexpr()),
152 anyOf(InterruptsControlFlow,
153 compoundStmt(has(InterruptsControlFlow))))),
154 hasElse(stmt().bind(
"else")))
161 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
162 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
163 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
165 bool IsLastInScope = OuterScope->body_back() == If;
166 SourceLocation ElseLoc = If->getElseLoc();
168 auto ControlFlowInterruptor = [&]() -> llvm::StringRef {
169 for (llvm::StringRef BindingName :
171 if (Result.Nodes.getNodeAs<Stmt>(BindingName))
177 if (WarnOnUnfixable) {
185 if (!WarnOnConditionVariables)
191 << ControlFlowInterruptor;
193 Diag << tooling::fixit::createReplacement(
194 SourceRange(If->getIfLoc()),
195 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
196 llvm::StringRef(
"\n"))
198 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
200 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
201 const VarDecl *VDecl = If->getConditionVariable();
203 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
204 llvm::StringRef(
";\n") +
205 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
207 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
209 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
212 }
else if (WarnOnUnfixable) {
220 if (!WarnOnConditionVariables)
226 << ControlFlowInterruptor;
227 Diag << tooling::fixit::createReplacement(
228 SourceRange(If->getIfLoc()),
229 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
231 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
233 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
235 }
else if (WarnOnUnfixable) {
243 << ControlFlowInterruptor;