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