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