10#include "../utils/LexerUtils.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
22 return llvm::all_of(
Node.decls(), [](
Decl *D) { return isa<VarDecl>(D); });
27 Finder->addMatcher(declStmt(onlyDeclaresVariables(), unless(isSingleDecl()),
28 hasParent(compoundStmt()))
35 const SourceManager &SM,
36 const LangOptions &LangOpts) {
37 assert(Indirections >= 0 &&
"Indirections must be non-negative");
38 if (Indirections == 0)
43 while (Indirections-- != 0) {
45 if (Start.isInvalid() || Start.isMacroID())
52 return R.getBegin().isMacroID() || R.getEnd().isMacroID();
60 if (T->isFunctionPointerType()) {
61 const auto *Pointee = T->getPointeeType()->castAs<FunctionType>();
63 Pointee->getReturnType().IgnoreParens().getTypePtr(), ++Indirections);
69 if (
const auto *AT = dyn_cast<ArrayType>(T))
73 if (isa<PointerType>(T) || isa<ReferenceType>(T))
81 if (isa<ArrayType>(T))
84 if ((isa<PointerType>(T) || isa<ReferenceType>(T)) &&
85 isa<PointerType>(T->getPointeeType()))
88 return isa<MemberPointerType>(T);
107static std::optional<std::vector<SourceRange>>
109 const LangOptions &LangOpts) {
110 std::size_t DeclCount = std::distance(DS->decl_begin(), DS->decl_end());
121 std::vector<SourceRange> Slices;
122 Slices.reserve(DeclCount + 1);
126 const auto *FirstDecl = dyn_cast<VarDecl>(*DS->decl_begin());
128 if (FirstDecl ==
nullptr)
141 FirstDecl->getLocation(),
149 if (FirstDecl->getType()->isFunctionPointerType())
157 if (Start.isInvalid() || Start.isMacroID())
161 if (T.is(tok::l_paren)) {
168 SourceRange DeclRange(DS->getBeginLoc(), Start);
169 if (DeclRange.isInvalid() ||
isMacroID(DeclRange))
174 Slices.emplace_back(DeclRange);
177 SourceLocation DeclBegin = Start;
178 for (
const auto &
Decl : DS->decls()) {
179 const auto *CurrentDecl = cast<VarDecl>(
Decl);
186 SourceLocation DeclEnd =
187 CurrentDecl->hasInit()
192 SourceRange VarNameRange(DeclBegin, DeclEnd);
193 if (VarNameRange.isInvalid() ||
isMacroID(VarNameRange))
196 Slices.emplace_back(VarNameRange);
197 DeclBegin = DeclEnd.getLocWithOffset(1);
202static std::optional<std::vector<StringRef>>
204 const LangOptions &LangOpts) {
205 std::vector<StringRef> Snippets;
206 Snippets.reserve(Ranges.size());
208 for (
const auto &
Range : Ranges) {
209 CharSourceRange CharRange = Lexer::getAsCharRange(
210 CharSourceRange::getCharRange(
Range.getBegin(),
Range.getEnd()), SM,
213 if (CharRange.isInvalid())
216 bool InvalidText =
false;
218 Lexer::getSourceText(CharRange, SM, LangOpts, &InvalidText);
223 Snippets.emplace_back(
Snippet);
230static std::vector<std::string>
233 assert(Snippets.size() > 2 &&
"Not enough snippets to create isolated decls");
234 std::vector<std::string> Decls(Snippets.size() - 1);
236 for (std::size_t I = 1; I < Snippets.size(); ++I)
237 Decls[I - 1] = Twine(Snippets[0])
238 .concat(Snippets[0].ends_with(
" ") ?
"" :
" ")
239 .concat(Snippets[I].ltrim())
247 const auto *WholeDecl = Result.Nodes.getNodeAs<DeclStmt>(
"decl_stmt");
250 diag(WholeDecl->getBeginLoc(),
251 "multiple declarations in a single statement reduces readability");
253 std::optional<std::vector<SourceRange>> PotentialRanges =
255 if (!PotentialRanges)
259 *PotentialRanges, *Result.SourceManager,
getLangOpts());
261 if (!PotentialSnippets)
265 std::string Replacement = llvm::join(
267 (Twine(
"\n") + Lexer::getIndentationForLine(WholeDecl->getBeginLoc(),
268 *Result.SourceManager))
271 Diag << FixItHint::CreateReplacement(WholeDecl->getSourceRange(),
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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.
AST_MATCHER(CXXMethodDecl, isStatic)
static std::optional< std::vector< StringRef > > collectSourceRanges(llvm::ArrayRef< SourceRange > Ranges, const SourceManager &SM, const LangOptions &LangOpts)
static SourceLocation findStartOfIndirection(SourceLocation Start, int Indirections, const SourceManager &SM, const LangOptions &LangOpts)
static std::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.
static std::vector< std::string > createIsolatedDecls(llvm::ArrayRef< StringRef > Snippets)
Expects a vector {TypeSnippet, Firstdecl, SecondDecl, ...}.
static int countIndirections(const Type *T, int Indirections=0)
This function counts the number of written indirections for the given Type T.
static bool isMacroID(SourceRange R)
static bool typeIsMemberPointer(const Type *T)
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,...
SourceLocation findNextTerminator(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.
SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
SourceLocation findPreviousAnyTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, TokenKind TK, TokenKinds... TKs)