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