brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · aa2db21 Raw
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