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 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);
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 const IdentifierTable::iterator It =
117 Idents.find(ProtoToken.getRawIdentifier());
118 if (It == Idents.end())
119 return false;
120
121 return It->second->hadMacroDefinition();
122}
123
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());
130
131 std::string DeclText =
132 Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
133 .str();
134 Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
135 DeclText.data(), DeclText.data() + DeclText.size());
136 enum class TokenState {
137 Start,
138 MacroId,
139 MacroLeftParen,
140 MacroArguments,
141 LeftParen,
142 Void,
143 };
144 TokenState State = TokenState::Start;
145 Token VoidToken;
146 Token ProtoToken;
147 const IdentifierTable &Idents = Result.Context->Idents;
148 int MacroLevel = 0;
149 const std::string Diagnostic =
150 ("redundant void argument list in " + GrammarLocation).str();
151
152 while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
153 switch (State) {
154 case TokenState::Start:
155 if (ProtoToken.is(tok::TokenKind::l_paren))
156 State = TokenState::LeftParen;
157 else if (isMacroIdentifier(Idents, ProtoToken))
158 State = TokenState::MacroId;
159 break;
160 case TokenState::MacroId:
161 if (ProtoToken.is(tok::TokenKind::l_paren))
162 State = TokenState::MacroLeftParen;
163 else
164 State = TokenState::Start;
165 break;
166 case TokenState::MacroLeftParen:
167 ++MacroLevel;
168 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
169 if (isMacroIdentifier(Idents, ProtoToken))
170 State = TokenState::MacroId;
171 else
172 State = TokenState::MacroArguments;
173 } else if (ProtoToken.is(tok::TokenKind::r_paren)) {
174 --MacroLevel;
175 if (MacroLevel == 0)
176 State = TokenState::Start;
177 else
178 State = TokenState::MacroId;
179 } else
180 State = TokenState::MacroArguments;
181 break;
182 case TokenState::MacroArguments:
183 if (isMacroIdentifier(Idents, ProtoToken))
184 State = TokenState::MacroLeftParen;
185 else if (ProtoToken.is(tok::TokenKind::r_paren)) {
186 --MacroLevel;
187 if (MacroLevel == 0)
188 State = TokenState::Start;
189 }
190 break;
191 case TokenState::LeftParen:
192 if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
193 if (isMacroIdentifier(Idents, ProtoToken))
194 State = TokenState::MacroId;
195 else if (ProtoToken.getRawIdentifier() == "void") {
196 State = TokenState::Void;
197 VoidToken = ProtoToken;
198 }
199 } else if (ProtoToken.is(tok::TokenKind::l_paren))
200 State = TokenState::LeftParen;
201 else
202 State = TokenState::Start;
203 break;
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;
210 break;
211 }
212 }
213
214 if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
215 removeVoidToken(VoidToken, Diagnostic);
216}
217
218void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
219 StringRef Diagnostic) {
220 const SourceLocation VoidLoc = VoidToken.getLocation();
221 diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
222}
223
224void RedundantVoidArgCheck::processTypedefNameDecl(
225 const MatchFinder::MatchResult &Result,
226 const TypedefNameDecl *TypedefName) {
227 if (protoTypeHasNoParms(TypedefName->getUnderlyingType()))
228 removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
229 isa<TypedefDecl>(TypedefName) ? "typedef"
230 : "type alias");
231}
232
233void RedundantVoidArgCheck::processFieldDecl(
234 const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
235 if (protoTypeHasNoParms(Member->getType()))
236 removeVoidArgumentTokens(Result, Member->getSourceRange(),
237 "field declaration");
238}
239
240void RedundantVoidArgCheck::processVarDecl(
241 const MatchFinder::MatchResult &Result, const VarDecl *Var) {
242 if (protoTypeHasNoParms(Var->getType())) {
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");
250 } else
251 removeVoidArgumentTokens(Result, Var->getSourceRange(),
252 "variable declaration");
253 }
254}
255
256void RedundantVoidArgCheck::processNamedCastExpr(
257 const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
258 if (protoTypeHasNoParms(NamedCast->getTypeAsWritten()))
259 removeVoidArgumentTokens(
260 Result,
261 NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
262 "named cast");
263}
264
265void RedundantVoidArgCheck::processExplicitCastExpr(
266 const MatchFinder::MatchResult &Result,
267 const ExplicitCastExpr *ExplicitCast) {
268 if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten()))
269 removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
270 "cast expression");
271}
272
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;
278 const TypeLoc TL =
279 Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
280 removeVoidArgumentTokens(Result,
281 {SM->getSpellingLoc(TL.getBeginLoc()),
282 SM->getSpellingLoc(TL.getEndLoc())},
283 "lambda expression");
284 }
285}
286
287} // 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