clang-tools 22.0.0git
RedundantVoidArgCheck.cpp
Go to the documentation of this file.
1//===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/Lexer.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang::tidy::modernize {
16
17// Determine if the given QualType is a nullary function or pointer to same.
18static bool protoTypeHasNoParms(QualType QT) {
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;
25 return false;
26}
27
28static const char FunctionId[] = "function";
29static const char TypedefId[] = "typedef";
30static const char FieldId[] = "field";
31static const char VarId[] = "var";
32static const char NamedCastId[] = "named-cast";
33static const char CStyleCastId[] = "c-style-cast";
34static const char ExplicitCastId[] = "explicit-cast";
35static const char LambdaId[] = "lambda";
36
37void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
38 Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
39 unless(isInstantiated()), unless(isExternC()))
40 .bind(FunctionId),
41 this);
42 Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId),
43 this);
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));
53 Finder->addMatcher(
54 cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
55 Finder->addMatcher(
56 cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
57 Finder->addMatcher(
58 cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
59 this);
60 Finder->addMatcher(
61 cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
62 Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
63}
64
65void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
66 const BoundNodes &Nodes = Result.Nodes;
67 if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId))
68 processFunctionDecl(Result, Function);
69 else if (const auto *TypedefName =
70 Nodes.getNodeAs<TypedefNameDecl>(TypedefId))
71 processTypedefNameDecl(Result, TypedefName);
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 =
77 Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId))
78 processNamedCastExpr(Result, NamedCast);
79 else if (const auto *CStyleCast =
80 Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId))
81 processExplicitCastExpr(Result, CStyleCast);
82 else if (const auto *ExplicitCast =
83 Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId))
84 processExplicitCastExpr(Result, ExplicitCast);
85 else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId))
86 processLambdaExpr(Result, Lambda);
87}
88
89void RedundantVoidArgCheck::processFunctionDecl(
90 const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
91 const auto *Method = dyn_cast<CXXMethodDecl>(Function);
92 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);
103 }
104 removeVoidArgumentTokens(Result, SourceRange(Start, End),
105 "function definition");
106 } else
107 removeVoidArgumentTokens(Result, SourceRange(Start, End),
108 "function declaration");
109}
110
111static bool isMacroIdentifier(const IdentifierTable &Idents,
112 const Token &ProtoToken) {
113 if (!ProtoToken.is(tok::TokenKind::raw_identifier))
114 return false;
115
116 IdentifierTable::iterator It = Idents.find(ProtoToken.getRawIdentifier());
117 if (It == Idents.end())
118 return false;
119
120 return It->second->hadMacroDefinition();
121}
122
123void RedundantVoidArgCheck::removeVoidArgumentTokens(
124 const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
125 StringRef GrammarLocation) {
126 CharSourceRange CharRange =
127 Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
128 *Result.SourceManager, getLangOpts());
129
130 std::string DeclText =
131 Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
132 .str();
133 Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
134 DeclText.data(), DeclText.data() + DeclText.size());
135 enum class TokenState {
136 Start,
137 MacroId,
138 MacroLeftParen,
139 MacroArguments,
140 LeftParen,
141 Void,
142 };
143 TokenState State = TokenState::Start;
144 Token VoidToken;
145 Token ProtoToken;
146 const IdentifierTable &Idents = Result.Context->Idents;
147 int MacroLevel = 0;
148 std::string Diagnostic =
149 ("redundant void argument list in " + GrammarLocation).str();
150
151 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
152 switch (State) {
153 case TokenState::Start:
154 if (ProtoToken.is(tok::TokenKind::l_paren))
155 State = TokenState::LeftParen;
156 else if (isMacroIdentifier(Idents, ProtoToken))
157 State = TokenState::MacroId;
158 break;
159 case TokenState::MacroId:
160 if (ProtoToken.is(tok::TokenKind::l_paren))
161 State = TokenState::MacroLeftParen;
162 else
163 State = TokenState::Start;
164 break;
165 case TokenState::MacroLeftParen:
166 ++MacroLevel;
167 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
168 if (isMacroIdentifier(Idents, ProtoToken))
169 State = TokenState::MacroId;
170 else
171 State = TokenState::MacroArguments;
172 } else if (ProtoToken.is(tok::TokenKind::r_paren)) {
173 --MacroLevel;
174 if (MacroLevel == 0)
175 State = TokenState::Start;
176 else
177 State = TokenState::MacroId;
178 } else
179 State = TokenState::MacroArguments;
180 break;
181 case TokenState::MacroArguments:
182 if (isMacroIdentifier(Idents, ProtoToken))
183 State = TokenState::MacroLeftParen;
184 else if (ProtoToken.is(tok::TokenKind::r_paren)) {
185 --MacroLevel;
186 if (MacroLevel == 0)
187 State = TokenState::Start;
188 }
189 break;
190 case TokenState::LeftParen:
191 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
192 if (isMacroIdentifier(Idents, ProtoToken))
193 State = TokenState::MacroId;
194 else if (ProtoToken.getRawIdentifier() == "void") {
195 State = TokenState::Void;
196 VoidToken = ProtoToken;
197 }
198 } else if (ProtoToken.is(tok::TokenKind::l_paren))
199 State = TokenState::LeftParen;
200 else
201 State = TokenState::Start;
202 break;
203 case TokenState::Void:
204 State = TokenState::Start;
205 if (ProtoToken.is(tok::TokenKind::r_paren))
206 removeVoidToken(VoidToken, Diagnostic);
207 else if (ProtoToken.is(tok::TokenKind::l_paren))
208 State = TokenState::LeftParen;
209 break;
210 }
211 }
212
213 if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
214 removeVoidToken(VoidToken, Diagnostic);
215}
216
217void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
218 StringRef Diagnostic) {
219 SourceLocation VoidLoc = VoidToken.getLocation();
220 diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
221}
222
223void RedundantVoidArgCheck::processTypedefNameDecl(
224 const MatchFinder::MatchResult &Result,
225 const TypedefNameDecl *TypedefName) {
226 if (protoTypeHasNoParms(TypedefName->getUnderlyingType()))
227 removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
228 isa<TypedefDecl>(TypedefName) ? "typedef"
229 : "type alias");
230}
231
232void RedundantVoidArgCheck::processFieldDecl(
233 const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
234 if (protoTypeHasNoParms(Member->getType()))
235 removeVoidArgumentTokens(Result, Member->getSourceRange(),
236 "field declaration");
237}
238
239void RedundantVoidArgCheck::processVarDecl(
240 const MatchFinder::MatchResult &Result, const VarDecl *Var) {
241 if (protoTypeHasNoParms(Var->getType())) {
242 SourceLocation Begin = Var->getBeginLoc();
243 if (Var->hasInit()) {
244 SourceLocation InitStart =
245 Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
246 .getLocWithOffset(-1);
247 removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
248 "variable declaration with initializer");
249 } else
250 removeVoidArgumentTokens(Result, Var->getSourceRange(),
251 "variable declaration");
252 }
253}
254
255void RedundantVoidArgCheck::processNamedCastExpr(
256 const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
257 if (protoTypeHasNoParms(NamedCast->getTypeAsWritten()))
258 removeVoidArgumentTokens(
259 Result,
260 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
261 "named cast");
262}
263
264void RedundantVoidArgCheck::processExplicitCastExpr(
265 const MatchFinder::MatchResult &Result,
266 const ExplicitCastExpr *ExplicitCast) {
267 if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten()))
268 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
269 "cast expression");
270}
271
272void RedundantVoidArgCheck::processLambdaExpr(
273 const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
274 if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
275 Lambda->hasExplicitParameters()) {
276 SourceManager *SM = Result.SourceManager;
277 TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
278 removeVoidArgumentTokens(Result,
279 {SM->getSpellingLoc(TL.getBeginLoc()),
280 SM->getSpellingLoc(TL.getEndLoc())},
281 "lambda expression");
282 }
283}
284
285} // namespace clang::tidy::modernize
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
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 ExplicitCastId[]
static constexpr llvm::StringLiteral TypedefName