RedundantVoidArgCheck.cpp 11 KB

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