10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchers.h" 12 #include "clang/Lex/Lexer.h" 18 namespace readability {
21 tok::TokenKind getTokenKind(SourceLocation
Loc,
const SourceManager &SM,
22 const ASTContext *Context) {
24 SourceLocation Beginning =
25 Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
27 Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
28 assert(!Invalid &&
"Expected a valid token.");
31 return tok::NUM_TOKENS;
36 SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
37 const SourceManager &SM,
38 const ASTContext *Context) {
39 assert(Loc.isValid());
41 while (isWhitespace(*SM.getCharacterData(Loc)))
42 Loc = Loc.getLocWithOffset(1);
44 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
45 if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
49 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
53 SourceLocation findEndLocation(SourceLocation LastTokenLoc,
54 const SourceManager &SM,
55 const ASTContext *Context) {
57 Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
60 assert(Loc.isValid());
61 bool SkipEndWhitespaceAndComments =
true;
62 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
63 if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
64 TokKind == tok::r_brace) {
67 SkipEndWhitespaceAndComments =
false;
70 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
72 if (SkipEndWhitespaceAndComments) {
73 Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
74 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
75 if (TokKind == tok::semi)
76 Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
80 assert(Loc.isValid());
81 while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
82 Loc = Loc.getLocWithOffset(1);
85 if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
89 tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
90 if (TokKind != tok::comment) {
95 SourceLocation TokEndLoc =
96 Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
97 SourceRange TokRange(Loc, TokEndLoc);
98 StringRef Comment = Lexer::getSourceText(
100 if (Comment.startswith(
"/*") && Comment.find(
'\n') != StringRef::npos) {
114 BracesAroundStatementsCheck::BracesAroundStatementsCheck(
118 ShortStatementLines(Options.get(
"ShortStatementLines", 0U)) {}
122 Options.
store(Opts,
"ShortStatementLines", ShortStatementLines);
126 Finder->addMatcher(ifStmt().bind(
"if"),
this);
127 Finder->addMatcher(whileStmt().bind(
"while"),
this);
128 Finder->addMatcher(doStmt().bind(
"do"),
this);
129 Finder->addMatcher(forStmt().bind(
"for"),
this);
130 Finder->addMatcher(cxxForRangeStmt().bind(
"for-range"),
this);
134 const MatchFinder::MatchResult &
Result) {
135 const SourceManager &SM = *Result.SourceManager;
136 const ASTContext *Context = Result.Context;
139 if (
auto S = Result.Nodes.getNodeAs<ForStmt>(
"for")) {
140 checkStmt(Result, S->getBody(), S->getRParenLoc());
141 }
else if (
auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>(
"for-range")) {
142 checkStmt(Result, S->getBody(), S->getRParenLoc());
143 }
else if (
auto S = Result.Nodes.getNodeAs<DoStmt>(
"do")) {
144 checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
145 }
else if (
auto S = Result.Nodes.getNodeAs<WhileStmt>(
"while")) {
146 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
147 if (StartLoc.isInvalid())
149 checkStmt(Result, S->getBody(), StartLoc);
150 }
else if (
auto S = Result.Nodes.getNodeAs<IfStmt>(
"if")) {
151 SourceLocation StartLoc = findRParenLoc(S, SM, Context);
152 if (StartLoc.isInvalid())
154 if (ForceBracesStmts.erase(S))
155 ForceBracesStmts.insert(S->getThen());
156 bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
157 const Stmt *Else = S->getElse();
158 if (Else && BracedIf)
159 ForceBracesStmts.insert(Else);
160 if (Else && !isa<IfStmt>(Else)) {
162 checkStmt(Result, Else, S->getElseLoc());
165 llvm_unreachable(
"Invalid match");
170 template <
typename IfOrWhileStmt>
172 BracesAroundStatementsCheck::findRParenLoc(
const IfOrWhileStmt *S,
173 const SourceManager &SM,
174 const ASTContext *Context) {
176 if (S->getBeginLoc().isMacroID())
177 return SourceLocation();
179 SourceLocation CondEndLoc = S->getCond()->getEndLoc();
180 if (
const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
181 CondEndLoc = CondVar->getEndLoc();
183 if (!CondEndLoc.isValid()) {
184 return SourceLocation();
187 SourceLocation PastCondEndLoc =
188 Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
189 if (PastCondEndLoc.isInvalid())
190 return SourceLocation();
191 SourceLocation RParenLoc =
192 forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
193 if (RParenLoc.isInvalid())
194 return SourceLocation();
195 tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
196 if (TokKind != tok::r_paren)
197 return SourceLocation();
203 bool BracesAroundStatementsCheck::checkStmt(
204 const MatchFinder::MatchResult &
Result,
const Stmt *S,
205 SourceLocation InitialLoc, SourceLocation EndLocHint) {
213 if (!S || isa<CompoundStmt>(S)) {
218 if (!InitialLoc.isValid())
220 const SourceManager &SM = *Result.SourceManager;
221 const ASTContext *Context = Result.Context;
224 CharSourceRange FileRange = Lexer::makeFileCharRange(
226 Context->getLangOpts());
227 if (FileRange.isInvalid())
233 InitialLoc = Lexer::makeFileCharRange(
234 CharSourceRange::getCharRange(InitialLoc, S->getBeginLoc()),
235 SM, Context->getLangOpts())
237 if (InitialLoc.isInvalid())
239 SourceLocation StartLoc =
240 Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
243 SourceLocation EndLoc;
244 std::string ClosingInsertion;
245 if (EndLocHint.isValid()) {
247 ClosingInsertion =
"} ";
249 const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
250 EndLoc = findEndLocation(FREnd, SM, Context);
251 ClosingInsertion =
"\n}";
254 assert(StartLoc.isValid());
255 assert(EndLoc.isValid());
258 if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
259 unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
260 unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
261 if (EndLine - StartLine < ShortStatementLines)
265 auto Diag =
diag(StartLoc,
"statement should be inside braces");
266 Diag << FixItHint::CreateInsertion(StartLoc,
" {")
267 << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
272 ForceBracesStmts.clear();
SourceLocation Loc
'#' location in the include directive
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
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.
void onEndOfTranslationUnit() override
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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.