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());
50 AST_POLYMORPHIC_SUPPORTED_TYPES(EnumDecl,
52 return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
55AST_MATCHER(EnumDecl, isEmptyEnum) {
return Node.enumerators().empty(); }
57AST_MATCHER(InitListExpr, isEmptyInitList) {
return Node.getNumInits() == 0; }
64 SingleLineCommaPolicy(
70 configurationDiag(
"The check '%0' will not perform any analysis because "
71 "'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' are "
72 "both set to 'Ignore'.")
77 Options.store(Opts,
"SingleLineCommaPolicy", SingleLineCommaPolicy);
78 Options.store(Opts,
"MultiLineCommaPolicy", MultiLineCommaPolicy);
83 enumDecl(isDefinition(), unless(isEmptyEnum()), unless(isMacro()))
87 Finder->addMatcher(initListExpr(unless(isEmptyInitList()), unless(isMacro()))
93 if (
const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>(
"enum"))
94 checkEnumDecl(Enum, Result);
95 else if (
const auto *InitList =
96 Result.Nodes.getNodeAs<InitListExpr>(
"initlist"))
97 checkInitListExpr(InitList, Result);
99 llvm_unreachable(
"No matches found");
102void TrailingCommaCheck::checkEnumDecl(
const EnumDecl *Enum,
103 const MatchFinder::MatchResult &Result) {
105 {Enum->getBeginLoc(), Enum->getEndLoc()}, *Result.SourceManager);
107 IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
112 const std::optional<Token> LastTok =
113 Lexer::findPreviousToken(Enum->getBraceRange().getEnd(),
114 *Result.SourceManager, getLangOpts(),
false);
118 emitDiag(LastTok->getLocation(), LastTok, DiagKind::Enum, Result, Policy);
121void TrailingCommaCheck::checkInitListExpr(
122 const InitListExpr *InitList,
const MatchFinder::MatchResult &Result) {
124 if (
const InitListExpr *SynInitInitList = InitList->getSyntacticForm();
125 SynInitInitList && SynInitInitList->getNumInits() > 0)
126 InitList = SynInitInitList;
129 {InitList->getBeginLoc(), InitList->getEndLoc()}, *Result.SourceManager);
131 IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
136 const Expr *LastInit = InitList->inits().back();
140 if (isa<PackExpansionExpr>(LastInit))
143 const std::optional<Token> NextTok =
145 LastInit->getEndLoc(), *Result.SourceManager, getLangOpts());
149 if (NextTok && !NextTok->isOneOf(tok::comma, tok::r_brace))
152 emitDiag(LastInit->getEndLoc(), NextTok, DiagKind::InitList, Result, Policy);
155void TrailingCommaCheck::emitDiag(
156 SourceLocation LastLoc, std::optional<Token> Token, DiagKind Kind,
157 const ast_matchers::MatchFinder::MatchResult &Result,
158 CommaPolicyKind Policy) {
159 if (LastLoc.isInvalid() || !Token)
162 const bool HasTrailingComma = Token->is(tok::comma);
164 const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
165 LastLoc, 0, *Result.SourceManager, getLangOpts());
166 diag(InsertLoc,
"%select{initializer list|enum}0 should have "
168 << Kind << FixItHint::CreateInsertion(InsertLoc,
",");
170 const SourceLocation CommaLoc = Token->getLocation();
171 if (CommaLoc.isInvalid())
173 diag(CommaLoc,
"%select{initializer list|enum}0 should not have "
175 << 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 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...