100 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
103 if (InitDeclStmt->isSingleDecl()) {
104 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
105 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
106 return findUsage(If->getElse(), InitDecl);
128 ASTContext &Context,
const Stmt *Else,
129 SourceLocation ElseLoc) {
130 auto Remap = [&](SourceLocation Loc) {
131 return Context.getSourceManager().getExpansionLoc(Loc);
134 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
135 Diag << tooling::fixit::createRemoval(ElseLoc)
136 << tooling::fixit::createRemoval(Remap(CS->getLBracLoc()))
137 << tooling::fixit::createRemoval(Remap(CS->getRBracLoc()));
139 Diag << tooling::fixit::createRemoval(Remap(ElseLoc));
163 const auto InterruptsControlFlow =
164 stmt(anyOf(returnStmt(), continueStmt(), breakStmt(), cxxThrowExpr(),
165 callExpr(callee(functionDecl(isNoReturn())))));
167 const auto IfWithInterruptingThenElse =
168 ifStmt(unless(isConstexpr()), unless(isConsteval()),
170 hasElse(stmt().bind(
"else")))
173 Finder->addMatcher(compoundStmt(forEach(stripLabelLikeStatements(
174 IfWithInterruptingThenElse)))
181 const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
182 const SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
183 const SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
184 if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
188 if (ExpandedStartLoc == ExpandedEndLoc)
191 assert(ExpandedStartLoc < ExpandedEndLoc);
193 auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
195 if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
198 const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
200 assert(llvm::is_sorted(ConditionalBranches,
201 [](
const SourceRange &LHS,
const SourceRange &RHS) {
202 return LHS.getEnd() < RHS.getEnd();
207 llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
208 [](
const SourceRange &LHS,
const SourceLocation &RHS) {
209 return LHS.getEnd() < RHS;
211 const auto *End = ConditionalBranches.end();
212 for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
213 if (Begin->getBegin() < ExpandedStartLoc)
233 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
234 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
235 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
237 const SourceLocation ElseLoc = If->getElseLoc();
240 PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
244 const bool IsLastInScope = OuterScope->body_back() == If;
248 if (WarnOnUnfixable) {
256 if (!WarnOnConditionVariables)
262 << ControlFlowInterrupter
263 << SourceRange(ElseLoc);
265 Diag << tooling::fixit::createReplacement(
266 SourceRange(If->getIfLoc()),
267 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
270 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
272 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
273 const VarDecl *VDecl = If->getConditionVariable();
274 const std::string Repl =
275 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
277 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
279 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
281 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
284 }
else if (WarnOnUnfixable) {
292 if (!WarnOnConditionVariables)
298 << ControlFlowInterrupter
299 << SourceRange(ElseLoc);
300 Diag << tooling::fixit::createReplacement(
301 SourceRange(If->getIfLoc()),
302 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
304 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
306 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
308 }
else if (WarnOnUnfixable) {
316 << ControlFlowInterrupter
317 << SourceRange(ElseLoc);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.