49static const DeclRefExpr *
findUsage(
const Stmt *Node, int64_t DeclIdentifier) {
52 if (
const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
53 if (DeclRef->getDecl()->getID() == DeclIdentifier)
56 for (
const Stmt *ChildNode : Node->children()) {
57 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 =
83 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
86 if (InitDeclStmt->isSingleDecl()) {
87 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
88 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
89 return findUsage(If->getElse(), InitDecl->getID());
91 llvm::SmallVector<int64_t, 4> DeclIdentifiers;
92 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
93 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
94 DeclIdentifiers.push_back(ChildDecl->getID());
116 const Stmt *Else, SourceLocation ElseLoc) {
117 auto Remap = [&](SourceLocation Loc) {
118 return Context.getSourceManager().getExpansionLoc(Loc);
120 auto TokLen = [&](SourceLocation Loc) {
121 return Lexer::MeasureTokenLength(Loc, Context.getSourceManager(),
122 Context.getLangOpts());
125 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
126 Diag << tooling::fixit::createRemoval(ElseLoc);
127 const SourceLocation LBrace = CS->getLBracLoc();
128 const SourceLocation RBrace = CS->getRBracLoc();
129 const SourceLocation RangeStart =
130 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
131 const SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
133 const llvm::StringRef Repl = Lexer::getSourceText(
134 CharSourceRange::getTokenRange(RangeStart, RangeEnd),
135 Context.getSourceManager(), Context.getLangOpts());
136 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
138 const SourceLocation ElseExpandedLoc = Remap(ElseLoc);
139 const SourceLocation EndLoc = Remap(Else->getEndLoc());
141 const llvm::StringRef Repl = Lexer::getSourceText(
142 CharSourceRange::getTokenRange(
143 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
144 Context.getSourceManager(), Context.getLangOpts());
145 Diag << tooling::fixit::createReplacement(
146 SourceRange(ElseExpandedLoc, EndLoc), Repl);
187 const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
188 const SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
189 const SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
190 if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
194 if (ExpandedStartLoc == ExpandedEndLoc)
197 assert(ExpandedStartLoc < ExpandedEndLoc);
199 auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
201 if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
204 const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
206 assert(llvm::is_sorted(ConditionalBranches,
207 [](
const SourceRange &LHS,
const SourceRange &RHS) {
208 return LHS.getEnd() < RHS.getEnd();
213 llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
214 [](
const SourceRange &LHS,
const SourceLocation &RHS) {
215 return LHS.getEnd() < RHS;
217 const auto *End = ConditionalBranches.end();
218 for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
219 if (Begin->getBegin() < ExpandedStartLoc)
237 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
238 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
239 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
241 const SourceLocation ElseLoc = If->getElseLoc();
244 PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
248 const bool IsLastInScope = OuterScope->body_back() == If;
252 if (WarnOnUnfixable) {
260 if (!WarnOnConditionVariables)
266 << ControlFlowInterrupter
267 << SourceRange(ElseLoc);
269 Diag << tooling::fixit::createReplacement(
270 SourceRange(If->getIfLoc()),
271 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
272 llvm::StringRef(
"\n"))
274 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
276 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
277 const VarDecl *VDecl = If->getConditionVariable();
278 const std::string Repl =
279 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
280 llvm::StringRef(
";\n") +
281 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
283 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
285 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
288 }
else if (WarnOnUnfixable) {
296 if (!WarnOnConditionVariables)
302 << ControlFlowInterrupter
303 << SourceRange(ElseLoc);
304 Diag << tooling::fixit::createReplacement(
305 SourceRange(If->getIfLoc()),
306 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
308 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
310 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
312 }
else if (WarnOnUnfixable) {
320 << ControlFlowInterrupter << SourceRange(ElseLoc);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.