|  | //===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "RedundantVoidArgCheck.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang::tidy::modernize { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Determine if the given QualType is a nullary function or pointer to same. | 
|  | bool protoTypeHasNoParms(QualType QT) { | 
|  | if (const auto *PT = QT->getAs<PointerType>()) | 
|  | QT = PT->getPointeeType(); | 
|  | if (auto *MPT = QT->getAs<MemberPointerType>()) | 
|  | QT = MPT->getPointeeType(); | 
|  | if (const auto *FP = QT->getAs<FunctionProtoType>()) | 
|  | return FP->getNumParams() == 0; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const char FunctionId[] = "function"; | 
|  | const char TypedefId[] = "typedef"; | 
|  | const char FieldId[] = "field"; | 
|  | const char VarId[] = "var"; | 
|  | const char NamedCastId[] = "named-cast"; | 
|  | const char CStyleCastId[] = "c-style-cast"; | 
|  | const char ExplicitCastId[] = "explicit-cast"; | 
|  | const char LambdaId[] = "lambda"; | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { | 
|  | Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), | 
|  | unless(isInstantiated()), unless(isExternC())) | 
|  | .bind(FunctionId), | 
|  | this); | 
|  | Finder->addMatcher(typedefNameDecl(unless(isImplicit())).bind(TypedefId), | 
|  | this); | 
|  | auto ParenFunctionType = parenType(innerType(functionType())); | 
|  | auto PointerToFunctionType = pointee(ParenFunctionType); | 
|  | auto FunctionOrMemberPointer = | 
|  | anyOf(hasType(pointerType(PointerToFunctionType)), | 
|  | hasType(memberPointerType(PointerToFunctionType))); | 
|  | Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this); | 
|  | Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this); | 
|  | auto CastDestinationIsFunction = | 
|  | hasDestinationType(pointsTo(ParenFunctionType)); | 
|  | Finder->addMatcher( | 
|  | cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this); | 
|  | Finder->addMatcher( | 
|  | cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); | 
|  | Finder->addMatcher( | 
|  | cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId), | 
|  | this); | 
|  | Finder->addMatcher( | 
|  | cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this); | 
|  | Finder->addMatcher(lambdaExpr().bind(LambdaId), this); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) { | 
|  | const BoundNodes &Nodes = Result.Nodes; | 
|  | if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) | 
|  | processFunctionDecl(Result, Function); | 
|  | else if (const auto *TypedefName = | 
|  | Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) | 
|  | processTypedefNameDecl(Result, TypedefName); | 
|  | else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) | 
|  | processFieldDecl(Result, Member); | 
|  | else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) | 
|  | processVarDecl(Result, Var); | 
|  | else if (const auto *NamedCast = | 
|  | Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) | 
|  | processNamedCastExpr(Result, NamedCast); | 
|  | else if (const auto *CStyleCast = | 
|  | Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) | 
|  | processExplicitCastExpr(Result, CStyleCast); | 
|  | else if (const auto *ExplicitCast = | 
|  | Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) | 
|  | processExplicitCastExpr(Result, ExplicitCast); | 
|  | else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) | 
|  | processLambdaExpr(Result, Lambda); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processFunctionDecl( | 
|  | const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { | 
|  | const auto *Method = dyn_cast<CXXMethodDecl>(Function); | 
|  | SourceLocation Start = Method && Method->getParent()->isLambda() | 
|  | ? Method->getBeginLoc() | 
|  | : Function->getLocation(); | 
|  | SourceLocation End = Function->getEndLoc(); | 
|  | if (Function->isThisDeclarationADefinition()) { | 
|  | if (const Stmt *Body = Function->getBody()) { | 
|  | End = Body->getBeginLoc(); | 
|  | if (End.isMacroID() && | 
|  | Result.SourceManager->isAtStartOfImmediateMacroExpansion(End)) | 
|  | End = Result.SourceManager->getExpansionLoc(End); | 
|  | End = End.getLocWithOffset(-1); | 
|  | } | 
|  | removeVoidArgumentTokens(Result, SourceRange(Start, End), | 
|  | "function definition"); | 
|  | } else | 
|  | removeVoidArgumentTokens(Result, SourceRange(Start, End), | 
|  | "function declaration"); | 
|  | } | 
|  |  | 
|  | bool isMacroIdentifier(const IdentifierTable &Idents, const Token &ProtoToken) { | 
|  | if (!ProtoToken.is(tok::TokenKind::raw_identifier)) | 
|  | return false; | 
|  |  | 
|  | IdentifierTable::iterator It = Idents.find(ProtoToken.getRawIdentifier()); | 
|  | if (It == Idents.end()) | 
|  | return false; | 
|  |  | 
|  | return It->second->hadMacroDefinition(); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::removeVoidArgumentTokens( | 
|  | const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range, | 
|  | StringRef GrammarLocation) { | 
|  | CharSourceRange CharRange = | 
|  | Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range), | 
|  | *Result.SourceManager, getLangOpts()); | 
|  |  | 
|  | std::string DeclText = | 
|  | Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts()) | 
|  | .str(); | 
|  | Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(), | 
|  | DeclText.data(), DeclText.data() + DeclText.size()); | 
|  | enum class TokenState { | 
|  | Start, | 
|  | MacroId, | 
|  | MacroLeftParen, | 
|  | MacroArguments, | 
|  | LeftParen, | 
|  | Void, | 
|  | }; | 
|  | TokenState State = TokenState::Start; | 
|  | Token VoidToken; | 
|  | Token ProtoToken; | 
|  | const IdentifierTable &Idents = Result.Context->Idents; | 
|  | int MacroLevel = 0; | 
|  | std::string Diagnostic = | 
|  | ("redundant void argument list in " + GrammarLocation).str(); | 
|  |  | 
|  | while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { | 
|  | switch (State) { | 
|  | case TokenState::Start: | 
|  | if (ProtoToken.is(tok::TokenKind::l_paren)) | 
|  | State = TokenState::LeftParen; | 
|  | else if (isMacroIdentifier(Idents, ProtoToken)) | 
|  | State = TokenState::MacroId; | 
|  | break; | 
|  | case TokenState::MacroId: | 
|  | if (ProtoToken.is(tok::TokenKind::l_paren)) | 
|  | State = TokenState::MacroLeftParen; | 
|  | else | 
|  | State = TokenState::Start; | 
|  | break; | 
|  | case TokenState::MacroLeftParen: | 
|  | ++MacroLevel; | 
|  | if (ProtoToken.is(tok::TokenKind::raw_identifier)) { | 
|  | if (isMacroIdentifier(Idents, ProtoToken)) | 
|  | State = TokenState::MacroId; | 
|  | else | 
|  | State = TokenState::MacroArguments; | 
|  | } else if (ProtoToken.is(tok::TokenKind::r_paren)) { | 
|  | --MacroLevel; | 
|  | if (MacroLevel == 0) | 
|  | State = TokenState::Start; | 
|  | else | 
|  | State = TokenState::MacroId; | 
|  | } else | 
|  | State = TokenState::MacroArguments; | 
|  | break; | 
|  | case TokenState::MacroArguments: | 
|  | if (isMacroIdentifier(Idents, ProtoToken)) | 
|  | State = TokenState::MacroLeftParen; | 
|  | else if (ProtoToken.is(tok::TokenKind::r_paren)) { | 
|  | --MacroLevel; | 
|  | if (MacroLevel == 0) | 
|  | State = TokenState::Start; | 
|  | } | 
|  | break; | 
|  | case TokenState::LeftParen: | 
|  | if (ProtoToken.is(tok::TokenKind::raw_identifier)) { | 
|  | if (isMacroIdentifier(Idents, ProtoToken)) | 
|  | State = TokenState::MacroId; | 
|  | else if (ProtoToken.getRawIdentifier() == "void") { | 
|  | State = TokenState::Void; | 
|  | VoidToken = ProtoToken; | 
|  | } | 
|  | } else if (ProtoToken.is(tok::TokenKind::l_paren)) | 
|  | State = TokenState::LeftParen; | 
|  | else | 
|  | State = TokenState::Start; | 
|  | break; | 
|  | case TokenState::Void: | 
|  | State = TokenState::Start; | 
|  | if (ProtoToken.is(tok::TokenKind::r_paren)) | 
|  | removeVoidToken(VoidToken, Diagnostic); | 
|  | else if (ProtoToken.is(tok::TokenKind::l_paren)) | 
|  | State = TokenState::LeftParen; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (State == TokenState::Void && ProtoToken.is(tok::TokenKind::r_paren)) | 
|  | removeVoidToken(VoidToken, Diagnostic); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::removeVoidToken(Token VoidToken, | 
|  | StringRef Diagnostic) { | 
|  | SourceLocation VoidLoc = VoidToken.getLocation(); | 
|  | diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidLoc); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processTypedefNameDecl( | 
|  | const MatchFinder::MatchResult &Result, | 
|  | const TypedefNameDecl *TypedefName) { | 
|  | if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) | 
|  | removeVoidArgumentTokens(Result, TypedefName->getSourceRange(), | 
|  | isa<TypedefDecl>(TypedefName) ? "typedef" | 
|  | : "type alias"); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processFieldDecl( | 
|  | const MatchFinder::MatchResult &Result, const FieldDecl *Member) { | 
|  | if (protoTypeHasNoParms(Member->getType())) | 
|  | removeVoidArgumentTokens(Result, Member->getSourceRange(), | 
|  | "field declaration"); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processVarDecl( | 
|  | const MatchFinder::MatchResult &Result, const VarDecl *Var) { | 
|  | if (protoTypeHasNoParms(Var->getType())) { | 
|  | SourceLocation Begin = Var->getBeginLoc(); | 
|  | if (Var->hasInit()) { | 
|  | SourceLocation InitStart = | 
|  | Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc()) | 
|  | .getLocWithOffset(-1); | 
|  | removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart), | 
|  | "variable declaration with initializer"); | 
|  | } else | 
|  | removeVoidArgumentTokens(Result, Var->getSourceRange(), | 
|  | "variable declaration"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processNamedCastExpr( | 
|  | const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) { | 
|  | if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) | 
|  | removeVoidArgumentTokens( | 
|  | Result, | 
|  | NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(), | 
|  | "named cast"); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processExplicitCastExpr( | 
|  | const MatchFinder::MatchResult &Result, | 
|  | const ExplicitCastExpr *ExplicitCast) { | 
|  | if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) | 
|  | removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(), | 
|  | "cast expression"); | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::processLambdaExpr( | 
|  | const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) { | 
|  | if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 && | 
|  | Lambda->hasExplicitParameters()) { | 
|  | SourceManager *SM = Result.SourceManager; | 
|  | TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc(); | 
|  | removeVoidArgumentTokens(Result, | 
|  | {SM->getSpellingLoc(TL.getBeginLoc()), | 
|  | SM->getSpellingLoc(TL.getEndLoc())}, | 
|  | "lambda expression"); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace clang::tidy::modernize |