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