//===--- MisleadingCaptureDefaultByValueCheck.cpp - clang-tidy-------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MisleadingCaptureDefaultByValueCheck.h" #include "../utils/LexerUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/Support/raw_ostream.h" #include using namespace clang::ast_matchers; namespace clang::tidy::cppcoreguidelines { MisleadingCaptureDefaultByValueCheck::MisleadingCaptureDefaultByValueCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void MisleadingCaptureDefaultByValueCheck::registerMatchers( MatchFinder *Finder) { Finder->addMatcher(lambdaExpr(hasAnyCapture(capturesThis())).bind("lambda"), this); } static SourceLocation findDefaultCaptureEnd(const LambdaExpr *Lambda, ASTContext &Context) { for (const LambdaCapture &Capture : Lambda->explicit_captures()) { if (Capture.isExplicit()) { if (Capture.getCaptureKind() == LCK_ByRef) { const SourceManager &SourceMgr = Context.getSourceManager(); SourceLocation AddressofLoc = utils::lexer::findPreviousTokenKind( Capture.getLocation(), SourceMgr, Context.getLangOpts(), tok::amp); return AddressofLoc; } return Capture.getLocation(); } } return Lambda->getIntroducerRange().getEnd(); } static std::string createReplacementText(const LambdaExpr *Lambda) { std::string Replacement; llvm::raw_string_ostream Stream(Replacement); auto AppendName = [&](llvm::StringRef Name) { if (!Replacement.empty()) { Stream << ", "; } if (Lambda->getCaptureDefault() == LCD_ByRef && Name != "this") { Stream << "&" << Name; } else { Stream << Name; } }; for (const LambdaCapture &Capture : Lambda->implicit_captures()) { assert(Capture.isImplicit()); if (Capture.capturesVariable() && Capture.isImplicit()) { AppendName(Capture.getCapturedVar()->getName()); } else if (Capture.capturesThis()) { AppendName("this"); } } if (!Replacement.empty() && !Lambda->explicit_captures().empty()) { // Add back separator if we are adding explicit capture variables. Stream << ", "; } return Replacement; } void MisleadingCaptureDefaultByValueCheck::check( const MatchFinder::MatchResult &Result) { const auto *Lambda = Result.Nodes.getNodeAs("lambda"); if (!Lambda) return; if (Lambda->getCaptureDefault() == LCD_ByCopy) { bool IsThisImplicitlyCaptured = std::any_of( Lambda->implicit_capture_begin(), Lambda->implicit_capture_end(), [](const LambdaCapture &Capture) { return Capture.capturesThis(); }); auto Diag = diag(Lambda->getCaptureDefaultLoc(), "lambdas that %select{|implicitly }0capture 'this' " "should not specify a by-value capture default") << IsThisImplicitlyCaptured; std::string ReplacementText = createReplacementText(Lambda); SourceLocation DefaultCaptureEnd = findDefaultCaptureEnd(Lambda, *Result.Context); Diag << FixItHint::CreateReplacement( CharSourceRange::getCharRange(Lambda->getCaptureDefaultLoc(), DefaultCaptureEnd), ReplacementText); } } } // namespace clang::tidy::cppcoreguidelines