clang-tools 8.0.0

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