10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/Lexer.h"
19 if (
const auto *PT = QT->getAs<PointerType>())
20 QT = PT->getPointeeType();
21 if (
auto *MPT = QT->getAs<MemberPointerType>())
22 QT = MPT->getPointeeType();
23 if (
const auto *FP = QT->getAs<FunctionProtoType>())
24 return FP->getNumParams() == 0;
31static const char VarId[] =
"var";
38 Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
39 unless(isInstantiated()), unless(isExternC()))
42 Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(
TypedefId),
44 auto ParenFunctionType = parenType(innerType(functionType()));
45 auto PointerToFunctionType = pointee(ParenFunctionType);
46 auto FunctionOrMemberPointer =
47 anyOf(hasType(pointerType(PointerToFunctionType)),
48 hasType(memberPointerType(PointerToFunctionType)));
49 Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(
FieldId),
this);
50 Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(
VarId),
this);
51 auto CastDestinationIsFunction =
52 hasDestinationType(pointsTo(ParenFunctionType));
54 cStyleCastExpr(CastDestinationIsFunction).bind(
CStyleCastId),
this);
56 cxxStaticCastExpr(CastDestinationIsFunction).bind(
NamedCastId),
this);
58 cxxReinterpretCastExpr(CastDestinationIsFunction).bind(
NamedCastId),
61 cxxConstCastExpr(CastDestinationIsFunction).bind(
NamedCastId),
this);
62 Finder->addMatcher(lambdaExpr().bind(
LambdaId),
this);
66 const BoundNodes &Nodes = Result.Nodes;
67 if (
const auto *Function = Nodes.getNodeAs<FunctionDecl>(
FunctionId))
68 processFunctionDecl(Result, Function);
70 Nodes.getNodeAs<TypedefNameDecl>(
TypedefId))
72 else if (
const auto *Member = Nodes.getNodeAs<FieldDecl>(
FieldId))
73 processFieldDecl(Result, Member);
74 else if (
const auto *Var = Nodes.getNodeAs<VarDecl>(
VarId))
75 processVarDecl(Result, Var);
76 else if (
const auto *NamedCast =
78 processNamedCastExpr(Result, NamedCast);
79 else if (
const auto *CStyleCast =
81 processExplicitCastExpr(Result, CStyleCast);
82 else if (
const auto *ExplicitCast =
84 processExplicitCastExpr(Result, ExplicitCast);
85 else if (
const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(
LambdaId))
86 processLambdaExpr(Result, Lambda);
89void RedundantVoidArgCheck::processFunctionDecl(
90 const MatchFinder::MatchResult &Result,
const FunctionDecl *Function) {
91 const auto *Method = dyn_cast<CXXMethodDecl>(Function);
92 const SourceLocation Start = Method && Method->getParent()->isLambda()
93 ? Method->getBeginLoc()
94 : Function->getLocation();
95 SourceLocation End = Function->getEndLoc();
96 if (Function->isThisDeclarationADefinition()) {
97 if (
const Stmt *Body = Function->getBody()) {
98 End = Body->getBeginLoc();
99 if (End.isMacroID() &&
100 Result.SourceManager->isAtStartOfImmediateMacroExpansion(End))
101 End = Result.SourceManager->getExpansionLoc(End);
102 End = End.getLocWithOffset(-1);
104 removeVoidArgumentTokens(Result, SourceRange(Start, End),
105 "function definition");
107 removeVoidArgumentTokens(Result, SourceRange(Start, End),
108 "function declaration");
112 const Token &ProtoToken) {
113 if (!ProtoToken.is(tok::TokenKind::raw_identifier))
116 const IdentifierTable::iterator It =
117 Idents.find(ProtoToken.getRawIdentifier());
118 if (It == Idents.end())
121 return It->second->hadMacroDefinition();
124void RedundantVoidArgCheck::removeVoidArgumentTokens(
125 const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
126 StringRef GrammarLocation) {
127 const CharSourceRange CharRange =
128 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
129 *Result.SourceManager, getLangOpts());
131 std::string DeclText =
132 Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
134 Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
135 DeclText.data(), DeclText.data() + DeclText.size());
136 enum class TokenState {
144 TokenState State = TokenState::Start;
147 const IdentifierTable &Idents = Result.Context->Idents;
149 const std::string Diagnostic =
150 (
"redundant void argument list in " + GrammarLocation).str();
152 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
154 case TokenState::Start:
155 if (ProtoToken.is(tok::TokenKind::l_paren))
156 State = TokenState::LeftParen;
158 State = TokenState::MacroId;
160 case TokenState::MacroId:
161 if (ProtoToken.is(tok::TokenKind::l_paren))
162 State = TokenState::MacroLeftParen;
164 State = TokenState::Start;
166 case TokenState::MacroLeftParen:
168 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
170 State = TokenState::MacroId;
172 State = TokenState::MacroArguments;
173 }
else if (ProtoToken.is(tok::TokenKind::r_paren)) {
176 State = TokenState::Start;
178 State = TokenState::MacroId;
180 State = TokenState::MacroArguments;
182 case TokenState::MacroArguments:
184 State = TokenState::MacroLeftParen;
185 else if (ProtoToken.is(tok::TokenKind::r_paren)) {
188 State = TokenState::Start;
191 case TokenState::LeftParen:
192 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
194 State = TokenState::MacroId;
195 else if (ProtoToken.getRawIdentifier() ==
"void") {
196 State = TokenState::Void;
197 VoidToken = ProtoToken;
199 }
else if (ProtoToken.is(tok::TokenKind::l_paren))
200 State = TokenState::LeftParen;
202 State = TokenState::Start;
204 case TokenState::Void:
205 State = TokenState::Start;
206 if (ProtoToken.is(tok::TokenKind::r_paren))
207 removeVoidToken(VoidToken, Diagnostic);
208 else if (ProtoToken.is(tok::TokenKind::l_paren))
209 State = TokenState::LeftParen;
214 if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
215 removeVoidToken(VoidToken, Diagnostic);
218void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
219 StringRef Diagnostic) {
220 const SourceLocation VoidLoc = VoidToken.getLocation();
221 diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
224void RedundantVoidArgCheck::processTypedefNameDecl(
225 const MatchFinder::MatchResult &Result,
228 removeVoidArgumentTokens(Result,
TypedefName->getSourceRange(),
233void RedundantVoidArgCheck::processFieldDecl(
234 const MatchFinder::MatchResult &Result,
const FieldDecl *Member) {
236 removeVoidArgumentTokens(Result, Member->getSourceRange(),
237 "field declaration");
240void RedundantVoidArgCheck::processVarDecl(
241 const MatchFinder::MatchResult &Result,
const VarDecl *Var) {
243 const SourceLocation Begin = Var->getBeginLoc();
244 if (Var->hasInit()) {
245 const SourceLocation InitStart =
246 Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
247 .getLocWithOffset(-1);
248 removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
249 "variable declaration with initializer");
251 removeVoidArgumentTokens(Result, Var->getSourceRange(),
252 "variable declaration");
256void RedundantVoidArgCheck::processNamedCastExpr(
257 const MatchFinder::MatchResult &Result,
const CXXNamedCastExpr *NamedCast) {
259 removeVoidArgumentTokens(
261 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
265void RedundantVoidArgCheck::processExplicitCastExpr(
266 const MatchFinder::MatchResult &Result,
267 const ExplicitCastExpr *ExplicitCast) {
269 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
273void RedundantVoidArgCheck::processLambdaExpr(
274 const MatchFinder::MatchResult &Result,
const LambdaExpr *Lambda) {
275 if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
276 Lambda->hasExplicitParameters()) {
277 const SourceManager *SM = Result.SourceManager;
279 Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
280 removeVoidArgumentTokens(Result,
281 {SM->getSpellingLoc(TL.getBeginLoc()),
282 SM->getSpellingLoc(TL.getEndLoc())},
283 "lambda expression");
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static const char TypedefId[]
static const char VarId[]
static const char FunctionId[]
static const char CStyleCastId[]
static const char NamedCastId[]
static bool protoTypeHasNoParms(QualType QT)
static bool isMacroIdentifier(const IdentifierTable &Idents, const Token &ProtoToken)
static const char LambdaId[]
static const char FieldId[]
static const char ExplicitCastId[]
static constexpr llvm::StringLiteral TypedefName