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