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