clang-tools  3.9.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  Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
49  unless(isExternC()))
50  .bind(FunctionId),
51  this);
52  Finder->addMatcher(typedefNameDecl().bind(TypedefId), this);
53  auto ParenFunctionType = parenType(innerType(functionType()));
54  auto PointerToFunctionType = pointee(ParenFunctionType);
55  auto FunctionOrMemberPointer =
56  anyOf(hasType(pointerType(PointerToFunctionType)),
57  hasType(memberPointerType(PointerToFunctionType)));
58  Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
59  Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
60  auto CastDestinationIsFunction =
61  hasDestinationType(pointsTo(ParenFunctionType));
62  Finder->addMatcher(
63  cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
64  Finder->addMatcher(
65  cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
66  Finder->addMatcher(
67  cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
68  this);
69  Finder->addMatcher(
70  cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
71  Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
72 }
73 
74 void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
75  if (!Result.Context->getLangOpts().CPlusPlus) {
76  return;
77  }
78 
79  const BoundNodes &Nodes = Result.Nodes;
80  if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
81  processFunctionDecl(Result, Function);
82  } else if (const auto *TypedefName =
83  Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
84  processTypedefNameDecl(Result, TypedefName);
85  } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
86  processFieldDecl(Result, Member);
87  } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
88  processVarDecl(Result, Var);
89  } else if (const auto *NamedCast =
90  Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
91  processNamedCastExpr(Result, NamedCast);
92  } else if (const auto *CStyleCast =
93  Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
94  processExplicitCastExpr(Result, CStyleCast);
95  } else if (const auto *ExplicitCast =
96  Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
97  processExplicitCastExpr(Result, ExplicitCast);
98  } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
99  processLambdaExpr(Result, Lambda);
100  }
101 }
102 
103 void RedundantVoidArgCheck::processFunctionDecl(
104  const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
105  if (Function->isThisDeclarationADefinition()) {
106  const Stmt *Body = Function->getBody();
107  SourceLocation Start = Function->getLocStart();
108  SourceLocation End =
109  Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
110  removeVoidArgumentTokens(Result, SourceRange(Start, End),
111  "function definition");
112  } else {
113  removeVoidArgumentTokens(Result, Function->getSourceRange(),
114  "function declaration");
115  }
116 }
117 
118 void RedundantVoidArgCheck::removeVoidArgumentTokens(
119  const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
120  StringRef GrammarLocation) {
121  CharSourceRange CharRange = Lexer::makeFileCharRange(
122  CharSourceRange::getTokenRange(Range), *Result.SourceManager,
123  Result.Context->getLangOpts());
124 
125  std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager,
126  Result.Context->getLangOpts())
127  .str();
128  Lexer PrototypeLexer(CharRange.getBegin(), Result.Context->getLangOpts(),
129  DeclText.data(), DeclText.data(),
130  DeclText.data() + DeclText.size());
131  enum TokenState {
132  NothingYet,
133  SawLeftParen,
134  SawVoid,
135  };
136  TokenState State = NothingYet;
137  Token VoidToken;
138  Token ProtoToken;
139  std::string Diagnostic =
140  ("redundant void argument list in " + GrammarLocation).str();
141 
142  while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
143  switch (State) {
144  case NothingYet:
145  if (ProtoToken.is(tok::TokenKind::l_paren)) {
146  State = SawLeftParen;
147  }
148  break;
149  case SawLeftParen:
150  if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
151  ProtoToken.getRawIdentifier() == "void") {
152  State = SawVoid;
153  VoidToken = ProtoToken;
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->getLocStart();
204  if (Var->hasInit()) {
205  SourceLocation InitStart =
206  Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
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  SourceLocation Begin =
241  Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
242  SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
243  removeVoidArgumentTokens(Result, SourceRange(Begin, End),
244  "lambda expression");
245  }
246 }
247 
248 } // namespace modernize
249 } // namespace tidy
250 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:210
CharSourceRange Range
SourceRange for the file name.
const NamedDecl * Result
Definition: USRFinder.cpp:137