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) {
32 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
33 if (
DeclRef->getDecl()->getID() == DeclIdentifier) {
37 for (
const Stmt *ChildNode : Node->children()) {
38 if (
const DeclRefExpr *Result = findUsage(ChildNode, DeclIdentifier)) {
47 findUsageRange(
const Stmt *Node,
48 const llvm::iterator_range<int64_t *> &DeclIdentifiers) {
51 if (
const auto *
DeclRef = dyn_cast<DeclRefExpr>(Node)) {
52 if (llvm::is_contained(DeclIdentifiers,
DeclRef->getDecl()->getID())) {
56 for (
const Stmt *ChildNode : Node->children()) {
57 if (
const DeclRefExpr *Result =
58 findUsageRange(ChildNode, DeclIdentifiers)) {
66 const DeclRefExpr *checkInitDeclUsageInElse(
const IfStmt *If) {
67 const auto *InitDeclStmt = dyn_cast_or_null<DeclStmt>(If->getInit());
70 if (InitDeclStmt->isSingleDecl()) {
71 const Decl *InitDecl = InitDeclStmt->getSingleDecl();
72 assert(isa<VarDecl>(InitDecl) &&
"SingleDecl must be a VarDecl");
73 return findUsage(If->getElse(), InitDecl->getID());
75 llvm::SmallVector<int64_t, 4> DeclIdentifiers;
76 for (
const Decl *ChildDecl : InitDeclStmt->decls()) {
77 assert(isa<VarDecl>(ChildDecl) &&
"Init Decls must be a VarDecl");
78 DeclIdentifiers.push_back(ChildDecl->getID());
80 return findUsageRange(If->getElse(), DeclIdentifiers);
83 const DeclRefExpr *checkConditionVarUsageInElse(
const IfStmt *If) {
84 const VarDecl *CondVar = If->getConditionVariable();
85 return CondVar !=
nullptr ? findUsage(If->getElse(), CondVar->getID())
89 bool containsDeclInScope(
const Stmt *Node) {
90 if (isa<DeclStmt>(Node)) {
93 if (
const auto *Compound = dyn_cast<CompoundStmt>(Node)) {
94 return llvm::any_of(Compound->body(), [](
const Stmt *SubNode) {
95 return isa<DeclStmt>(SubNode);
101 void removeElseAndBrackets(DiagnosticBuilder &Diag, ASTContext &Context,
102 const Stmt *Else, SourceLocation ElseLoc) {
103 auto Remap = [&](SourceLocation
Loc) {
104 return Context.getSourceManager().getExpansionLoc(
Loc);
106 auto TokLen = [&](SourceLocation
Loc) {
107 return Lexer::MeasureTokenLength(
Loc, Context.getSourceManager(),
108 Context.getLangOpts());
111 if (
const auto *CS = dyn_cast<CompoundStmt>(Else)) {
112 Diag << tooling::fixit::createRemoval(ElseLoc);
113 SourceLocation LBrace = CS->getLBracLoc();
114 SourceLocation RBrace = CS->getRBracLoc();
115 SourceLocation RangeStart =
116 Remap(LBrace).getLocWithOffset(TokLen(LBrace) + 1);
117 SourceLocation RangeEnd = Remap(RBrace).getLocWithOffset(-1);
119 llvm::StringRef Repl = Lexer::getSourceText(
121 Context.getSourceManager(), Context.getLangOpts());
122 Diag << tooling::fixit::createReplacement(CS->getSourceRange(), Repl);
124 SourceLocation ElseExpandedLoc = Remap(ElseLoc);
125 SourceLocation EndLoc = Remap(Else->getEndLoc());
127 llvm::StringRef Repl = Lexer::getSourceText(
129 ElseExpandedLoc.getLocWithOffset(TokLen(ElseLoc) + 1), EndLoc),
130 Context.getSourceManager(), Context.getLangOpts());
131 Diag << tooling::fixit::createReplacement(
132 SourceRange(ElseExpandedLoc, EndLoc), Repl);
137 ElseAfterReturnCheck::ElseAfterReturnCheck(StringRef
Name,
140 WarnOnUnfixable(Options.get(WarnOnUnfixableStr, true)) {}
143 Options.
store(Opts, WarnOnUnfixableStr, WarnOnUnfixable);
147 const auto InterruptsControlFlow =
148 stmt(anyOf(returnStmt().bind(ReturnStr), continueStmt().bind(ContinueStr),
149 breakStmt().bind(BreakStr),
150 expr(ignoringImplicit(cxxThrowExpr().bind(ThrowStr)))));
153 forEach(ifStmt(unless(isConstexpr()),
155 anyOf(InterruptsControlFlow,
156 compoundStmt(has(InterruptsControlFlow))))),
157 hasElse(stmt().bind(
"else")))
164 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
165 const auto *Else = Result.Nodes.getNodeAs<Stmt>(
"else");
166 const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>(
"cs");
168 bool IsLastInScope = OuterScope->body_back() == If;
169 SourceLocation ElseLoc = If->getElseLoc();
171 auto ControlFlowInterruptor = [&]() -> llvm::StringRef {
172 for (llvm::StringRef BindingName :
173 {ReturnStr, ContinueStr, BreakStr, ThrowStr})
174 if (Result.Nodes.getNodeAs<Stmt>(BindingName))
179 if (!IsLastInScope && containsDeclInScope(Else)) {
180 if (WarnOnUnfixable) {
182 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
187 if (checkConditionVarUsageInElse(If) !=
nullptr) {
191 DiagnosticBuilder Diag =
192 diag(ElseLoc, WarningMessage, clang::DiagnosticIDs::Level::Remark)
193 << ControlFlowInterruptor;
194 if (checkInitDeclUsageInElse(If) !=
nullptr) {
195 Diag << tooling::fixit::createReplacement(
196 SourceRange(If->getIfLoc()),
197 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
198 llvm::StringRef(
"\n"))
200 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
202 const DeclStmt *VDeclStmt = If->getConditionVariableDeclStmt();
203 const VarDecl *VDecl = If->getConditionVariable();
205 (tooling::fixit::getText(*VDeclStmt, *Result.Context) +
206 llvm::StringRef(
";\n") +
207 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
209 Diag << tooling::fixit::createReplacement(SourceRange(If->getIfLoc()),
211 << tooling::fixit::createReplacement(VDeclStmt->getSourceRange(),
213 removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
214 }
else if (WarnOnUnfixable) {
216 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
221 if (checkInitDeclUsageInElse(If) !=
nullptr) {
225 DiagnosticBuilder Diag =
diag(ElseLoc, WarningMessage)
226 << ControlFlowInterruptor;
227 Diag << tooling::fixit::createReplacement(
228 SourceRange(If->getIfLoc()),
229 (tooling::fixit::getText(*If->getInit(), *Result.Context) +
231 tooling::fixit::getText(If->getIfLoc(), *Result.Context))
233 << tooling::fixit::createRemoval(If->getInit()->getSourceRange());
234 removeElseAndBrackets(Diag, *Result.Context, Else, ElseLoc);
235 }
else if (WarnOnUnfixable) {
237 diag(ElseLoc, WarningMessage) << ControlFlowInterruptor;
242 DiagnosticBuilder Diag =
diag(ElseLoc, WarningMessage)
243 << ControlFlowInterruptor;
244 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.