clang-tools  3.8.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 
18 namespace {
19 
20 // Determine if the given QualType is a nullary function or pointer to same.
21 bool protoTypeHasNoParms(QualType QT) {
22  if (auto PT = QT->getAs<PointerType>()) {
23  QT = PT->getPointeeType();
24  }
25  if (auto *MPT = QT->getAs<MemberPointerType>()) {
26  QT = MPT->getPointeeType();
27  }
28  if (auto FP = QT->getAs<FunctionProtoType>()) {
29  return FP->getNumParams() == 0;
30  }
31  return false;
32 }
33 
34 const char FunctionId[] = "function";
35 const char TypedefId[] = "typedef";
36 const char FieldId[] = "field";
37 const char VarId[] = "var";
38 const char NamedCastId[] = "named-cast";
39 const char CStyleCastId[] = "c-style-cast";
40 const char ExplicitCastId[] = "explicit-cast";
41 const char LambdaId[] = "lambda";
42 
43 } // namespace
44 
45 namespace tidy {
46 namespace modernize {
47 
48 void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
49  Finder->addMatcher(functionDecl(isExpansionInMainFile(), parameterCountIs(0),
50  unless(isImplicit()), unless(isExternC()))
51  .bind(FunctionId),
52  this);
53  Finder->addMatcher(typedefDecl(isExpansionInMainFile()).bind(TypedefId),
54  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(
61  fieldDecl(isExpansionInMainFile(), FunctionOrMemberPointer).bind(FieldId),
62  this);
63  Finder->addMatcher(
64  varDecl(isExpansionInMainFile(), FunctionOrMemberPointer).bind(VarId),
65  this);
66  auto CastDestinationIsFunction =
67  hasDestinationType(pointsTo(ParenFunctionType));
68  Finder->addMatcher(
69  cStyleCastExpr(isExpansionInMainFile(), CastDestinationIsFunction)
70  .bind(CStyleCastId),
71  this);
72  Finder->addMatcher(
73  cxxStaticCastExpr(isExpansionInMainFile(), CastDestinationIsFunction)
74  .bind(NamedCastId),
75  this);
76  Finder->addMatcher(
77  cxxReinterpretCastExpr(isExpansionInMainFile(), CastDestinationIsFunction)
78  .bind(NamedCastId),
79  this);
80  Finder->addMatcher(
81  cxxConstCastExpr(isExpansionInMainFile(), CastDestinationIsFunction)
82  .bind(NamedCastId),
83  this);
84  Finder->addMatcher(lambdaExpr(isExpansionInMainFile()).bind(LambdaId), this);
85 }
86 
87 void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
88  if (!Result.Context->getLangOpts().CPlusPlus) {
89  return;
90  }
91 
92  const BoundNodes &Nodes = Result.Nodes;
93  if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
94  processFunctionDecl(Result, Function);
95  } else if (const auto *Typedef = Nodes.getNodeAs<TypedefDecl>(TypedefId)) {
96  processTypedefDecl(Result, Typedef);
97  } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
98  processFieldDecl(Result, Member);
99  } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
100  processVarDecl(Result, Var);
101  } else if (const auto *NamedCast =
102  Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
103  processNamedCastExpr(Result, NamedCast);
104  } else if (const auto *CStyleCast =
105  Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
106  processExplicitCastExpr(Result, CStyleCast);
107  } else if (const auto *ExplicitCast =
108  Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
109  processExplicitCastExpr(Result, ExplicitCast);
110  } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
111  processLambdaExpr(Result, Lambda);
112  }
113 }
114 
115 void RedundantVoidArgCheck::processFunctionDecl(
116  const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
117  SourceLocation Start = Function->getLocStart();
118  if (Function->isThisDeclarationADefinition()) {
119  SourceLocation End;
120  if (Function->hasBody())
121  End = Function->getBody()->getLocStart().getLocWithOffset(-1);
122  else
123  End = Function->getLocEnd();
124  removeVoidArgumentTokens(Result, SourceRange(Start, End),
125  "function definition");
126  } else {
127  removeVoidArgumentTokens(Result, Function->getSourceRange(),
128  "function declaration");
129  }
130 }
131 
132 void RedundantVoidArgCheck::removeVoidArgumentTokens(
133  const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
134  StringRef GrammarLocation) {
135  CharSourceRange CharRange = Lexer::makeFileCharRange(
136  CharSourceRange::getTokenRange(Range), *Result.SourceManager,
137  Result.Context->getLangOpts());
138 
139  std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager,
140  Result.Context->getLangOpts())
141  .str();
142  Lexer PrototypeLexer(CharRange.getBegin(), Result.Context->getLangOpts(),
143  DeclText.data(), DeclText.data(),
144  DeclText.data() + DeclText.size());
145  enum TokenState {
146  NothingYet,
147  SawLeftParen,
148  SawVoid,
149  };
150  TokenState State = NothingYet;
151  Token VoidToken;
152  Token ProtoToken;
153  std::string Diagnostic =
154  ("redundant void argument list in " + GrammarLocation).str();
155 
156  while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
157  switch (State) {
158  case NothingYet:
159  if (ProtoToken.is(tok::TokenKind::l_paren)) {
160  State = SawLeftParen;
161  }
162  break;
163  case SawLeftParen:
164  if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
165  ProtoToken.getRawIdentifier() == "void") {
166  State = SawVoid;
167  VoidToken = ProtoToken;
168  } else {
169  State = NothingYet;
170  }
171  break;
172  case SawVoid:
173  State = NothingYet;
174  if (ProtoToken.is(tok::TokenKind::r_paren)) {
175  removeVoidToken(VoidToken, Diagnostic);
176  } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
177  State = SawLeftParen;
178  }
179  break;
180  }
181  }
182 
183  if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
184  removeVoidToken(VoidToken, Diagnostic);
185  }
186 }
187 
188 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
189  StringRef Diagnostic) {
190  SourceLocation VoidLoc(VoidToken.getLocation());
191  auto VoidRange =
192  CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3));
193  diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange);
194 }
195 
196 void RedundantVoidArgCheck::processTypedefDecl(
197  const MatchFinder::MatchResult &Result, const TypedefDecl *Typedef) {
198  if (protoTypeHasNoParms(Typedef->getUnderlyingType())) {
199  removeVoidArgumentTokens(Result, Typedef->getSourceRange(), "typedef");
200  }
201 }
202 
203 void RedundantVoidArgCheck::processFieldDecl(
204  const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
205  if (protoTypeHasNoParms(Member->getType())) {
206  removeVoidArgumentTokens(Result, Member->getSourceRange(),
207  "field declaration");
208  }
209 }
210 
211 void RedundantVoidArgCheck::processVarDecl(
212  const MatchFinder::MatchResult &Result, const VarDecl *Var) {
213  if (protoTypeHasNoParms(Var->getType())) {
214  SourceLocation Begin = Var->getLocStart();
215  if (Var->hasInit()) {
216  SourceLocation InitStart =
217  Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
218  .getLocWithOffset(-1);
219  removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
220  "variable declaration with initializer");
221  } else {
222  removeVoidArgumentTokens(Result, Var->getSourceRange(),
223  "variable declaration");
224  }
225  }
226 }
227 
228 void RedundantVoidArgCheck::processNamedCastExpr(
229  const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
230  if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
231  removeVoidArgumentTokens(
232  Result,
233  NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
234  "named cast");
235  }
236 }
237 
238 void RedundantVoidArgCheck::processExplicitCastExpr(
239  const MatchFinder::MatchResult &Result,
240  const ExplicitCastExpr *ExplicitCast) {
241  if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
242  removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
243  "cast expression");
244  }
245 }
246 
247 void RedundantVoidArgCheck::processLambdaExpr(
248  const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
249  if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
250  Lambda->hasExplicitParameters()) {
251  SourceLocation Begin =
252  Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
253  SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
254  removeVoidArgumentTokens(Result, SourceRange(Begin, End),
255  "lambda expression");
256  }
257 }
258 
259 } // namespace modernize
260 } // namespace tidy
261 } // namespace clang
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
static const char PointerType[]
CharSourceRange Range
SourceRange for the file name.
const NamedDecl * Result
Definition: USRFinder.cpp:121