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