| //===- 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 |