brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.8 KiB · a58c041 Raw
139 lines · cpp
1//===----------------------------------------------------------------------===//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 "RedundantStrcatCallsCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include <deque>13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::abseil {17 18// TODO: Features to add to the check:19//  - Make it work if num_args > 26.20//  - Remove empty literal string arguments.21//  - Collapse consecutive literal string arguments into one (remove the ,).22//  - Replace StrCat(a + b)  ->  StrCat(a, b)  if a or b are strings.23//  - Make it work in macros if the outer and inner StrCats are both in the24//    argument.25 26void RedundantStrcatCallsCheck::registerMatchers(MatchFinder *Finder) {27  const auto CallToStrcat =28      callExpr(callee(functionDecl(hasName("::absl::StrCat"))));29  const auto CallToStrappend =30      callExpr(callee(functionDecl(hasName("::absl::StrAppend"))));31  // Do not match StrCat() calls that are descendants of other StrCat calls.32  // Those are handled on the ancestor call.33  const auto CallToEither = callExpr(34      callee(functionDecl(hasAnyName("::absl::StrCat", "::absl::StrAppend"))));35  Finder->addMatcher(36      callExpr(CallToStrcat, unless(hasAncestor(CallToEither))).bind("StrCat"),37      this);38  Finder->addMatcher(CallToStrappend.bind("StrAppend"), this);39}40 41namespace {42 43struct StrCatCheckResult {44  int NumCalls = 0;45  std::vector<FixItHint> Hints;46};47 48} // namespace49 50static void removeCallLeaveArgs(const CallExpr *Call,51                                StrCatCheckResult *CheckResult) {52  if (Call->getNumArgs() == 0)53    return;54  // Remove 'Foo('55  CheckResult->Hints.push_back(56      FixItHint::CreateRemoval(CharSourceRange::getCharRange(57          Call->getBeginLoc(), Call->getArg(0)->getBeginLoc())));58  // Remove the ')'59  CheckResult->Hints.push_back(60      FixItHint::CreateRemoval(CharSourceRange::getCharRange(61          Call->getRParenLoc(), Call->getEndLoc().getLocWithOffset(1))));62}63 64static const clang::CallExpr *65processArgument(const Expr *Arg, const MatchFinder::MatchResult &Result,66                StrCatCheckResult *CheckResult) {67  const auto IsAlphanum = hasDeclaration(cxxMethodDecl(hasName("AlphaNum")));68  static const auto *const Strcat = new auto(hasName("::absl::StrCat"));69  const auto IsStrcat = cxxBindTemporaryExpr(70      has(callExpr(callee(functionDecl(*Strcat))).bind("StrCat")));71  if (const auto *SubStrcatCall = selectFirst<const CallExpr>(72          "StrCat",73          match(stmt(traverse(TK_AsIs,74                              anyOf(cxxConstructExpr(IsAlphanum,75                                                     hasArgument(0, IsStrcat)),76                                    IsStrcat))),77                *Arg->IgnoreParenImpCasts(), *Result.Context))) {78    removeCallLeaveArgs(SubStrcatCall, CheckResult);79    return SubStrcatCall;80  }81  return nullptr;82}83 84static StrCatCheckResult processCall(const CallExpr *RootCall, bool IsAppend,85                                     const MatchFinder::MatchResult &Result) {86  StrCatCheckResult CheckResult;87  std::deque<const CallExpr *> CallsToProcess = {RootCall};88 89  while (!CallsToProcess.empty()) {90    ++CheckResult.NumCalls;91 92    const CallExpr *CallExpr = CallsToProcess.front();93    CallsToProcess.pop_front();94 95    int StartArg = CallExpr == RootCall && IsAppend;96    for (const auto *Arg : CallExpr->arguments()) {97      if (StartArg-- > 0)98        continue;99      if (const clang::CallExpr *Sub =100              processArgument(Arg, Result, &CheckResult)) {101        CallsToProcess.push_back(Sub);102      }103    }104  }105  return CheckResult;106}107 108void RedundantStrcatCallsCheck::check(const MatchFinder::MatchResult &Result) {109  bool IsAppend = false;110 111  const CallExpr *RootCall = nullptr;112  if ((RootCall = Result.Nodes.getNodeAs<CallExpr>("StrCat")))113    IsAppend = false;114  else if ((RootCall = Result.Nodes.getNodeAs<CallExpr>("StrAppend")))115    IsAppend = true;116  else117    return;118 119  if (RootCall->getBeginLoc().isMacroID()) {120    // Ignore calls within macros.121    // In many cases the outer StrCat part of the macro and the inner StrCat is122    // a macro argument. Removing the inner StrCat() converts one macro123    // argument into many.124    return;125  }126 127  const StrCatCheckResult CheckResult = processCall(RootCall, IsAppend, Result);128  if (CheckResult.NumCalls == 1) {129    // Just one call, so nothing to fix.130    return;131  }132 133  diag(RootCall->getBeginLoc(),134       "multiple calls to 'absl::StrCat' can be flattened into a single call")135      << CheckResult.Hints;136}137 138} // namespace clang::tidy::abseil139