50static const DeclRefExpr *
findUsage(
const Stmt *Node, int64_t DeclIdentifier) {
53 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
54 if (DeclRef->getDecl()->getID() == DeclIdentifier)
57 for (
const Stmt *ChildNode : Node->children())
58 if (
const DeclRefExpr *Result =
findUsage(ChildNode, DeclIdentifier))
66 const llvm::ArrayRef<int64_t> &DeclIdentifiers) {
69 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
70 if (llvm::is_contained(DeclIdentifiers, DeclRef->getDecl()->getID()))
73 for (
const Stmt *ChildNode : Node->children())
74 if (
const DeclRefExpr *Result =
82 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
85 if (InitDeclStmt->isSingleDecl()) {
86 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
87 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
88 return findUsage(If->getElse(), InitDecl->getID());
90 llvm::SmallVector<int64_t, 4> DeclIdentifiers;
91 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
92 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
93 DeclIdentifiers.push_back(ChildDecl->getID());
115 const Stmt *Else, SourceLocation ElseLoc) {
116 auto Remap = [&](SourceLocation Loc) {
117 return Context.getSourceManager().getExpansionLoc(Loc);
119 auto TokLen = [&](SourceLocation Loc) {
120 return Lexer::MeasureTokenLength(Loc, Context.getSourceManager(),
121 Context.getLangOpts());
124 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
125 Diag << tooling::fixit::createRemoval(ElseLoc);
126 const SourceLocation LBrace = CS->getLBracLoc();
127 const SourceLocation RBrace = CS->getRBracLoc();
128 const SourceLocation RangeStart =
129 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
130 const SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
132 const llvm::StringRef Repl = Lexer::getSourceText(
133 CharSourceRange::getTokenRange(RangeStart, RangeEnd),
134 Context.getSourceManager(), Context.getLangOpts());
135 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
137 const SourceLocation ElseExpandedLoc = Remap(ElseLoc);
138 const SourceLocation EndLoc = Remap(Else->getEndLoc());
140 const llvm::StringRef Repl = Lexer::getSourceText(
141 CharSourceRange::getTokenRange(
142 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
143 Context.getSourceManager(), Context.getLangOpts());
144 Diag << tooling::fixit::createReplacement(
145 SourceRange(ElseExpandedLoc, EndLoc), Repl);
186 const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
187 const SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
188 const SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
189 if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
193 if (ExpandedStartLoc == ExpandedEndLoc)
196 assert(ExpandedStartLoc < ExpandedEndLoc);
198 auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
200 if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
203 const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
205 assert(llvm::is_sorted(ConditionalBranches,
206 [](
const SourceRange &LHS,
const SourceRange &RHS) {
207 return LHS.getEnd() < RHS.getEnd();
212 llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
213 [](
const SourceRange &LHS,
const SourceLocation &RHS) {
214 return LHS.getEnd() < RHS;
216 const auto *End = ConditionalBranches.end();
217 for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
218 if (Begin->getBegin() < ExpandedStartLoc)
236 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
237 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
238 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
240 const SourceLocation ElseLoc = If->getElseLoc();
243 PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
247 const bool IsLastInScope = OuterScope->body_back() == If;
251 if (WarnOnUnfixable) {
259 if (!WarnOnConditionVariables)
265 << ControlFlowInterrupter
266 << SourceRange(ElseLoc);
268 Diag << tooling::fixit::createReplacement(
269 SourceRange(If->getIfLoc()),
270 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
271 llvm::StringRef(
"\n"))
273 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
275 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
276 const VarDecl *VDecl = If->getConditionVariable();
277 const std::string Repl =
278 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
279 llvm::StringRef(
";\n") +
280 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
282 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
284 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
287 }
else if (WarnOnUnfixable) {
295 if (!WarnOnConditionVariables)
301 << ControlFlowInterrupter
302 << SourceRange(ElseLoc);
303 Diag << tooling::fixit::createReplacement(
304 SourceRange(If->getIfLoc()),
305 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
307 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
309 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
311 }
else if (WarnOnUnfixable) {
319 << ControlFlowInterrupter << SourceRange(ElseLoc);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.