10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
13#include "clang/Lex/Preprocessor.h"
14#include "clang/Tooling/FixIt.h"
15#include "llvm/ADT/SmallVector.h"
25 PPConditionalCollector(
27 const SourceManager &SM)
28 : Collections(Collections), SM(SM) {}
29 void Endif(SourceLocation
Loc, SourceLocation IfLoc)
override {
30 if (!SM.isWrittenInSameFile(
Loc, IfLoc))
32 SmallVectorImpl<SourceRange> &Collection = Collections[SM.getFileID(
Loc)];
33 assert(Collection.empty() || Collection.back().getEnd() <
Loc);
34 Collection.emplace_back(IfLoc,
Loc);
39 const SourceManager &SM;
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))
64static const DeclRefExpr *
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());
100 if (
const VarDecl *CondVar = If->getConditionVariable())
101 return findUsage(If->getElse(), CondVar->getID());
106 if (isa<DeclStmt>(
Node))
108 if (
const auto *Compound = dyn_cast<CompoundStmt>(
Node))
109 return llvm::any_of(Compound->body(), [](
const Stmt *SubNode) {
110 return isa<DeclStmt>(SubNode);
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 SourceLocation LBrace = CS->getLBracLoc();
128 SourceLocation RBrace = CS->getRBracLoc();
129 SourceLocation RangeStart =
130 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
131 SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
133 llvm::StringRef Repl = Lexer::getSourceText(
134 CharSourceRange::getTokenRange(RangeStart, RangeEnd),
135 Context.getSourceManager(), Context.getLangOpts());
136 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
138 SourceLocation ElseExpandedLoc = Remap(ElseLoc);
139 SourceLocation EndLoc = Remap(Else->getEndLoc());
141 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);
164 Preprocessor *ModuleExpanderPP) {
166 std::make_unique<PPConditionalCollector>(this->PPConditionals, SM));
170 const auto InterruptsControlFlow = stmt(anyOf(
175 forEach(ifStmt(unless(isConstexpr()), unless(isConsteval()),
177 anyOf(InterruptsControlFlow,
178 compoundStmt(has(InterruptsControlFlow))))),
179 hasElse(stmt().bind(
"else")))
187 const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
189 SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
190 SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
191 if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
195 if (ExpandedStartLoc == ExpandedEndLoc)
198 assert(ExpandedStartLoc < ExpandedEndLoc);
200 auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
202 if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
205 const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
207 assert(llvm::is_sorted(ConditionalBranches,
208 [](
const SourceRange &LHS,
const SourceRange &RHS) {
209 return LHS.getEnd() < RHS.getEnd();
214 llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
215 [](
const SourceRange &LHS,
const SourceLocation &RHS) {
216 return LHS.getEnd() < RHS;
218 const auto *End = ConditionalBranches.end();
219 for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
220 if (Begin->getBegin() < ExpandedStartLoc)
226 if (isa<ReturnStmt>(Stmt))
228 if (isa<ContinueStmt>(Stmt))
230 if (isa<BreakStmt>(Stmt))
232 if (isa<CXXThrowExpr>(Stmt))
234 llvm_unreachable(
"Unknown control flow interrupter");
238 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
239 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
240 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
242 SourceLocation ElseLoc = If->getElseLoc();
245 PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
249 bool IsLastInScope = OuterScope->body_back() == If;
253 if (WarnOnUnfixable) {
261 if (!WarnOnConditionVariables)
267 << ControlFlowInterrupter
268 << SourceRange(ElseLoc);
270 Diag << tooling::fixit::createReplacement(
271 SourceRange(If->getIfLoc()),
272 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
273 llvm::StringRef(
"\n"))
275 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
277 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
278 const VarDecl *VDecl = If->getConditionVariable();
280 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
281 llvm::StringRef(
";\n") +
282 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
284 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
286 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
289 }
else if (WarnOnUnfixable) {
297 if (!WarnOnConditionVariables)
303 << ControlFlowInterrupter
304 << SourceRange(ElseLoc);
305 Diag << tooling::fixit::createReplacement(
306 SourceRange(If->getIfLoc()),
307 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
309 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
311 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
313 }
else if (WarnOnUnfixable) {
321 << ControlFlowInterrupter << SourceRange(ElseLoc);
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
const DeclRefExpr * DeclRef
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::DenseMap< FileID, SmallVector< SourceRange, 1 > > ConditionalBranchMap
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
ElseAfterReturnCheck(StringRef Name, ClangTidyContext *Context)
static bool containsDeclInScope(const Stmt *Node)
static const char WarningMessage[]
static const DeclRefExpr * findUsage(const Stmt *Node, int64_t DeclIdentifier)
static bool hasPreprocessorBranchEndBetweenLocations(const ElseAfterReturnCheck::ConditionalBranchMap &ConditionalBranchMap, const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc)
static const char InterruptingStr[]
static StringRef getControlFlowString(const Stmt &Stmt)
static const DeclRefExpr * checkConditionVarUsageInElse(const IfStmt *If)
static const char WarnOnConditionVariablesStr[]
static void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context, const Stmt *Else, SourceLocation ElseLoc)
static const DeclRefExpr * checkInitDeclUsageInElse(const IfStmt *If)
static const char WarnOnUnfixableStr[]
static const DeclRefExpr * findUsageRange(const Stmt *Node, const llvm::ArrayRef< int64_t > &DeclIdentifiers)
llvm::StringMap< ClangTidyValue > OptionMap