clang-tools  15.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 (const auto *PT = QT->getAs<PointerType>())
24  QT = PT->getPointeeType();
25  if (auto *MPT = QT->getAs<MemberPointerType>())
26  QT = MPT->getPointeeType();
27  if (const auto *FP = QT->getAs<FunctionProtoType>())
28  return FP->getNumParams() == 0;
29  return false;
30 }
31 
32 const char FunctionId[] = "function";
33 const char TypedefId[] = "typedef";
34 const char FieldId[] = "field";
35 const char VarId[] = "var";
36 const char NamedCastId[] = "named-cast";
37 const char CStyleCastId[] = "c-style-cast";
38 const char ExplicitCastId[] = "explicit-cast";
39 const char LambdaId[] = "lambda";
40 
41 } // namespace
42 
43 void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
44  Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
45  unless(isInstantiated()), unless(isExternC()))
46  .bind(FunctionId),
47  this);
48  Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId),
49  this);
50  auto ParenFunctionType = parenType(innerType(functionType()));
51  auto PointerToFunctionType = pointee(ParenFunctionType);
52  auto FunctionOrMemberPointer =
53  anyOf(hasType(pointerType(PointerToFunctionType)),
54  hasType(memberPointerType(PointerToFunctionType)));
55  Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
56  Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
57  auto CastDestinationIsFunction =
58  hasDestinationType(pointsTo(ParenFunctionType));
59  Finder->addMatcher(
60  cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
61  Finder->addMatcher(
62  cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
63  Finder->addMatcher(
64  cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
65  this);
66  Finder->addMatcher(
67  cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
68  Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
69 }
70 
71 void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
72  const BoundNodes &Nodes = Result.Nodes;
73  if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId))
74  processFunctionDecl(Result, Function);
75  else if (const auto *TypedefName =
76  Nodes.getNodeAs<TypedefNameDecl>(TypedefId))
77  processTypedefNameDecl(Result, TypedefName);
78  else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId))
79  processFieldDecl(Result, Member);
80  else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId))
81  processVarDecl(Result, Var);
82  else if (const auto *NamedCast =
83  Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId))
84  processNamedCastExpr(Result, NamedCast);
85  else if (const auto *CStyleCast =
86  Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId))
87  processExplicitCastExpr(Result, CStyleCast);
88  else if (const auto *ExplicitCast =
89  Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId))
90  processExplicitCastExpr(Result, ExplicitCast);
91  else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId))
92  processLambdaExpr(Result, Lambda);
93 }
94 
95 void RedundantVoidArgCheck::processFunctionDecl(
96  const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
97  const auto *Method = dyn_cast<CXXMethodDecl>(Function);
98  SourceLocation Start = Method && Method->getParent()->isLambda()
99  ? Method->getBeginLoc()
100  : Function->getLocation();
101  SourceLocation End = Function->getEndLoc();
102  if (Function->isThisDeclarationADefinition()) {
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, SourceRange(Start, End),
114  "function declaration");
115 }
116 
117 bool isMacroIdentifier(const IdentifierTable &Idents, const Token &ProtoToken) {
118  if (!ProtoToken.is(tok::TokenKind::raw_identifier))
119  return false;
120 
121  IdentifierTable::iterator It = Idents.find(ProtoToken.getRawIdentifier());
122  if (It == Idents.end())
123  return false;
124 
125  return It->second->hadMacroDefinition();
126 }
127 
128 void RedundantVoidArgCheck::removeVoidArgumentTokens(
129  const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
130  StringRef GrammarLocation) {
131  CharSourceRange CharRange =
132  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
133  *Result.SourceManager, getLangOpts());
134 
135  std::string DeclText =
136  Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
137  .str();
138  Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
139  DeclText.data(), DeclText.data() + DeclText.size());
140  enum class TokenState {
141  Start,
142  MacroId,
143  MacroLeftParen,
144  MacroArguments,
145  LeftParen,
146  Void,
147  };
148  TokenState State = TokenState::Start;
149  Token VoidToken;
150  Token ProtoToken;
151  const IdentifierTable &Idents = Result.Context->Idents;
152  int MacroLevel = 0;
153  std::string Diagnostic =
154  ("redundant void argument list in " + GrammarLocation).str();
155 
156  while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
157  switch (State) {
158  case TokenState::Start:
159  if (ProtoToken.is(tok::TokenKind::l_paren))
160  State = TokenState::LeftParen;
161  else if (isMacroIdentifier(Idents, ProtoToken))
162  State = TokenState::MacroId;
163  break;
164  case TokenState::MacroId:
165  if (ProtoToken.is(tok::TokenKind::l_paren))
166  State = TokenState::MacroLeftParen;
167  else
168  State = TokenState::Start;
169  break;
170  case TokenState::MacroLeftParen:
171  ++MacroLevel;
172  if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
173  if (isMacroIdentifier(Idents, ProtoToken))
174  State = TokenState::MacroId;
175  else
176  State = TokenState::MacroArguments;
177  } else if (ProtoToken.is(tok::TokenKind::r_paren)) {
178  --MacroLevel;
179  if (MacroLevel == 0)
180  State = TokenState::Start;
181  else
182  State = TokenState::MacroId;
183  } else
184  State = TokenState::MacroArguments;
185  break;
186  case TokenState::MacroArguments:
187  if (isMacroIdentifier(Idents, ProtoToken))
188  State = TokenState::MacroLeftParen;
189  else if (ProtoToken.is(tok::TokenKind::r_paren)) {
190  --MacroLevel;
191  if (MacroLevel == 0)
192  State = TokenState::Start;
193  }
194  break;
195  case TokenState::LeftParen:
196  if (ProtoToken.is(tok::TokenKind::raw_identifier)) {
197  if (isMacroIdentifier(Idents, ProtoToken))
198  State = TokenState::MacroId;
199  else if (ProtoToken.getRawIdentifier() == "void") {
200  State = TokenState::Void;
201  VoidToken = ProtoToken;
202  }
203  } else if (ProtoToken.is(tok::TokenKind::l_paren))
204  State = TokenState::LeftParen;
205  else
206  State = TokenState::Start;
207  break;
208  case TokenState::Void:
209  State = TokenState::Start;
210  if (ProtoToken.is(tok::TokenKind::r_paren))
211  removeVoidToken(VoidToken, Diagnostic);
212  else if (ProtoToken.is(tok::TokenKind::l_paren))
213  State = TokenState::LeftParen;
214  break;
215  }
216  }
217 
218  if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren))
219  removeVoidToken(VoidToken, Diagnostic);
220 }
221 
222 void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
223  StringRef Diagnostic) {
224  SourceLocation VoidLoc = VoidToken.getLocation();
225  diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc);
226 }
227 
228 void RedundantVoidArgCheck::processTypedefNameDecl(
229  const MatchFinder::MatchResult &Result,
230  const TypedefNameDecl *TypedefName) {
231  if (protoTypeHasNoParms(TypedefName->getUnderlyingType()))
232  removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
233  isa<TypedefDecl>(TypedefName) ? "typedef"
234  : "type alias");
235 }
236 
237 void RedundantVoidArgCheck::processFieldDecl(
238  const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
239  if (protoTypeHasNoParms(Member->getType()))
240  removeVoidArgumentTokens(Result, Member->getSourceRange(),
241  "field declaration");
242 }
243 
244 void RedundantVoidArgCheck::processVarDecl(
245  const MatchFinder::MatchResult &Result, const VarDecl *Var) {
246  if (protoTypeHasNoParms(Var->getType())) {
247  SourceLocation Begin = Var->getBeginLoc();
248  if (Var->hasInit()) {
249  SourceLocation InitStart =
250  Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc())
251  .getLocWithOffset(-1);
252  removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
253  "variable declaration with initializer");
254  } else
255  removeVoidArgumentTokens(Result, Var->getSourceRange(),
256  "variable declaration");
257  }
258 }
259 
260 void RedundantVoidArgCheck::processNamedCastExpr(
261  const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
262  if (protoTypeHasNoParms(NamedCast->getTypeAsWritten()))
263  removeVoidArgumentTokens(
264  Result,
265  NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
266  "named cast");
267 }
268 
269 void RedundantVoidArgCheck::processExplicitCastExpr(
270  const MatchFinder::MatchResult &Result,
271  const ExplicitCastExpr *ExplicitCast) {
272  if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten()))
273  removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
274  "cast expression");
275 }
276 
277 void RedundantVoidArgCheck::processLambdaExpr(
278  const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
279  if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
280  Lambda->hasExplicitParameters()) {
281  SourceManager *SM = Result.SourceManager;
282  TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc();
283  removeVoidArgumentTokens(Result,
284  {SM->getSpellingLoc(TL.getBeginLoc()),
285  SM->getSpellingLoc(TL.getEndLoc())},
286  "lambda expression");
287  }
288 }
289 
290 } // namespace modernize
291 } // namespace tidy
292 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::check
bool check(llvm::StringRef File, llvm::Optional< Range > LineRange, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts, bool EnableCodeCompletion)
Definition: Check.cpp:273
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:112
clang::tidy::modernize::isMacroIdentifier
bool isMacroIdentifier(const IdentifierTable &Idents, const Token &ProtoToken)
Definition: RedundantVoidArgCheck.cpp:117
RedundantVoidArgCheck.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
Diagnostic
DiagnosticCallback Diagnostic
Definition: ConfigCompile.cpp:100
clang::tidy::modernize::TypedefName
static constexpr llvm::StringLiteral TypedefName
Definition: UseUsingCheck.cpp:21
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::doc::FieldId
FieldId
Definition: BitcodeWriter.h:130