11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
21 static llvm::ArrayRef<
22 std::pair<readability::TrailingCommaCheck::CommaPolicyKind, StringRef>>
42static bool isSingleLine(SourceRange Range,
const SourceManager &SM) {
43 return SM.getExpansionLineNumber(Range.getBegin()) ==
44 SM.getExpansionLineNumber(Range.getEnd());
48 const SourceManager &SM,
49 const LangOptions &LangOpts) {
50 if (Tok.is(tok::hash))
52 const std::optional<Token> Prev = Lexer::findPreviousToken(
53 Tok.getLocation(), SM, LangOpts,
false);
54 return Prev && Prev->is(tok::hash) &&
55 SM.getExpansionLineNumber(Prev->getLocation()) ==
56 SM.getExpansionLineNumber(Tok.getLocation());
62 AST_POLYMORPHIC_SUPPORTED_TYPES(EnumDecl,
64 return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
67AST_MATCHER(EnumDecl, isEmptyEnum) {
return Node.enumerators().empty(); }
69AST_MATCHER(InitListExpr, isEmptyInitList) {
return Node.getNumInits() == 0; }
76 SingleLineCommaPolicy(
82 configurationDiag(
"The check '%0' will not perform any analysis because "
83 "'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' are "
84 "both set to 'Ignore'.")
89 Options.store(Opts,
"SingleLineCommaPolicy", SingleLineCommaPolicy);
90 Options.store(Opts,
"MultiLineCommaPolicy", MultiLineCommaPolicy);
95 enumDecl(isDefinition(), unless(isEmptyEnum()), unless(isMacro()))
99 Finder->addMatcher(initListExpr(unless(isEmptyInitList()), unless(isMacro()),
100 unless(isImplicit()))
106 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"enum"))
107 checkEnumDecl(Enum, Result);
108 else if (
const auto *InitList =
109 Result.Nodes.getNodeAs<InitListExpr>(
"initlist"))
110 checkInitListExpr(InitList, Result);
112 llvm_unreachable(
"No matches found");
115void TrailingCommaCheck::checkEnumDecl(
const EnumDecl *Enum,
116 const MatchFinder::MatchResult &Result) {
118 {Enum->getBeginLoc(), Enum->getEndLoc()}, *Result.SourceManager);
120 IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
125 const std::optional<Token> LastTok = Lexer::findPreviousToken(
126 Enum->getBraceRange().getEnd(), *Result.SourceManager, getLangOpts(),
135 emitDiag(LastTok->getLocation(), LastTok, DiagKind::Enum, Result, Policy);
138void TrailingCommaCheck::checkInitListExpr(
139 const InitListExpr *InitList,
const MatchFinder::MatchResult &Result) {
141 if (
const InitListExpr *SynInitInitList = InitList->getSyntacticForm()) {
142 if (SynInitInitList->getNumInits() == 0)
144 InitList = SynInitInitList;
148 {InitList->getBeginLoc(), InitList->getEndLoc()}, *Result.SourceManager);
150 IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
155 const Expr *LastInit = InitList->inits().back();
159 if (isa<PackExpansionExpr>(LastInit))
162 const std::optional<Token> NextTok =
164 LastInit->getEndLoc(), *Result.SourceManager, getLangOpts());
168 if (NextTok && !NextTok->isOneOf(tok::comma, tok::r_brace))
171 emitDiag(LastInit->getEndLoc(), NextTok, DiagKind::InitList, Result, Policy);
174void TrailingCommaCheck::emitDiag(
175 SourceLocation LastLoc, std::optional<Token> Token, DiagKind Kind,
176 const ast_matchers::MatchFinder::MatchResult &Result,
177 CommaPolicyKind Policy) {
178 if (LastLoc.isInvalid() || !Token)
181 const bool HasTrailingComma = Token->is(tok::comma);
183 const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
184 LastLoc, 0, *Result.SourceManager, getLangOpts());
185 diag(InsertLoc,
"%select{initializer list|enum}0 should have "
187 << Kind << FixItHint::CreateInsertion(InsertLoc,
",");
189 const SourceLocation CommaLoc = Token->getLocation();
190 if (CommaLoc.isInvalid())
192 diag(CommaLoc,
"%select{initializer list|enum}0 should not have "
194 << Kind << FixItHint::CreateRemoval(CommaLoc);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
TrailingCommaCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
static bool isPreprocessorDirectiveToken(const Token &Tok, const SourceManager &SM, const LangOptions &LangOpts)
static bool isSingleLine(SourceRange Range, const SourceManager &SM)
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
llvm::StringMap< ClangTidyValue > OptionMap
static llvm::ArrayRef< std::pair< readability::TrailingCommaCheck::CommaPolicyKind, StringRef > > getEnumMapping()
This class should be specialized by any enum type that needs to be converted to and from an llvm::Str...