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