10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Tooling/FixIt.h" 13 #include "llvm/ADT/SmallVector.h" 19 namespace readability {
22 static const char ReturnStr[] =
"return";
23 static const char ContinueStr[] =
"continue";
24 static const char BreakStr[] =
"break";
25 static const char ThrowStr[] =
"throw";
26 static const char WarningMessage[] =
"do not use 'else' after '%0'";
27 static const char WarnOnUnfixableStr[] =
"WarnOnUnfixable";
29 const DeclRefExpr *findUsage(
const Stmt *Node, int64_t DeclIdentifier) {
30 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
31 if (
DeclRef->getDecl()->getID() == DeclIdentifier) {
35 for (
const Stmt *ChildNode : Node->children()) {
36 if (
const DeclRefExpr *Result = findUsage(ChildNode, DeclIdentifier)) {
45 findUsageRange(
const Stmt *Node,
46 const llvm::iterator_range<int64_t *> &DeclIdentifiers) {
47 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
48 if (llvm::is_contained(DeclIdentifiers,
DeclRef->getDecl()->getID())) {
52 for (
const Stmt *ChildNode : Node->children()) {
53 if (
const DeclRefExpr *Result =
54 findUsageRange(ChildNode, DeclIdentifiers)) {
62 const DeclRefExpr *checkInitDeclUsageInElse(
const IfStmt *If) {
63 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
66 if (InitDeclStmt->isSingleDecl()) {
67 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
68 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
69 return findUsage(If->getElse(), InitDecl->getID());
71 llvm::SmallVector<int64_t, 4> DeclIdentifiers;
72 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
73 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
74 DeclIdentifiers.push_back(ChildDecl->getID());
76 return findUsageRange(If->getElse(), DeclIdentifiers);
79 const DeclRefExpr *checkConditionVarUsageInElse(
const IfStmt *If) {
80 const VarDecl *CondVar = If->getConditionVariable();
81 return CondVar !=
nullptr ? findUsage(If->getElse(), CondVar->getID())
85 bool containsDeclInScope(
const Stmt *Node) {
86 if (isa<DeclStmt>(Node)) {
89 if (
const auto *Compound = dyn_cast<CompoundStmt>(Node)) {
90 return llvm::any_of(Compound->body(), [](
const Stmt *SubNode) {
91 return isa<DeclStmt>(SubNode);
97 void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context,
98 const Stmt *Else, SourceLocation ElseLoc) {
99 auto Remap = [&](SourceLocation
Loc) {
100 return Context.getSourceManager().getExpansionLoc(
Loc);
102 auto TokLen = [&](SourceLocation
Loc) {
103 return Lexer::MeasureTokenLength(
Loc, Context.getSourceManager(),
104 Context.getLangOpts());
107 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
108 Diag << tooling::fixit::createRemoval(ElseLoc);
109 SourceLocation LBrace = CS->getLBracLoc();
110 SourceLocation RBrace = CS->getRBracLoc();
111 SourceLocation RangeStart =
112 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
113 SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
115 llvm::StringRef Repl = Lexer::getSourceText(
117 Context.getSourceManager(), Context.getLangOpts());
118 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
120 SourceLocation ElseExpandedLoc = Remap(ElseLoc);
121 SourceLocation EndLoc = Remap(Else->getEndLoc());
123 llvm::StringRef Repl = Lexer::getSourceText(
125 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
126 Context.getSourceManager(), Context.getLangOpts());
127 Diag << tooling::fixit::createReplacement(
128 SourceRange(ElseExpandedLoc, EndLoc), Repl);
133 ElseAfterReturnCheck::ElseAfterReturnCheck(StringRef
Name,
136 WarnOnUnfixable(Options.get(WarnOnUnfixableStr, true)) {}
139 Options.
store(Opts, WarnOnUnfixableStr, WarnOnUnfixable);
143 const auto InterruptsControlFlow =
144 stmt(anyOf(returnStmt().bind(ReturnStr), continueStmt().bind(ContinueStr),
145 breakStmt().bind(BreakStr),
146 expr(ignoringImplicit(cxxThrowExpr().bind(ThrowStr)))));
149 forEach(ifStmt(unless(isConstexpr()),
151 anyOf(InterruptsControlFlow,
152 compoundStmt(has(InterruptsControlFlow))))),
153 hasElse(stmt().bind(
"else")))
160 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
161 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
162 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
164 bool IsLastInScope = OuterScope->body_back() == If;
165 SourceLocation ElseLoc = If->getElseLoc();
167 auto ControlFlowInterruptor = [&]() -> llvm::StringRef {
168 for (llvm::StringRef BindingName :
169 {ReturnStr, ContinueStr, BreakStr, ThrowStr})
170 if (Result.Nodes.getNodeAs<Stmt>(BindingName))
175 if (!IsLastInScope && containsDeclInScope(Else)) {
176 if (WarnOnUnfixable) {
178 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
183 if (checkConditionVarUsageInElse(If) !=
nullptr) {
187 DiagnosticBuilder Diag =
188 diag(ElseLoc, WarningMessage, clang::DiagnosticIDs::Level::Remark)
189 << ControlFlowInterruptor;
190 if (checkInitDeclUsageInElse(If) !=
nullptr) {
191 Diag << tooling::fixit::createReplacement(
192 SourceRange(If->getIfLoc()),
193 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
194 llvm::StringRef(
"\n"))
196 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
198 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
199 const VarDecl *VDecl = If->getConditionVariable();
201 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
202 llvm::StringRef(
";\n") +
203 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
205 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
207 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
209 removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
210 }
else if (WarnOnUnfixable) {
212 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
217 if (checkInitDeclUsageInElse(If) !=
nullptr) {
221 DiagnosticBuilder Diag =
diag(ElseLoc, WarningMessage)
222 << ControlFlowInterruptor;
223 Diag << tooling::fixit::createReplacement(
224 SourceRange(If->getIfLoc()),
225 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
227 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
229 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
230 removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
231 }
else if (WarnOnUnfixable) {
233 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
238 DiagnosticBuilder Diag =
diag(ElseLoc, WarningMessage)
239 << ControlFlowInterruptor;
240 removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
SourceLocation Loc
'#' location in the include directive
const FunctionDecl * Decl
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
const DeclRefExpr * DeclRef
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.