clang-tools  10.0.0git
RedundantVoidArgCheck.cpp
Go to the documentation of this file.
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 
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.
22 bool protoTypeHasNoParms(QualType QT) {
23  if (auto PT = QT->getAs<PointerType>()) {
24  QT = PT->getPointeeType();
25  }
26  if (auto *MPT = QT->getAs<MemberPointerType>()) {
27  QT = MPT->getPointeeType();
28  }
29  if (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 
46 void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
47  if (!getLangOpts().CPlusPlus)
48  return;
49 
50  Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
51  unless(isInstantiated()), unless(isExternC()))
52  .bind(FunctionId),
53  this);
54  Finder->addMatcher(typedefNameDecl().bind(TypedefId), 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(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
61  Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
62  auto CastDestinationIsFunction =
63  hasDestinationType(pointsTo(ParenFunctionType));
64  Finder->addMatcher(
65  cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
66  Finder->addMatcher(
67  cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
68  Finder->addMatcher(
69  cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
70  this);
71  Finder->addMatcher(
72  cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
73  Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
74 }
75 
76 void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
77  const BoundNodes &Nodes = Result.Nodes;
78  if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
79  processFunctionDecl(Result, Function);
80  } else if (const auto *TypedefName =
81  Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
82  processTypedefNameDecl(Result, TypedefName);
83  } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
84  processFieldDecl(Result, Member);
85  } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
86  processVarDecl(Result, Var);
87  } else if (const auto *NamedCast =
88  Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
89  processNamedCastExpr(Result, NamedCast);
90  } else if (const auto *CStyleCast =
91  Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
92  processExplicitCastExpr(Result, CStyleCast);
93  } else if (const auto *ExplicitCast =
94  Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
95  processExplicitCastExpr(Result, ExplicitCast);
96  } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
97  processLambdaExpr(Result, Lambda);
98  }
99 }
100 
101 void RedundantVoidArgCheck::processFunctionDecl(
102  const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
103  if (Function->isThisDeclarationADefinition()) {
104  SourceLocation Start = Function->getBeginLoc();
105  SourceLocation End = Function->getEndLoc();
106  if (const Stmt *Body = Function->getBody()) {
107  End = Body->getBeginLoc();
108  if (End.isMacroID() &&
109  Result.SourceManager->isAtStartOfImmediateMacroExpansion(End))
110  End = Result.SourceManager->getExpansionLoc(End);
111  End = End.getLocWithOffset(-1);
112  }
113  removeVoidArgumentTokens(Result, SourceRange(Start, End),
114  "function definition");
115  } else {
116  removeVoidArgumentTokens(Result, Function->getSourceRange(),
117  "function declaration");
118  }
119 }
120 
121 void RedundantVoidArgCheck::removeVoidArgumentTokens(
122  const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
123  StringRef GrammarLocation) {
124  CharSourceRange CharRange =
125  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
126  *Result.SourceManager, getLangOpts());
127 
128  std::string DeclText =
129  Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
130  .str();
131  Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
132  DeclText.data(), DeclText.data() + DeclText.size());
133  enum TokenState {
134  NothingYet,
135  SawLeftParen,
136  SawVoid,
137  };
138  TokenState State = NothingYet;
139  Token VoidToken;
140  Token ProtoToken;
141  std::string Diagnostic =
142  ("redundant void argument list in " + GrammarLocation).str();
143 
144  while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
145  switch (State) {
146  case NothingYet:
147  if (ProtoToken.is(tok::TokenKind::l_paren)) {
148  State = SawLeftParen;
149  }
150  break;
151  case SawLeftParen:
152  if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
153  ProtoToken.getRawIdentifier() == "void") {
154  State = SawVoid;
155  VoidToken = ProtoToken;
156  } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
157  State = SawLeftParen;
158  } else {
159  State = NothingYet;
160  }
161  break;
162  case SawVoid:
163  State = NothingYet;
164  if (ProtoToken.is(tok::TokenKind::r_paren)) {
165  removeVoidToken(VoidToken, Diagnostic);
166  } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
167  State = SawLeftParen;
168  }
169  break;
170  }
171  }
172 
173  if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
174  removeVoidToken(VoidToken, Diagnostic);
175  }
176 }
177 
178 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
179  StringRef Diagnostic) {
180  SourceLocation VoidLoc = VoidToken.getLocation();
181  diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
182 }
183 
184 void RedundantVoidArgCheck::processTypedefNameDecl(
185  const MatchFinder::MatchResult &Result,
186  const TypedefNameDecl *TypedefName) {
187  if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
188  removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
189  isa<TypedefDecl>(TypedefName) ? "typedef"
190  : "type alias");
191  }
192 }
193 
194 void RedundantVoidArgCheck::processFieldDecl(
195  const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
196  if (protoTypeHasNoParms(Member->getType())) {
197  removeVoidArgumentTokens(Result, Member->getSourceRange(),
198  "field declaration");
199  }
200 }
201 
202 void RedundantVoidArgCheck::processVarDecl(
203  const MatchFinder::MatchResult &Result, const VarDecl *Var) {
204  if (protoTypeHasNoParms(Var->getType())) {
205  SourceLocation Begin = Var->getBeginLoc();
206  if (Var->hasInit()) {
207  SourceLocation InitStart =
208  Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
209  .getLocWithOffset(-1);
210  removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
211  "variable declaration with initializer");
212  } else {
213  removeVoidArgumentTokens(Result, Var->getSourceRange(),
214  "variable declaration");
215  }
216  }
217 }
218 
219 void RedundantVoidArgCheck::processNamedCastExpr(
220  const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
221  if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
222  removeVoidArgumentTokens(
223  Result,
224  NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
225  "named cast");
226  }
227 }
228 
229 void RedundantVoidArgCheck::processExplicitCastExpr(
230  const MatchFinder::MatchResult &Result,
231  const ExplicitCastExpr *ExplicitCast) {
232  if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
233  removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
234  "cast expression");
235  }
236 }
237 
238 void RedundantVoidArgCheck::processLambdaExpr(
239  const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
240  if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
241  Lambda->hasExplicitParameters()) {
242  SourceManager *SM = Result.SourceManager;
243  TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
244  removeVoidArgumentTokens(Result,
245  {SM->getSpellingLoc(TL.getBeginLoc()),
246  SM->getSpellingLoc(TL.getEndLoc())},
247  "lambda expression");
248  }
249 }
250 
251 } // namespace modernize
252 } // namespace tidy
253 } // namespace clang
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
Definition: SourceCode.cpp:227
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.