56static const DeclRefExpr *
findUsage(
const Stmt *Node, int64_t DeclIdentifier) {
59 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
60 if (DeclRef->getDecl()->getID() == DeclIdentifier)
63 for (
const Stmt *ChildNode : Node->children())
64 if (
const DeclRefExpr *Result =
findUsage(ChildNode, DeclIdentifier))
72 const llvm::ArrayRef<int64_t> &DeclIdentifiers) {
75 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
76 if (llvm::is_contained(DeclIdentifiers, DeclRef->getDecl()->getID()))
79 for (
const Stmt *ChildNode : Node->children())
80 if (
const DeclRefExpr *Result =
88 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
91 if (InitDeclStmt->isSingleDecl()) {
92 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
93 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
94 return findUsage(If->getElse(), InitDecl->getID());
97 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
98 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
99 DeclIdentifiers.push_back(ChildDecl->getID());
121 const Stmt *Else, SourceLocation ElseLoc) {
122 auto Remap = [&](SourceLocation Loc) {
123 return Context.getSourceManager().getExpansionLoc(Loc);
125 auto TokLen = [&](SourceLocation Loc) {
126 return Lexer::MeasureTokenLength(Loc, Context.getSourceManager(),
127 Context.getLangOpts());
130 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
131 Diag << tooling::fixit::createRemoval(ElseLoc);
132 const SourceLocation LBrace = CS->getLBracLoc();
133 const SourceLocation RBrace = CS->getRBracLoc();
134 const SourceLocation RangeStart =
135 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
136 const SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
138 const StringRef Repl = Lexer::getSourceText(
139 CharSourceRange::getTokenRange(RangeStart, RangeEnd),
140 Context.getSourceManager(), Context.getLangOpts());
141 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
143 const SourceLocation ElseExpandedLoc = Remap(ElseLoc);
144 const SourceLocation EndLoc = Remap(Else->getEndLoc());
146 const StringRef Repl = Lexer::getSourceText(
147 CharSourceRange::getTokenRange(
148 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
149 Context.getSourceManager(), Context.getLangOpts());
150 Diag << tooling::fixit::createReplacement(
151 SourceRange(ElseExpandedLoc, EndLoc), Repl);
175 const auto InterruptsControlFlow = stmt(anyOf(
179 const auto IfWithInterruptingThenElse =
180 ifStmt(unless(isConstexpr()), unless(isConsteval()),
181 hasThen(stripLabelLikeStatements(
182 stmt(anyOf(InterruptsControlFlow,
183 compoundStmt(has(InterruptsControlFlow)))))),
184 hasElse(stmt().bind(
"else")))
187 Finder->addMatcher(compoundStmt(forEach(stripLabelLikeStatements(
188 IfWithInterruptingThenElse)))
195 const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
196 const SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
197 const SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
198 if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
202 if (ExpandedStartLoc == ExpandedEndLoc)
205 assert(ExpandedStartLoc < ExpandedEndLoc);
207 auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
209 if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
214 assert(llvm::is_sorted(ConditionalBranches,
215 [](
const SourceRange &LHS,
const SourceRange &RHS) {
216 return LHS.getEnd() < RHS.getEnd();
221 llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
222 [](
const SourceRange &LHS,
const SourceLocation &RHS) {
223 return LHS.getEnd() < RHS;
225 const auto *End = ConditionalBranches.end();
226 for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
227 if (Begin->getBegin() < ExpandedStartLoc)
245 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
246 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
247 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
249 const SourceLocation ElseLoc = If->getElseLoc();
252 PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
256 const bool IsLastInScope = OuterScope->body_back() == If;
260 if (WarnOnUnfixable) {
268 if (!WarnOnConditionVariables)
274 << ControlFlowInterrupter
275 << SourceRange(ElseLoc);
277 Diag << tooling::fixit::createReplacement(
278 SourceRange(If->getIfLoc()),
279 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
282 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
284 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
285 const VarDecl *VDecl = If->getConditionVariable();
286 const std::string Repl =
287 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
289 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
291 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
293 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
296 }
else if (WarnOnUnfixable) {
304 if (!WarnOnConditionVariables)
310 << ControlFlowInterrupter
311 << SourceRange(ElseLoc);
312 Diag << tooling::fixit::createReplacement(
313 SourceRange(If->getIfLoc()),
314 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
316 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
318 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
320 }
else if (WarnOnUnfixable) {
328 << ControlFlowInterrupter << SourceRange(ElseLoc);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.