1 //===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "RedundantVoidArgCheck.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/Lexer.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::modernize
{
19 // Determine if the given QualType is a nullary function or pointer to same.
20 bool protoTypeHasNoParms(QualType QT
) {
21 if (const auto *PT
= QT
->getAs
<PointerType
>())
22 QT
= PT
->getPointeeType();
23 if (auto *MPT
= QT
->getAs
<MemberPointerType
>())
24 QT
= MPT
->getPointeeType();
25 if (const auto *FP
= QT
->getAs
<FunctionProtoType
>())
26 return FP
->getNumParams() == 0;
30 const char FunctionId
[] = "function";
31 const char TypedefId
[] = "typedef";
32 const char FieldId
[] = "field";
33 const char VarId
[] = "var";
34 const char NamedCastId
[] = "named-cast";
35 const char CStyleCastId
[] = "c-style-cast";
36 const char ExplicitCastId
[] = "explicit-cast";
37 const char LambdaId
[] = "lambda";
41 void RedundantVoidArgCheck::registerMatchers(MatchFinder
*Finder
) {
42 Finder
->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
43 unless(isInstantiated()), unless(isExternC()))
46 Finder
->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId
),
48 auto ParenFunctionType
= parenType(innerType(functionType()));
49 auto PointerToFunctionType
= pointee(ParenFunctionType
);
50 auto FunctionOrMemberPointer
=
51 anyOf(hasType(pointerType(PointerToFunctionType
)),
52 hasType(memberPointerType(PointerToFunctionType
)));
53 Finder
->addMatcher(fieldDecl(FunctionOrMemberPointer
).bind(FieldId
), this);
54 Finder
->addMatcher(varDecl(FunctionOrMemberPointer
).bind(VarId
), this);
55 auto CastDestinationIsFunction
=
56 hasDestinationType(pointsTo(ParenFunctionType
));
58 cStyleCastExpr(CastDestinationIsFunction
).bind(CStyleCastId
), this);
60 cxxStaticCastExpr(CastDestinationIsFunction
).bind(NamedCastId
), this);
62 cxxReinterpretCastExpr(CastDestinationIsFunction
).bind(NamedCastId
),
65 cxxConstCastExpr(CastDestinationIsFunction
).bind(NamedCastId
), this);
66 Finder
->addMatcher(lambdaExpr().bind(LambdaId
), this);
69 void RedundantVoidArgCheck::check(const MatchFinder::MatchResult
&Result
) {
70 const BoundNodes
&Nodes
= Result
.Nodes
;
71 if (const auto *Function
= Nodes
.getNodeAs
<FunctionDecl
>(FunctionId
))
72 processFunctionDecl(Result
, Function
);
73 else if (const auto *TypedefName
=
74 Nodes
.getNodeAs
<TypedefNameDecl
>(TypedefId
))
75 processTypedefNameDecl(Result
, TypedefName
);
76 else if (const auto *Member
= Nodes
.getNodeAs
<FieldDecl
>(FieldId
))
77 processFieldDecl(Result
, Member
);
78 else if (const auto *Var
= Nodes
.getNodeAs
<VarDecl
>(VarId
))
79 processVarDecl(Result
, Var
);
80 else if (const auto *NamedCast
=
81 Nodes
.getNodeAs
<CXXNamedCastExpr
>(NamedCastId
))
82 processNamedCastExpr(Result
, NamedCast
);
83 else if (const auto *CStyleCast
=
84 Nodes
.getNodeAs
<CStyleCastExpr
>(CStyleCastId
))
85 processExplicitCastExpr(Result
, CStyleCast
);
86 else if (const auto *ExplicitCast
=
87 Nodes
.getNodeAs
<ExplicitCastExpr
>(ExplicitCastId
))
88 processExplicitCastExpr(Result
, ExplicitCast
);
89 else if (const auto *Lambda
= Nodes
.getNodeAs
<LambdaExpr
>(LambdaId
))
90 processLambdaExpr(Result
, Lambda
);
93 void RedundantVoidArgCheck::processFunctionDecl(
94 const MatchFinder::MatchResult
&Result
, const FunctionDecl
*Function
) {
95 const auto *Method
= dyn_cast
<CXXMethodDecl
>(Function
);
96 SourceLocation Start
= Method
&& Method
->getParent()->isLambda()
97 ? Method
->getBeginLoc()
98 : Function
->getLocation();
99 SourceLocation End
= Function
->getEndLoc();
100 if (Function
->isThisDeclarationADefinition()) {
101 if (const Stmt
*Body
= Function
->getBody()) {
102 End
= Body
->getBeginLoc();
103 if (End
.isMacroID() &&
104 Result
.SourceManager
->isAtStartOfImmediateMacroExpansion(End
))
105 End
= Result
.SourceManager
->getExpansionLoc(End
);
106 End
= End
.getLocWithOffset(-1);
108 removeVoidArgumentTokens(Result
, SourceRange(Start
, End
),
109 "function definition");
111 removeVoidArgumentTokens(Result
, SourceRange(Start
, End
),
112 "function declaration");
115 bool isMacroIdentifier(const IdentifierTable
&Idents
, const Token
&ProtoToken
) {
116 if (!ProtoToken
.is(tok::TokenKind::raw_identifier
))
119 IdentifierTable::iterator It
= Idents
.find(ProtoToken
.getRawIdentifier());
120 if (It
== Idents
.end())
123 return It
->second
->hadMacroDefinition();
126 void RedundantVoidArgCheck::removeVoidArgumentTokens(
127 const ast_matchers::MatchFinder::MatchResult
&Result
, SourceRange Range
,
128 StringRef GrammarLocation
) {
129 CharSourceRange CharRange
=
130 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range
),
131 *Result
.SourceManager
, getLangOpts());
133 std::string DeclText
=
134 Lexer::getSourceText(CharRange
, *Result
.SourceManager
, getLangOpts())
136 Lexer
PrototypeLexer(CharRange
.getBegin(), getLangOpts(), DeclText
.data(),
137 DeclText
.data(), DeclText
.data() + DeclText
.size());
138 enum class TokenState
{
146 TokenState State
= TokenState::Start
;
149 const IdentifierTable
&Idents
= Result
.Context
->Idents
;
151 std::string Diagnostic
=
152 ("redundant void argument list in " + GrammarLocation
).str();
154 while (!PrototypeLexer
.LexFromRawLexer(ProtoToken
)) {
156 case TokenState::Start
:
157 if (ProtoToken
.is(tok::TokenKind::l_paren
))
158 State
= TokenState::LeftParen
;
159 else if (isMacroIdentifier(Idents
, ProtoToken
))
160 State
= TokenState::MacroId
;
162 case TokenState::MacroId
:
163 if (ProtoToken
.is(tok::TokenKind::l_paren
))
164 State
= TokenState::MacroLeftParen
;
166 State
= TokenState::Start
;
168 case TokenState::MacroLeftParen
:
170 if (ProtoToken
.is(tok::TokenKind::raw_identifier
)) {
171 if (isMacroIdentifier(Idents
, ProtoToken
))
172 State
= TokenState::MacroId
;
174 State
= TokenState::MacroArguments
;
175 } else if (ProtoToken
.is(tok::TokenKind::r_paren
)) {
178 State
= TokenState::Start
;
180 State
= TokenState::MacroId
;
182 State
= TokenState::MacroArguments
;
184 case TokenState::MacroArguments
:
185 if (isMacroIdentifier(Idents
, ProtoToken
))
186 State
= TokenState::MacroLeftParen
;
187 else if (ProtoToken
.is(tok::TokenKind::r_paren
)) {
190 State
= TokenState::Start
;
193 case TokenState::LeftParen
:
194 if (ProtoToken
.is(tok::TokenKind::raw_identifier
)) {
195 if (isMacroIdentifier(Idents
, ProtoToken
))
196 State
= TokenState::MacroId
;
197 else if (ProtoToken
.getRawIdentifier() == "void") {
198 State
= TokenState::Void
;
199 VoidToken
= ProtoToken
;
201 } else if (ProtoToken
.is(tok::TokenKind::l_paren
))
202 State
= TokenState::LeftParen
;
204 State
= TokenState::Start
;
206 case TokenState::Void
:
207 State
= TokenState::Start
;
208 if (ProtoToken
.is(tok::TokenKind::r_paren
))
209 removeVoidToken(VoidToken
, Diagnostic
);
210 else if (ProtoToken
.is(tok::TokenKind::l_paren
))
211 State
= TokenState::LeftParen
;
216 if (State
== TokenState::Void
&& ProtoToken
.is(tok::TokenKind::r_paren
))
217 removeVoidToken(VoidToken
, Diagnostic
);
220 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken
,
221 StringRef Diagnostic
) {
222 SourceLocation VoidLoc
= VoidToken
.getLocation();
223 diag(VoidLoc
, Diagnostic
) << FixItHint::CreateRemoval(VoidLoc
);
226 void RedundantVoidArgCheck::processTypedefNameDecl(
227 const MatchFinder::MatchResult
&Result
,
228 const TypedefNameDecl
*TypedefName
) {
229 if (protoTypeHasNoParms(TypedefName
->getUnderlyingType()))
230 removeVoidArgumentTokens(Result
, TypedefName
->getSourceRange(),
231 isa
<TypedefDecl
>(TypedefName
) ? "typedef"
235 void RedundantVoidArgCheck::processFieldDecl(
236 const MatchFinder::MatchResult
&Result
, const FieldDecl
*Member
) {
237 if (protoTypeHasNoParms(Member
->getType()))
238 removeVoidArgumentTokens(Result
, Member
->getSourceRange(),
239 "field declaration");
242 void RedundantVoidArgCheck::processVarDecl(
243 const MatchFinder::MatchResult
&Result
, const VarDecl
*Var
) {
244 if (protoTypeHasNoParms(Var
->getType())) {
245 SourceLocation Begin
= Var
->getBeginLoc();
246 if (Var
->hasInit()) {
247 SourceLocation InitStart
=
248 Result
.SourceManager
->getExpansionLoc(Var
->getInit()->getBeginLoc())
249 .getLocWithOffset(-1);
250 removeVoidArgumentTokens(Result
, SourceRange(Begin
, InitStart
),
251 "variable declaration with initializer");
253 removeVoidArgumentTokens(Result
, Var
->getSourceRange(),
254 "variable declaration");
258 void RedundantVoidArgCheck::processNamedCastExpr(
259 const MatchFinder::MatchResult
&Result
, const CXXNamedCastExpr
*NamedCast
) {
260 if (protoTypeHasNoParms(NamedCast
->getTypeAsWritten()))
261 removeVoidArgumentTokens(
263 NamedCast
->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
267 void RedundantVoidArgCheck::processExplicitCastExpr(
268 const MatchFinder::MatchResult
&Result
,
269 const ExplicitCastExpr
*ExplicitCast
) {
270 if (protoTypeHasNoParms(ExplicitCast
->getTypeAsWritten()))
271 removeVoidArgumentTokens(Result
, ExplicitCast
->getSourceRange(),
275 void RedundantVoidArgCheck::processLambdaExpr(
276 const MatchFinder::MatchResult
&Result
, const LambdaExpr
*Lambda
) {
277 if (Lambda
->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
278 Lambda
->hasExplicitParameters()) {
279 SourceManager
*SM
= Result
.SourceManager
;
280 TypeLoc TL
= Lambda
->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
281 removeVoidArgumentTokens(Result
,
282 {SM
->getSpellingLoc(TL
.getBeginLoc()),
283 SM
->getSpellingLoc(TL
.getEndLoc())},
284 "lambda expression");
288 } // namespace clang::tidy::modernize