11 #include "../utils/LexerUtils.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 19 namespace readability {
22 AST_MATCHER(DeclStmt, isSingleDecl) {
return Node.isSingleDecl(); }
24 return llvm::all_of(Node.decls(), [](Decl *
D) {
return isa<VarDecl>(
D); });
28 void IsolateDeclarationCheck::registerMatchers(MatchFinder *Finder) {
29 Finder->addMatcher(declStmt(onlyDeclaresVariables(), unless(isSingleDecl()),
30 hasParent(compoundStmt()))
37 const SourceManager &SM,
38 const LangOptions &LangOpts) {
39 assert(Indirections >= 0 &&
"Indirections must be non-negative");
40 if (Indirections == 0)
45 while (Indirections-- != 0) {
47 if (Start.isInvalid() || Start.isMacroID())
48 return SourceLocation();
54 return R.getBegin().isMacroID() || R.getEnd().isMacroID();
62 if (T->isFunctionPointerType()) {
63 const auto *Pointee = T->getPointeeType()->castAs<FunctionType>();
65 Pointee->getReturnType().IgnoreParens().getTypePtr(), ++Indirections);
71 if (
const auto *AT = dyn_cast<ArrayType>(T))
75 if (isa<PointerType>(T) || isa<ReferenceType>(T))
83 if (isa<ArrayType>(T))
86 if ((isa<PointerType>(T) || isa<ReferenceType>(T)) &&
87 isa<PointerType>(T->getPointeeType()))
90 return isa<MemberPointerType>(T);
109 static Optional<std::vector<SourceRange>>
111 const LangOptions &LangOpts) {
112 std::size_t DeclCount = std::distance(DS->decl_begin(), DS->decl_end());
123 std::vector<SourceRange> Slices;
124 Slices.reserve(DeclCount + 1);
128 const auto *FirstDecl = dyn_cast<VarDecl>(*DS->decl_begin());
130 if (FirstDecl ==
nullptr)
143 FirstDecl->getLocation(),
151 if (FirstDecl->getType()->isFunctionPointerType())
159 if (Start.isInvalid() || Start.isMacroID())
163 if (T.is(tok::l_paren)) {
170 SourceRange DeclRange(DS->getBeginLoc(), Start);
171 if (DeclRange.isInvalid() ||
isMacroID(DeclRange))
176 Slices.emplace_back(DeclRange);
179 SourceLocation DeclBegin = Start;
180 for (
const auto &Decl : DS->decls()) {
181 const auto *CurrentDecl = cast<VarDecl>(Decl);
188 SourceLocation DeclEnd =
189 CurrentDecl->hasInit()
194 SourceRange VarNameRange(DeclBegin, DeclEnd);
195 if (VarNameRange.isInvalid() ||
isMacroID(VarNameRange))
198 Slices.emplace_back(VarNameRange);
199 DeclBegin = DeclEnd.getLocWithOffset(1);
204 static Optional<std::vector<StringRef>>
206 const LangOptions &LangOpts) {
207 std::vector<StringRef> Snippets;
208 Snippets.reserve(Ranges.size());
210 for (
const auto &
Range : Ranges) {
211 CharSourceRange CharRange = Lexer::getAsCharRange(
212 CharSourceRange::getCharRange(
Range.getBegin(),
Range.getEnd()), SM,
215 if (CharRange.isInvalid())
218 bool InvalidText =
false;
220 Lexer::getSourceText(CharRange, SM, LangOpts, &InvalidText);
225 Snippets.emplace_back(Snippet);
232 static std::vector<std::string>
235 assert(Snippets.size() > 2 &&
"Not enough snippets to create isolated decls");
236 std::vector<std::string> Decls(Snippets.size() - 1);
238 for (std::size_t I = 1; I < Snippets.size(); ++I)
239 Decls[I - 1] = Twine(Snippets[0])
240 .concat(Snippets[0].endswith(
" ") ?
"" :
" ")
241 .concat(Snippets[I].ltrim())
248 void IsolateDeclarationCheck::check(
const MatchFinder::MatchResult &
Result) {
249 const auto *WholeDecl = Result.Nodes.getNodeAs<DeclStmt>(
"decl_stmt");
252 diag(WholeDecl->getBeginLoc(),
253 "multiple declarations in a single statement reduces readability");
255 Optional<std::vector<SourceRange>> PotentialRanges =
256 declRanges(WholeDecl, *Result.SourceManager, getLangOpts());
257 if (!PotentialRanges)
261 *PotentialRanges, *Result.SourceManager, getLangOpts());
263 if (!PotentialSnippets)
269 (Twine(
"\n") + Lexer::getIndentationForLine(WholeDecl->getBeginLoc(),
270 *Result.SourceManager))
273 Diag << FixItHint::CreateReplacement(WholeDecl->getSourceRange(),
bool rangeContainsExpansionsOrDirectives(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Re-lex the provide Range and return false if either a macro spans multiple tokens, a pre-processor directive or failure to retrieve the next token is found, otherwise true.
AST_MATCHER(BinaryOperator, isAssignmentOperator)
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.
static bool isMacroID(SourceRange R)
SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
static SourceLocation findStartOfIndirection(SourceLocation Start, int Indirections, const SourceManager &SM, const LangOptions &LangOpts)
static std::vector< std::string > createIsolatedDecls(llvm::ArrayRef< StringRef > Snippets)
Expects a vector {TypeSnippet, Firstdecl, SecondDecl, ...}.
static Optional< std::vector< StringRef > > collectSourceRanges(llvm::ArrayRef< SourceRange > Ranges, const SourceManager &SM, const LangOptions &LangOpts)
static int countIndirections(const Type *T, int Indirections=0)
This function counts the number of written indirections for the given Type T.
static Optional< std::vector< SourceRange > > declRanges(const DeclStmt *DS, const SourceManager &SM, const LangOptions &LangOpts)
This function tries to extract the SourceRanges that make up all declarations in this DeclStmt...
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
SourceLocation findPreviousAnyTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, TokenKind TK, TokenKinds... TKs)
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
static bool typeIsMemberPointer(const Type *T)