clang-tools  5.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(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->getLocStart();
107  SourceLocation End =
108  Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
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 {
153  State = NothingYet;
154  }
155  break;
156  case SawVoid:
157  State = NothingYet;
158  if (ProtoToken.is(tok::TokenKind::r_paren)) {
159  removeVoidToken(VoidToken, Diagnostic);
160  } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
161  State = SawLeftParen;
162  }
163  break;
164  }
165  }
166 
167  if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
168  removeVoidToken(VoidToken, Diagnostic);
169  }
170 }
171 
172 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
173  StringRef Diagnostic) {
174  SourceLocation VoidLoc(VoidToken.getLocation());
175  auto VoidRange =
176  CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3));
177  diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange);
178 }
179 
180 void RedundantVoidArgCheck::processTypedefNameDecl(
181  const MatchFinder::MatchResult &Result,
182  const TypedefNameDecl *TypedefName) {
183  if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
184  removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
185  isa<TypedefDecl>(TypedefName) ? "typedef"
186  : "type alias");
187  }
188 }
189 
190 void RedundantVoidArgCheck::processFieldDecl(
191  const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
192  if (protoTypeHasNoParms(Member->getType())) {
193  removeVoidArgumentTokens(Result, Member->getSourceRange(),
194  "field declaration");
195  }
196 }
197 
198 void RedundantVoidArgCheck::processVarDecl(
199  const MatchFinder::MatchResult &Result, const VarDecl *Var) {
200  if (protoTypeHasNoParms(Var->getType())) {
201  SourceLocation Begin = Var->getLocStart();
202  if (Var->hasInit()) {
203  SourceLocation InitStart =
204  Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
205  .getLocWithOffset(-1);
206  removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
207  "variable declaration with initializer");
208  } else {
209  removeVoidArgumentTokens(Result, Var->getSourceRange(),
210  "variable declaration");
211  }
212  }
213 }
214 
215 void RedundantVoidArgCheck::processNamedCastExpr(
216  const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
217  if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
218  removeVoidArgumentTokens(
219  Result,
220  NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
221  "named cast");
222  }
223 }
224 
225 void RedundantVoidArgCheck::processExplicitCastExpr(
226  const MatchFinder::MatchResult &Result,
227  const ExplicitCastExpr *ExplicitCast) {
228  if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
229  removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
230  "cast expression");
231  }
232 }
233 
234 void RedundantVoidArgCheck::processLambdaExpr(
235  const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
236  if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
237  Lambda->hasExplicitParameters()) {
238  SourceLocation Begin =
239  Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
240  SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
241  removeVoidArgumentTokens(Result, SourceRange(Begin, End),
242  "lambda expression");
243  }
244 }
245 
246 } // namespace modernize
247 } // namespace tidy
248 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
CharSourceRange Range
SourceRange for the file name.