|  | //===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "RedundantVoidArgCheck.h" | 
|  | #include "clang/Frontend/CompilerInstance.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang { | 
|  | namespace tidy { | 
|  | namespace modernize { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Determine if the given QualType is a nullary function or pointer to same. | 
|  | bool protoTypeHasNoParms(QualType QT) { | 
|  | if (auto PT = QT->getAs<PointerType>()) { | 
|  | QT = PT->getPointeeType(); | 
|  | } | 
|  | if (auto *MPT = QT->getAs<MemberPointerType>()) { | 
|  | QT = MPT->getPointeeType(); | 
|  | } | 
|  | if (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) { | 
|  | if (!getLangOpts().CPlusPlus) | 
|  | return; | 
|  |  | 
|  | Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), | 
|  | unless(isExternC())) | 
|  | .bind(FunctionId), | 
|  | this); | 
|  | Finder->addMatcher(typedefNameDecl().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) { | 
|  | if (Function->isThisDeclarationADefinition()) { | 
|  | const Stmt *Body = Function->getBody(); | 
|  | SourceLocation Start = Function->getLocStart(); | 
|  | SourceLocation End = | 
|  | Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd(); | 
|  | removeVoidArgumentTokens(Result, SourceRange(Start, End), | 
|  | "function definition"); | 
|  | } else { | 
|  | removeVoidArgumentTokens(Result, Function->getSourceRange(), | 
|  | "function declaration"); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 TokenState { | 
|  | NothingYet, | 
|  | SawLeftParen, | 
|  | SawVoid, | 
|  | }; | 
|  | TokenState State = NothingYet; | 
|  | Token VoidToken; | 
|  | Token ProtoToken; | 
|  | std::string Diagnostic = | 
|  | ("redundant void argument list in " + GrammarLocation).str(); | 
|  |  | 
|  | while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { | 
|  | switch (State) { | 
|  | case NothingYet: | 
|  | if (ProtoToken.is(tok::TokenKind::l_paren)) { | 
|  | State = SawLeftParen; | 
|  | } | 
|  | break; | 
|  | case SawLeftParen: | 
|  | if (ProtoToken.is(tok::TokenKind::raw_identifier) && | 
|  | ProtoToken.getRawIdentifier() == "void") { | 
|  | State = SawVoid; | 
|  | VoidToken = ProtoToken; | 
|  | } else { | 
|  | State = NothingYet; | 
|  | } | 
|  | break; | 
|  | case SawVoid: | 
|  | State = NothingYet; | 
|  | if (ProtoToken.is(tok::TokenKind::r_paren)) { | 
|  | removeVoidToken(VoidToken, Diagnostic); | 
|  | } else if (ProtoToken.is(tok::TokenKind::l_paren)) { | 
|  | State = SawLeftParen; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) { | 
|  | removeVoidToken(VoidToken, Diagnostic); | 
|  | } | 
|  | } | 
|  |  | 
|  | void RedundantVoidArgCheck::removeVoidToken(Token VoidToken, | 
|  | StringRef Diagnostic) { | 
|  | SourceLocation VoidLoc(VoidToken.getLocation()); | 
|  | auto VoidRange = | 
|  | CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3)); | 
|  | diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange); | 
|  | } | 
|  |  | 
|  | 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->getLocStart(); | 
|  | if (Var->hasInit()) { | 
|  | SourceLocation InitStart = | 
|  | Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart()) | 
|  | .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()) { | 
|  | SourceLocation Begin = | 
|  | Lambda->getIntroducerRange().getEnd().getLocWithOffset(1); | 
|  | SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1); | 
|  | removeVoidArgumentTokens(Result, SourceRange(Begin, End), | 
|  | "lambda expression"); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace modernize | 
|  | } // namespace tidy | 
|  | } // namespace clang |