Skip to content

Commit 94b1435

Browse files
5chmidtiHighCommander4
authored andcommitted
[clangd] allow extracting to variable for lambda expressions
Support for extracting lambda expressions, e.g. extracting a lambda from a callexpr (e.g. algorithms/ranges) to a named variable. Reviewed By: nridge Differential Revision: https://reviews.llvm.org/D141757
1 parent 64366d4 commit 94b1435

File tree

3 files changed

+351
-11
lines changed

3 files changed

+351
-11
lines changed

clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
#include "SourceCode.h"
1313
#include "refactor/Tweak.h"
1414
#include "clang/AST/ASTContext.h"
15+
#include "clang/AST/Decl.h"
16+
#include "clang/AST/DeclCXX.h"
1517
#include "clang/AST/Expr.h"
1618
#include "clang/AST/ExprCXX.h"
19+
#include "clang/AST/LambdaCapture.h"
1720
#include "clang/AST/OperationKinds.h"
1821
#include "clang/AST/RecursiveASTVisitor.h"
1922
#include "clang/AST/Stmt.h"
@@ -74,10 +77,52 @@ computeReferencedDecls(const clang::Expr *Expr) {
7477
public:
7578
std::vector<Decl *> ReferencedDecls;
7679
bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { // NOLINT
80+
// Stop the call operator of lambdas from being marked as a referenced
81+
// DeclRefExpr in immediately invoked lambdas.
82+
if (const auto *const Method =
83+
llvm::dyn_cast<CXXMethodDecl>(DeclRef->getDecl());
84+
Method != nullptr && Method->getParent()->isLambda()) {
85+
return true;
86+
}
7787
ReferencedDecls.push_back(DeclRef->getDecl());
7888
return true;
7989
}
90+
91+
// Local variables declared inside of the selected lambda cannot go out of
92+
// scope. The DeclRefExprs that are important are the variables captured,
93+
// the DeclRefExprs inside the initializers of init-capture variables,
94+
// variables mentioned in trailing return types, constraints and explicit
95+
// defaulted template parameters.
96+
bool TraverseLambdaExpr(LambdaExpr *LExpr) {
97+
for (const auto &[Capture, Initializer] :
98+
llvm::zip(LExpr->captures(), LExpr->capture_inits())) {
99+
TraverseLambdaCapture(LExpr, &Capture, Initializer);
100+
}
101+
102+
if (clang::Expr *const RequiresClause =
103+
LExpr->getTrailingRequiresClause()) {
104+
TraverseStmt(RequiresClause);
105+
}
106+
107+
for (auto *const TemplateParam : LExpr->getExplicitTemplateParameters())
108+
TraverseDecl(TemplateParam);
109+
110+
if (auto *const CallOperator = LExpr->getCallOperator()) {
111+
TraverseType(CallOperator->getDeclaredReturnType());
112+
113+
for (auto *const Param : CallOperator->parameters()) {
114+
TraverseParmVarDecl(Param);
115+
}
116+
117+
for (auto *const Attr : CallOperator->attrs()) {
118+
TraverseAttr(Attr);
119+
}
120+
}
121+
122+
return true;
123+
}
80124
};
125+
81126
FindDeclRefsVisitor Visitor;
82127
Visitor.TraverseStmt(const_cast<Stmt *>(cast<Stmt>(Expr)));
83128
return Visitor.ReferencedDecls;
@@ -152,10 +197,16 @@ const clang::Stmt *ExtractionContext::computeInsertionPoint() const {
152197
auto CanExtractOutside =
153198
[](const SelectionTree::Node *InsertionPoint) -> bool {
154199
if (const clang::Stmt *Stmt = InsertionPoint->ASTNode.get<clang::Stmt>()) {
155-
// Allow all expressions except LambdaExpr since we don't want to extract
156-
// from the captures/default arguments of a lambda
157-
if (isa<clang::Expr>(Stmt))
158-
return !isa<LambdaExpr>(Stmt);
200+
if (isa<clang::Expr>(Stmt)) {
201+
// Do not allow extraction from the initializer of a defaulted parameter
202+
// to a local variable (e.g. a function-local lambda).
203+
if (InsertionPoint->Parent->ASTNode.get<ParmVarDecl>() != nullptr) {
204+
return false;
205+
}
206+
207+
return true;
208+
}
209+
159210
// We don't yet allow extraction from switch/case stmt as we would need to
160211
// jump over the switch stmt even if there is a CompoundStmt inside the
161212
// switch. And there are other Stmts which we don't care about (e.g.
@@ -240,7 +291,7 @@ struct ParsedBinaryOperator {
240291
SelectedOperands.clear();
241292

242293
if (const BinaryOperator *Op =
243-
llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) {
294+
llvm::dyn_cast_or_null<BinaryOperator>(N.ASTNode.get<Expr>())) {
244295
Kind = Op->getOpcode();
245296
ExprLoc = Op->getExprLoc();
246297
SelectedOperands = N.Children;
@@ -255,7 +306,7 @@ struct ParsedBinaryOperator {
255306
Kind = BinaryOperator::getOverloadedOpcode(Op->getOperator());
256307
ExprLoc = Op->getExprLoc();
257308
// Not all children are args, there's also the callee (operator).
258-
for (const auto* Child : N.Children) {
309+
for (const auto *Child : N.Children) {
259310
const Expr *E = Child->ASTNode.get<Expr>();
260311
assert(E && "callee and args should be Exprs!");
261312
if (E == Op->getArg(0) || E == Op->getArg(1))
@@ -376,15 +427,15 @@ bool childExprIsStmt(const Stmt *Outer, const Expr *Inner) {
376427
if (llvm::isa<SwitchCase>(Outer))
377428
return true;
378429
// Control flow statements use condition etc, but not the body.
379-
if (const auto* WS = llvm::dyn_cast<WhileStmt>(Outer))
430+
if (const auto *WS = llvm::dyn_cast<WhileStmt>(Outer))
380431
return Inner == WS->getBody();
381-
if (const auto* DS = llvm::dyn_cast<DoStmt>(Outer))
432+
if (const auto *DS = llvm::dyn_cast<DoStmt>(Outer))
382433
return Inner == DS->getBody();
383-
if (const auto* FS = llvm::dyn_cast<ForStmt>(Outer))
434+
if (const auto *FS = llvm::dyn_cast<ForStmt>(Outer))
384435
return Inner == FS->getBody();
385-
if (const auto* FS = llvm::dyn_cast<CXXForRangeStmt>(Outer))
436+
if (const auto *FS = llvm::dyn_cast<CXXForRangeStmt>(Outer))
386437
return Inner == FS->getBody();
387-
if (const auto* IS = llvm::dyn_cast<IfStmt>(Outer))
438+
if (const auto *IS = llvm::dyn_cast<IfStmt>(Outer))
388439
return Inner == IS->getThen() || Inner == IS->getElse();
389440
// Assume all other cases may be actual expressions.
390441
// This includes the important case of subexpressions (where Outer is Expr).

0 commit comments

Comments
 (0)