brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · 366bd12 Raw
103 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 "MisleadingCaptureDefaultByValueCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "llvm/Support/raw_ostream.h"14 15#include <algorithm>16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::cppcoreguidelines {20 21MisleadingCaptureDefaultByValueCheck::MisleadingCaptureDefaultByValueCheck(22    StringRef Name, ClangTidyContext *Context)23    : ClangTidyCheck(Name, Context) {}24 25void MisleadingCaptureDefaultByValueCheck::registerMatchers(26    MatchFinder *Finder) {27  Finder->addMatcher(lambdaExpr(hasAnyCapture(capturesThis())).bind("lambda"),28                     this);29}30 31static SourceLocation findDefaultCaptureEnd(const LambdaExpr *Lambda,32                                            ASTContext &Context) {33  for (const LambdaCapture &Capture : Lambda->explicit_captures()) {34    if (Capture.isExplicit()) {35      if (Capture.getCaptureKind() == LCK_ByRef) {36        const SourceManager &SourceMgr = Context.getSourceManager();37        SourceLocation AddressofLoc = utils::lexer::findPreviousTokenKind(38            Capture.getLocation(), SourceMgr, Context.getLangOpts(), tok::amp);39        return AddressofLoc;40      }41      return Capture.getLocation();42    }43  }44  return Lambda->getIntroducerRange().getEnd();45}46 47static std::string createReplacementText(const LambdaExpr *Lambda) {48  std::string Replacement;49  llvm::raw_string_ostream Stream(Replacement);50 51  auto AppendName = [&](llvm::StringRef Name) {52    if (!Replacement.empty()) {53      Stream << ", ";54    }55    if (Lambda->getCaptureDefault() == LCD_ByRef && Name != "this") {56      Stream << "&" << Name;57    } else {58      Stream << Name;59    }60  };61 62  for (const LambdaCapture &Capture : Lambda->implicit_captures()) {63    assert(Capture.isImplicit());64    if (Capture.capturesVariable() && Capture.isImplicit()) {65      AppendName(Capture.getCapturedVar()->getName());66    } else if (Capture.capturesThis()) {67      AppendName("this");68    }69  }70  if (!Replacement.empty() && !Lambda->explicit_captures().empty()) {71    // Add back separator if we are adding explicit capture variables.72    Stream << ", ";73  }74  return Replacement;75}76 77void MisleadingCaptureDefaultByValueCheck::check(78    const MatchFinder::MatchResult &Result) {79  const auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");80  if (!Lambda)81    return;82 83  if (Lambda->getCaptureDefault() == LCD_ByCopy) {84    const bool IsThisImplicitlyCaptured = std::any_of(85        Lambda->implicit_capture_begin(), Lambda->implicit_capture_end(),86        [](const LambdaCapture &Capture) { return Capture.capturesThis(); });87    auto Diag = diag(Lambda->getCaptureDefaultLoc(),88                     "lambdas that %select{|implicitly }0capture 'this' "89                     "should not specify a by-value capture default")90                << IsThisImplicitlyCaptured;91 92    const std::string ReplacementText = createReplacementText(Lambda);93    const SourceLocation DefaultCaptureEnd =94        findDefaultCaptureEnd(Lambda, *Result.Context);95    Diag << FixItHint::CreateReplacement(96        CharSourceRange::getCharRange(Lambda->getCaptureDefaultLoc(),97                                      DefaultCaptureEnd),98        ReplacementText);99  }100}101 102} // namespace clang::tidy::cppcoreguidelines103