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