clang-tools 24.0.0git
ExtractFunction.cpp
Go to the documentation of this file.
1//===--- ExtractFunction.cpp -------------------------------------*- C++-*-===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Extracts statements to a new function and replaces the statements with a
10// call to the new function.
11// Before:
12// void f(int a) {
13// [[if(a < 5)
14// a = 5;]]
15// }
16// After:
17// void extracted(int &a) {
18// if(a < 5)
19// a = 5;
20// }
21// void f(int a) {
22// extracted(a);
23// }
24//
25// - Only extract statements
26// - Extracts from non-templated free functions only.
27// - Parameters that are never (conservatively) mutated in the extracted
28// code become const references.
29// - Always passed by l-value reference
30// - Void return type
31// - Cannot extract declarations that will be needed in the original function
32// after extraction.
33// - Checks for broken control flow (break/continue without loop/switch)
34//
35// 1. ExtractFunction is the tweak subclass
36// - Prepare does basic analysis of the selection and is therefore fast.
37// Successful prepare doesn't always mean we can apply the tweak.
38// - Apply does a more detailed analysis and can be slower. In case of
39// failure, we let the user know that we are unable to perform extraction.
40// 2. ExtractionZone store information about the range being extracted and the
41// enclosing function.
42// 3. NewFunction stores properties of the extracted function and provides
43// methods for rendering it.
44// 4. CapturedZoneInfo uses a RecursiveASTVisitor to capture information about
45// the extraction like declarations, existing return statements, etc.
46// 5. getExtractedFunction is responsible for analyzing the CapturedZoneInfo and
47// creating a NewFunction.
48//===----------------------------------------------------------------------===//
49
50#include "AST.h"
51#include "FindTarget.h"
52#include "ParsedAST.h"
53#include "Selection.h"
54#include "SourceCode.h"
55#include "refactor/Tweak.h"
56#include "support/Logger.h"
57#include "clang/AST/ASTContext.h"
58#include "clang/AST/Decl.h"
59#include "clang/AST/DeclBase.h"
60#include "clang/AST/ExprCXX.h"
61#include "clang/AST/NestedNameSpecifier.h"
62#include "clang/AST/RecursiveASTVisitor.h"
63#include "clang/AST/Stmt.h"
64#include "clang/Basic/LangOptions.h"
65#include "clang/Basic/SourceLocation.h"
66#include "clang/Basic/SourceManager.h"
67#include "clang/Tooling/Core/Replacement.h"
68#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
69#include "llvm/ADT/STLExtras.h"
70#include "llvm/ADT/SmallSet.h"
71#include "llvm/ADT/SmallVector.h"
72#include "llvm/ADT/StringRef.h"
73#include "llvm/Support/Casting.h"
74#include "llvm/Support/Error.h"
75#include <optional>
76
77namespace clang {
78namespace clangd {
79namespace {
80
81using Node = SelectionTree::Node;
82
83// ExtractionZone is the part of code that is being extracted.
84// EnclosingFunction is the function/method inside which the zone lies.
85// We split the file into 4 parts relative to extraction zone.
86enum class ZoneRelative {
87 Before, // Before Zone and inside EnclosingFunction.
88 Inside, // Inside Zone.
89 After, // After Zone and inside EnclosingFunction.
90 OutsideFunc // Outside EnclosingFunction.
91};
92
93enum FunctionDeclKind {
94 InlineDefinition,
95 ForwardDeclaration,
96 OutOfLineDefinition
97};
98
99// Whether N, despite being Unselected, may still be a single RootStmt: a
100// DeclStmt can be unselected since VarDecls claim the entire selection range
101// in the selection tree. Similarly, a CXXOperatorCallExpr of a binary
102// operation can be unselected because its children (the operands) claim the
103// entire selection range in the selection tree (e.g. <<).
104bool isUnselectedRootStmtCandidate(const Node *N) {
105 return N->ASTNode.get<DeclStmt>() || N->ASTNode.get<CXXOperatorCallExpr>();
106}
107
108// Whether Child is the condition (or condition-variable declaration) of a
109// control-flow Parent, or the range-expression of a range-based for. These
110// are the only slots whose *value* is actually consumed by the construct
111// itself -- to decide whether to keep looping/branching, or to build the
112// hidden begin/end iterators -- so replacing them with a call to a
113// void-returning extracted function would not compile. Other slots, like a
114// loop's init-statement or increment expression, have their value discarded
115// just like an ordinary expression-statement (and any hazard from
116// extracting a declaration that's used later is already caught by
117// ExtractionZone::requiresHoisting), so they remain extractable.
118//
119// For CXXForRangeStmt, only RangeInit is ever reachable here: clangd's
120// SelectionTree has a custom traversal for range-based for loops (see
121// TraverseCXXForRangeStmt in Selection.cpp) that visits only the
122// init-statement, loop variable, range-expression, and body -- the
123// compiler-synthesized condition/increment/begin/end never become
124// SelectionTree nodes at all.
125bool isConditionClause(const Stmt *Parent, const Stmt *Child) {
126 if (const auto *If = llvm::dyn_cast<IfStmt>(Parent))
127 return Child == If->getCond() ||
128 Child == If->getConditionVariableDeclStmt();
129 if (const auto *For = llvm::dyn_cast<ForStmt>(Parent))
130 return Child == For->getCond() ||
131 Child == For->getConditionVariableDeclStmt();
132 if (const auto *While = llvm::dyn_cast<WhileStmt>(Parent))
133 return Child == While->getCond() ||
134 Child == While->getConditionVariableDeclStmt();
135 if (const auto *Do = llvm::dyn_cast<DoStmt>(Parent))
136 return Child == Do->getCond();
137 if (const auto *Switch = llvm::dyn_cast<SwitchStmt>(Parent))
138 return Child == Switch->getCond() ||
139 Child == Switch->getConditionVariableDeclStmt();
140 if (const auto *ForRange = llvm::dyn_cast<CXXForRangeStmt>(Parent))
141 return Child == ForRange->getRangeInit();
142 return false;
143}
144
145// A RootStmt is a statement that's fully selected including all its children
146// and its parent is unselected.
147// Check if a node is a root statement.
148bool isRootStmt(const Node *N) {
149 const Stmt *S = N->ASTNode.get<Stmt>();
150 if (!S)
151 return false;
152 // Root statement cannot be partially selected.
153 if (N->Selected == SelectionTree::Partial)
154 return false;
155 if (N->Selected == SelectionTree::Unselected &&
156 !isUnselectedRootStmtCandidate(N))
157 return false;
158 if (const Stmt *Parent = N->Parent ? N->Parent->ASTNode.get<Stmt>() : nullptr)
159 if (isConditionClause(Parent, S))
160 return false;
161 return true;
162}
163
164// Given a Child that is itself a single RootStmt (either because it's
165// completely selected, or because it's an isUnselectedRootStmtCandidate),
166// returns Child's enclosing statement, which is where we'll look for
167// Child's RootStmt siblings.
168//
169// If parent is a DeclStmt, even though it's unselected, we consider it a
170// root statement and return its parent instead. This is done because the
171// VarDecls claim the entire selection range of the Declaration and DeclStmt
172// is always unselected.
173//
174// Returns null if the (possibly DeclStmt-adjusted) parent isn't a "plain"
175// Stmt, or is an Expr: this means Child is merely a subexpression of a
176// larger expression or declaration rather than a genuine standalone
177// statement, and extracting it would produce broken code. This covers two
178// distinct cases:
179// - Parent is an Expr, e.g. selecting just the "3" in `stream << 3;`
180// (Child is a subexpression of a larger expression).
181// - Parent isn't a Stmt at all, e.g. selecting just the "func()" in
182// `auto A = func();` (Child is a VarDecl's initializer, so Parent is
183// that VarDecl -- a Decl, not a Stmt).
184const Node *getEnclosingStmt(const Node *Child) {
185 const Node *Parent = Child->Parent;
186 if (Parent->ASTNode.get<DeclStmt>())
187 Parent = Parent->Parent;
188 if (!Parent->ASTNode.get<Stmt>() || Parent->ASTNode.get<Expr>())
189 return nullptr;
190 return Parent;
191}
192
193// Returns the (unselected) parent of all RootStmts given the commonAncestor.
194// Returns null if:
195// 1. any node is partially selected
196// 2. If all completely selected nodes don't have the same common parent
197// 3. Any child of Parent isn't a RootStmt.
198// Returns null if any child is not a RootStmt.
199// We only support extraction of RootStmts since it allows us to extract without
200// having to change the selection range. Also, this means that any scope that
201// begins in selection range, ends in selection range and any scope that begins
202// outside the selection range, ends outside as well.
203const Node *getParentOfRootStmts(const Node *CommonAnc) {
204 if (!CommonAnc)
205 return nullptr;
206 const Node *Parent = nullptr;
207 switch (CommonAnc->Selected) {
209 // Typically a block, with the { and } unselected, could also be ForStmt
210 // etc. However, CommonAnc may instead be a single statement that is
211 // itself Unselected only because all of its own tokens are claimed by
212 // its children (see isUnselectedRootStmtCandidate); in that case it's a
213 // root statement in its own right, and we need its actual parent, same
214 // as in the Complete case below.
215 if (isUnselectedRootStmtCandidate(CommonAnc)) {
216 Parent = getEnclosingStmt(CommonAnc);
217 break;
218 }
219 // Ensure all Children are RootStmts.
220 Parent = CommonAnc;
221 break;
223 // Only a fully-selected single statement can be selected.
224 return nullptr;
226 Parent = getEnclosingStmt(CommonAnc);
227 break;
228 }
229 if (!Parent)
230 return nullptr;
231 // Ensure all Children are RootStmts.
232 return llvm::all_of(Parent->Children, isRootStmt) ? Parent : nullptr;
233}
234
235// The ExtractionZone class forms a view of the code wrt Zone.
236struct ExtractionZone {
237 // Parent of RootStatements being extracted.
238 const Node *Parent = nullptr;
239 // The half-open file range of the code being extracted.
240 SourceRange ZoneRange;
241 // The function inside which our zone resides.
242 const FunctionDecl *EnclosingFunction = nullptr;
243 // The half-open file range of the enclosing function.
244 SourceRange EnclosingFuncRange;
245 // Set of statements that form the ExtractionZone.
246 llvm::DenseSet<const Stmt *> RootStmts;
247
248 SourceLocation getInsertionPoint() const {
249 return EnclosingFuncRange.getBegin();
250 }
251 bool isRootStmt(const Stmt *S) const;
252 // The last root statement is important to decide where we need to insert a
253 // semicolon after the extraction.
254 const Node *getLastRootStmt() const { return Parent->Children.back(); }
255
256 // Checks if declarations inside extraction zone are accessed afterwards.
257 //
258 // This performs a partial AST traversal proportional to the size of the
259 // enclosing function, so it is possibly expensive.
260 bool requiresHoisting(const SourceManager &SM,
261 const HeuristicResolver *Resolver) const {
262 // First find all the declarations that happened inside extraction zone.
263 llvm::SmallPtrSet<const Decl *, 1> DeclsInExtZone;
264 for (auto *RootStmt : RootStmts) {
266 RootStmt,
267 [&DeclsInExtZone](const ReferenceLoc &Loc) {
268 if (!Loc.IsDecl)
269 return;
270 DeclsInExtZone.insert(Loc.Targets.front());
271 },
272 Resolver);
273 }
274 // Early exit without performing expensive traversal below.
275 if (DeclsInExtZone.empty())
276 return false;
277 // Then make sure they are not used outside the zone.
278 for (const auto *S : EnclosingFunction->getBody()->children()) {
279 if (SM.isBeforeInTranslationUnit(S->getSourceRange().getEnd(),
280 ZoneRange.getEnd()))
281 continue;
282 bool HasPostUse = false;
284 S,
285 [&](const ReferenceLoc &Loc) {
286 if (HasPostUse ||
287 SM.isBeforeInTranslationUnit(Loc.NameLoc, ZoneRange.getEnd()))
288 return;
289 HasPostUse = llvm::any_of(Loc.Targets,
290 [&DeclsInExtZone](const Decl *Target) {
291 return DeclsInExtZone.contains(Target);
292 });
293 },
294 Resolver);
295 if (HasPostUse)
296 return true;
297 }
298 return false;
299 }
300};
301
302// Whether the code in the extraction zone is guaranteed to return, assuming
303// no broken control flow (unbound break/continue).
304// This is a very naive check (does it end with a return stmt).
305// Doing some rudimentary control flow analysis would cover more cases.
306bool alwaysReturns(const ExtractionZone &EZ) {
307 const Stmt *Last = EZ.getLastRootStmt()->ASTNode.get<Stmt>();
308 // Unwrap enclosing (unconditional) compound statement.
309 while (const auto *CS = llvm::dyn_cast<CompoundStmt>(Last)) {
310 if (CS->body_empty())
311 return false;
312 Last = CS->body_back();
313 }
314 return llvm::isa<ReturnStmt>(Last);
315}
316
317bool ExtractionZone::isRootStmt(const Stmt *S) const {
318 return RootStmts.contains(S);
319}
320
321// Finds the function in which the zone lies.
322const FunctionDecl *findEnclosingFunction(const Node *CommonAnc) {
323 // Walk up the SelectionTree until we find a function Decl
324 for (const Node *CurNode = CommonAnc; CurNode; CurNode = CurNode->Parent) {
325 // Don't extract from lambdas
326 if (CurNode->ASTNode.get<LambdaExpr>())
327 return nullptr;
328 if (const FunctionDecl *Func = CurNode->ASTNode.get<FunctionDecl>()) {
329 // FIXME: Support extraction from templated functions.
330 if (Func->isTemplated())
331 return nullptr;
332 if (!Func->getBody())
333 return nullptr;
334 for (const auto *S : Func->getBody()->children()) {
335 // During apply phase, we perform semantic analysis (e.g. figure out
336 // what variables requires hoisting). We cannot perform those when the
337 // body has invalid statements, so fail up front.
338 if (!S)
339 return nullptr;
340 }
341 return Func;
342 }
343 }
344 return nullptr;
345}
346
347// Zone Range is the union of SourceRanges of all child Nodes in Parent since
348// all child Nodes are RootStmts
349std::optional<SourceRange> findZoneRange(const Node *Parent,
350 const SourceManager &SM,
351 const LangOptions &LangOpts) {
352 SourceRange SR;
353 if (auto BeginFileRange = toHalfOpenFileRange(
354 SM, LangOpts, Parent->Children.front()->ASTNode.getSourceRange()))
355 SR.setBegin(BeginFileRange->getBegin());
356 else
357 return std::nullopt;
358 if (auto EndFileRange = toHalfOpenFileRange(
359 SM, LangOpts, Parent->Children.back()->ASTNode.getSourceRange()))
360 SR.setEnd(EndFileRange->getEnd());
361 else
362 return std::nullopt;
363 return SR;
364}
365
366// Compute the range spanned by the enclosing function.
367// FIXME: check if EnclosingFunction has any attributes as the AST doesn't
368// always store the source range of the attributes and thus we end up extracting
369// between the attributes and the EnclosingFunction.
370std::optional<SourceRange>
371computeEnclosingFuncRange(const FunctionDecl *EnclosingFunction,
372 const SourceManager &SM,
373 const LangOptions &LangOpts) {
374 return toHalfOpenFileRange(SM, LangOpts, EnclosingFunction->getSourceRange());
375}
376
377// returns true if Child can be a single RootStmt being extracted from
378// EnclosingFunc.
379bool validSingleChild(const Node *Child, const FunctionDecl *EnclosingFunc) {
380 // Extracting the body of EnclosingFunc would remove it's definition.
381 assert(EnclosingFunc->hasBody() &&
382 "We should always be extracting from a function body.");
383 if (Child->ASTNode.get<Stmt>() == EnclosingFunc->getBody())
384 return false;
385 return true;
386}
387
388std::optional<ExtractionZone> findExtractionZone(const Node *CommonAnc,
389 const SourceManager &SM,
390 const LangOptions &LangOpts) {
391 ExtractionZone ExtZone;
392 ExtZone.Parent = getParentOfRootStmts(CommonAnc);
393 if (!ExtZone.Parent || ExtZone.Parent->Children.empty())
394 return std::nullopt;
395 ExtZone.EnclosingFunction = findEnclosingFunction(ExtZone.Parent);
396 if (!ExtZone.EnclosingFunction)
397 return std::nullopt;
398 // When there is a single RootStmt, we must check if it's valid for
399 // extraction.
400 if (ExtZone.Parent->Children.size() == 1 &&
401 !validSingleChild(ExtZone.getLastRootStmt(), ExtZone.EnclosingFunction))
402 return std::nullopt;
403 if (auto FuncRange =
404 computeEnclosingFuncRange(ExtZone.EnclosingFunction, SM, LangOpts))
405 ExtZone.EnclosingFuncRange = *FuncRange;
406 if (auto ZoneRange = findZoneRange(ExtZone.Parent, SM, LangOpts))
407 ExtZone.ZoneRange = *ZoneRange;
408 if (ExtZone.EnclosingFuncRange.isInvalid() || ExtZone.ZoneRange.isInvalid())
409 return std::nullopt;
410
411 for (const Node *Child : ExtZone.Parent->Children)
412 ExtZone.RootStmts.insert(Child->ASTNode.get<Stmt>());
413
414 return ExtZone;
415}
416
417// Stores information about the extracted function and provides methods for
418// rendering it.
419struct NewFunction {
420 struct Parameter {
421 std::string Name;
422 QualType TypeInfo;
423 bool PassByReference;
424 unsigned OrderPriority; // Lower value parameters are preferred first.
425 std::string render(const DeclContext *Context) const;
426 bool operator<(const Parameter &Other) const {
427 return OrderPriority < Other.OrderPriority;
428 }
429 };
430 std::string Name = "extracted";
431 QualType ReturnType;
432 std::vector<Parameter> Parameters;
433 SourceRange BodyRange;
434 SourceLocation DefinitionPoint;
435 std::optional<SourceLocation> ForwardDeclarationPoint;
436 const CXXRecordDecl *EnclosingClass = nullptr;
437 NestedNameSpecifier DefinitionQualifier = std::nullopt;
438 const DeclContext *SemanticDC = nullptr;
439 const DeclContext *SyntacticDC = nullptr;
440 const DeclContext *ForwardDeclarationSyntacticDC = nullptr;
441 bool CallerReturnsValue = false;
442 bool Static = false;
443 ConstexprSpecKind Constexpr = ConstexprSpecKind::Unspecified;
444 bool Const = false;
445
446 // Decides whether the extracted function body and the function call need a
447 // semicolon after extraction.
448 tooling::ExtractionSemicolonPolicy SemicolonPolicy;
449 const LangOptions *LangOpts;
450 NewFunction(tooling::ExtractionSemicolonPolicy SemicolonPolicy,
451 const LangOptions *LangOpts)
452 : SemicolonPolicy(SemicolonPolicy), LangOpts(LangOpts) {}
453 // Render the call for this function.
454 std::string renderCall() const;
455 // Render the definition for this function.
456 std::string renderDeclaration(FunctionDeclKind K,
457 const DeclContext &SemanticDC,
458 const DeclContext &SyntacticDC,
459 const SourceManager &SM) const;
460
461private:
462 std::string
463 renderParametersForDeclaration(const DeclContext &Enclosing) const;
464 std::string renderParametersForCall() const;
465 std::string renderSpecifiers(FunctionDeclKind K) const;
466 std::string renderQualifiers() const;
467 std::string renderDeclarationName(FunctionDeclKind K) const;
468 // Generate the function body.
469 std::string getFuncBody(const SourceManager &SM) const;
470};
471
472std::string NewFunction::renderParametersForDeclaration(
473 const DeclContext &Enclosing) const {
474 std::string Result;
475 bool NeedCommaBefore = false;
476 for (const Parameter &P : Parameters) {
477 if (NeedCommaBefore)
478 Result += ", ";
479 NeedCommaBefore = true;
480 Result += P.render(&Enclosing);
481 }
482 return Result;
483}
484
485std::string NewFunction::renderParametersForCall() const {
486 std::string Result;
487 bool NeedCommaBefore = false;
488 for (const Parameter &P : Parameters) {
489 if (NeedCommaBefore)
490 Result += ", ";
491 NeedCommaBefore = true;
492 Result += P.Name;
493 }
494 return Result;
495}
496
497std::string NewFunction::renderSpecifiers(FunctionDeclKind K) const {
498 std::string Attributes;
499
500 if (Static && K != FunctionDeclKind::OutOfLineDefinition) {
501 Attributes += "static ";
502 }
503
504 switch (Constexpr) {
505 case ConstexprSpecKind::Unspecified:
506 case ConstexprSpecKind::Constinit:
507 break;
508 case ConstexprSpecKind::Constexpr:
509 Attributes += "constexpr ";
510 break;
511 case ConstexprSpecKind::Consteval:
512 Attributes += "consteval ";
513 break;
514 }
515
516 return Attributes;
517}
518
519std::string NewFunction::renderQualifiers() const {
520 std::string Attributes;
521
522 if (Const) {
523 Attributes += " const";
524 }
525
526 return Attributes;
527}
528
529std::string NewFunction::renderDeclarationName(FunctionDeclKind K) const {
530 if (!DefinitionQualifier || K != OutOfLineDefinition)
531 return Name;
532
533 std::string QualifierName;
534 llvm::raw_string_ostream Oss(QualifierName);
535 DefinitionQualifier.print(Oss, *LangOpts);
536 return llvm::formatv("{0}{1}", QualifierName, Name);
537}
538
539std::string NewFunction::renderCall() const {
540 return std::string(
541 llvm::formatv("{0}{1}({2}){3}", CallerReturnsValue ? "return " : "", Name,
542 renderParametersForCall(),
543 (SemicolonPolicy.isNeededInOriginalFunction() ? ";" : "")));
544}
545
546std::string NewFunction::renderDeclaration(FunctionDeclKind K,
547 const DeclContext &SemanticDC,
548 const DeclContext &SyntacticDC,
549 const SourceManager &SM) const {
550 std::string Declaration = std::string(llvm::formatv(
551 "{0}{1} {2}({3}){4}", renderSpecifiers(K),
552 printType(ReturnType, SyntacticDC), renderDeclarationName(K),
553 renderParametersForDeclaration(SemanticDC), renderQualifiers()));
554
555 switch (K) {
556 case ForwardDeclaration:
557 return std::string(llvm::formatv("{0};\n", Declaration));
558 case OutOfLineDefinition:
559 case InlineDefinition:
560 return std::string(
561 llvm::formatv("{0} {\n{1}\n}\n", Declaration, getFuncBody(SM)));
562 break;
563 }
564 llvm_unreachable("Unsupported FunctionDeclKind enum");
565}
566
567std::string NewFunction::getFuncBody(const SourceManager &SM) const {
568 // FIXME: Generate tooling::Replacements instead of std::string to
569 // - hoist decls
570 // - add return statement
571 // - Add semicolon
572 return toSourceCode(SM, BodyRange).str() +
573 (SemicolonPolicy.isNeededInExtractedFunction() ? ";" : "");
574}
575
576std::string NewFunction::Parameter::render(const DeclContext *Context) const {
577 return printType(TypeInfo, *Context) + (PassByReference ? " &" : " ") + Name;
578}
579
580// Stores captured information about Extraction Zone.
581struct CapturedZoneInfo {
582 struct DeclInformation {
583 const Decl *TheDecl;
584 ZoneRelative DeclaredIn;
585 // index of the declaration or first reference.
586 unsigned DeclIndex;
587 bool IsReferencedInZone = false;
588 bool IsReferencedInPostZone = false;
589 // Conservatively: could this Decl be mutated somewhere in the zone? See
590 // ExtractionZoneVisitor::markPossiblyMutated() for what "conservatively"
591 // means here.
592 bool IsPossiblyMutated = false;
593 DeclInformation(const Decl *TheDecl, ZoneRelative DeclaredIn,
594 unsigned DeclIndex)
595 : TheDecl(TheDecl), DeclaredIn(DeclaredIn), DeclIndex(DeclIndex){};
596 // Marks the occurence of a reference for this declaration
597 void markOccurence(ZoneRelative ReferenceLoc);
598 };
599 // Maps Decls to their DeclInfo
600 llvm::DenseMap<const Decl *, DeclInformation> DeclInfoMap;
601 bool HasReturnStmt = false; // Are there any return statements in the zone?
602 bool AlwaysReturns = false; // Does the zone always return?
603 // Control flow is broken if we are extracting a break/continue without a
604 // corresponding parent loop/switch
605 bool BrokenControlFlow = false;
606 // FIXME: capture TypeAliasDecl and UsingDirectiveDecl
607 // FIXME: Capture type information as well.
608 DeclInformation *createDeclInfo(const Decl *D, ZoneRelative RelativeLoc);
609 DeclInformation *getDeclInfoFor(const Decl *D);
610};
611
612CapturedZoneInfo::DeclInformation *
613CapturedZoneInfo::createDeclInfo(const Decl *D, ZoneRelative RelativeLoc) {
614 // The new Decl's index is the size of the map so far.
615 auto InsertionResult = DeclInfoMap.insert(
616 {D, DeclInformation(D, RelativeLoc, DeclInfoMap.size())});
617 // Return the newly created DeclInfo
618 return &InsertionResult.first->second;
619}
620
621CapturedZoneInfo::DeclInformation *
622CapturedZoneInfo::getDeclInfoFor(const Decl *D) {
623 // If the Decl doesn't exist, we
624 auto Iter = DeclInfoMap.find(D);
625 if (Iter == DeclInfoMap.end())
626 return nullptr;
627 return &Iter->second;
628}
629
630void CapturedZoneInfo::DeclInformation::markOccurence(
631 ZoneRelative ReferenceLoc) {
632 switch (ReferenceLoc) {
633 case ZoneRelative::Inside:
634 IsReferencedInZone = true;
635 break;
636 case ZoneRelative::After:
637 IsReferencedInPostZone = true;
638 break;
639 default:
640 break;
641 }
642}
643
644bool isLoop(const Stmt *S) {
645 return isa<ForStmt>(S) || isa<DoStmt>(S) || isa<WhileStmt>(S) ||
646 isa<CXXForRangeStmt>(S);
647}
648
649// Strips E down to the Decl(s) whose storage it ultimately refers to,
650// chaining through parens, casts, and member/array-element access (e.g.
651// `a.b[i]` resolves to `a`), but only when that access reaches through
652// value semantics: mutating `a.b` mutates `a`'s own storage, so we keep
653// chaining. We deliberately stop at a pointer-typed base (e.g. `p->b`,
654// `p[i]`, `p->*pmf`): that only mutates `*p`, never `p`'s own binding, so
655// chaining through it would incorrectly require `p` to stay non-const. For
656// the same reason, a dereference (`*p = 1`) is intentionally not handled
657// at all: it only ever mutates the pointee, never the pointer itself.
658//
659// A genuine array subscript (`arr[i]` where `arr` is an array, not a
660// pointer) also stops here: the base is always wrapped in an
661// ArrayToPointerDecay cast, indistinguishable at this point from
662// subscripting a real pointer. That's fine because createParameters()
663// never makes an array-typed capture const in the first place, regardless
664// of what we compute here.
665//
666// A conditional expression (e.g. `(cond ? a : b).m`) could resolve to
667// either branch at runtime, so both are collected -- this can only grow
668// the result, never replace it, which is why this appends to Decls rather
669// than returning a single Decl the way the rest of this function might
670// suggest. This has to be handled at every level of the chain, not just
671// the top: `(cond ? a : b).m = 1` reaches the conditional through a
672// MemberExpr base, not directly.
673//
674// Appends nothing if E isn't ultimately grounded in a variable this way
675// (e.g. it's a temporary, a call result, or reached through a pointer).
676void collectUnderlyingDecls(const Expr *E,
677 llvm::SmallVectorImpl<const Decl *> &Decls) {
678 if (!E)
679 return;
680 E = E->IgnoreParenCasts();
681 if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
682 Decls.push_back(DRE->getDecl());
683 return;
684 }
685 if (const auto *ME = dyn_cast<MemberExpr>(E)) {
686 if (!ME->getBase()->getType()->isPointerType())
687 collectUnderlyingDecls(ME->getBase(), Decls);
688 return;
689 }
690 if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
691 if (!ASE->getBase()->getType()->isPointerType())
692 collectUnderlyingDecls(ASE->getBase(), Decls);
693 return;
694 }
695 if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
696 // Only BO_PtrMemD (`.*`) is handled: its LHS is always an object, by
697 // grammar, so the chain always continues (matching MemberExpr's `.`
698 // above). BO_PtrMemI (`->*`) is excluded: its LHS is always a pointer,
699 // by grammar, so the chain should never continue (matching
700 // MemberExpr's `->` above).
701 if (BO->getOpcode() == BO_PtrMemD)
702 collectUnderlyingDecls(BO->getLHS(), Decls);
703 return;
704 }
705 if (const auto *CO = dyn_cast<AbstractConditionalOperator>(E)) {
706 collectUnderlyingDecls(CO->getTrueExpr(), Decls);
707 collectUnderlyingDecls(CO->getFalseExpr(), Decls);
708 }
709}
710
711// Captures information from Extraction Zone
712CapturedZoneInfo captureZoneInfo(const ExtractionZone &ExtZone) {
713 // We use the ASTVisitor instead of using the selection tree since we need to
714 // find references in the PostZone as well.
715 // FIXME: Check which statements we don't allow to extract.
716 class ExtractionZoneVisitor
717 : public clang::RecursiveASTVisitor<ExtractionZoneVisitor> {
718 public:
719 ExtractionZoneVisitor(const ExtractionZone &ExtZone) : ExtZone(ExtZone) {
720 TraverseDecl(const_cast<FunctionDecl *>(ExtZone.EnclosingFunction));
721 }
722
723 bool TraverseStmt(Stmt *S) {
724 if (!S)
725 return true;
726 bool IsRootStmt = ExtZone.isRootStmt(const_cast<const Stmt *>(S));
727 // If we are starting traversal of a RootStmt, we are somewhere inside
728 // ExtractionZone
729 if (IsRootStmt)
730 CurrentLocation = ZoneRelative::Inside;
731 addToLoopSwitchCounters(S, 1);
732 // Traverse using base class's TraverseStmt
733 RecursiveASTVisitor::TraverseStmt(S);
734 addToLoopSwitchCounters(S, -1);
735 // We set the current location as after since next stmt will either be a
736 // RootStmt (handled at the beginning) or after extractionZone
737 if (IsRootStmt)
738 CurrentLocation = ZoneRelative::After;
739 return true;
740 }
741
742 // Add Increment to CurNumberOf{Loops,Switch} if statement is
743 // {Loop,Switch} and inside Extraction Zone.
744 void addToLoopSwitchCounters(Stmt *S, int Increment) {
745 if (CurrentLocation != ZoneRelative::Inside)
746 return;
747 if (isLoop(S))
748 CurNumberOfNestedLoops += Increment;
749 else if (isa<SwitchStmt>(S))
750 CurNumberOfSwitch += Increment;
751 }
752
753 bool VisitDecl(Decl *D) {
754 Info.createDeclInfo(D, CurrentLocation);
755 return true;
756 }
757
758 bool VisitDeclRefExpr(DeclRefExpr *DRE) {
759 // Find the corresponding Decl and mark it's occurrence.
760 const Decl *D = DRE->getDecl();
761 auto *DeclInfo = Info.getDeclInfoFor(D);
762 // If no Decl was found, the Decl must be outside the enclosingFunc.
763 if (!DeclInfo)
764 DeclInfo = Info.createDeclInfo(D, ZoneRelative::OutsideFunc);
765 DeclInfo->markOccurence(CurrentLocation);
766 return true;
767 }
768
769 // Conservatively marks D as possibly mutated: used both for actual
770 // direct mutations (assignment, increment/decrement, ...) and for
771 // constructs that alias D in a way we don't want to trace further (a
772 // reference bound to D, D's address taken, D captured by reference in a
773 // lambda, ...). We never try to determine whether such an alias is
774 // itself later mutated -- that would require searching beyond this one
775 // occurrence, which is exactly the cost this design avoids (and what
776 // makes ExprMutationAnalyzer prohibitively slow). The price is
777 // that we sometimes keep a parameter non-const where a full alias
778 // analysis could prove it safe to const; we never get this wrong in the
779 // other, unsafe direction.
780 void markPossiblyMutated(const Decl *D) {
781 if (!D || CurrentLocation != ZoneRelative::Inside)
782 return;
783 if (auto *DeclInfo = Info.getDeclInfoFor(D))
784 DeclInfo->IsPossiblyMutated = true;
785 }
786 void markPossiblyMutated(const Expr *E) {
787 llvm::SmallVector<const Decl *, 2> Decls;
788 collectUnderlyingDecls(E, Decls);
789 for (const Decl *D : Decls)
790 markPossiblyMutated(D);
791 }
792
793 bool VisitBinaryOperator(BinaryOperator *BO) {
794 if (BO->isAssignmentOp())
795 markPossiblyMutated(BO->getLHS());
796 return true;
797 }
798
799 bool VisitUnaryOperator(UnaryOperator *UO) {
800 // FIXME: Try to track where the result of the address operator
801 // ends up. If it's a const pointer, this is not a mutating access.
802 if (UO->isIncrementDecrementOp() || UO->getOpcode() == UO_AddrOf)
803 markPossiblyMutated(UO->getSubExpr());
804 return true;
805 }
806
807 bool VisitExplicitCastExpr(ExplicitCastExpr *ECE) {
808 // An explicit cast to a non-const reference type allows mutating the
809 // result as if it were a plain non-const reference.
810 if (ECE->getType()->isReferenceType() &&
811 !ECE->getType()->getPointeeType().isConstQualified())
812 markPossiblyMutated(ECE->getSubExpr());
813 return true;
814 }
815
816 // Marks the object a non-const member function is (or may be) called
817 // on, whether through `.`, `->`, or an overloaded operator. A static
818 // method (possible since C++23 for operator() and operator[]) has no
819 // `this` at all, so it never touches Object regardless of constness.
820 void markPossiblyMutatedCallee(const Expr *Object,
821 const CXXMethodDecl *Method) {
822 if (Method && !Method->isStatic() && !Method->isConst())
823 markPossiblyMutated(Object);
824 }
825
826 // Marks the arguments of a call that bind to a non-const reference
827 // parameter of Callee (a FunctionDecl or CXXConstructorDecl). If Callee
828 // is null (e.g. a call through a function pointer), conservatively marks
829 // every argument, since we don't know the parameter types.
830 void markPossiblyMutatedArgs(ArrayRef<const Expr *> Args,
831 const FunctionDecl *Callee) {
832 for (unsigned I = 0; I < Args.size(); ++I) {
833 if (!Callee || I >= Callee->getNumParams() ||
834 (Callee->getParamDecl(I)->getType()->isReferenceType() &&
835 !Callee->getParamDecl(I)
836 ->getType()
837 ->getPointeeType()
838 .isConstQualified()))
839 markPossiblyMutated(Args[I]);
840 }
841 }
842
843 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) {
844 markPossiblyMutatedCallee(MCE->getImplicitObjectArgument(),
845 MCE->getMethodDecl());
846 // getArgs() here is just the explicit argument list, without the
847 // implicit object handled above, so it aligns directly with the
848 // method's own parameters.
849 markPossiblyMutatedArgs(
850 llvm::ArrayRef<const Expr *>(MCE->getArgs(), MCE->getNumArgs()),
851 MCE->getMethodDecl());
852 return true;
853 }
854
855 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) {
856 // Unlike CXXMemberCallExpr, a member operator's implicit object is
857 // args[0], not split out separately; args[1:] are the real
858 // parameters. A non-member (free function) operator overload has no
859 // implicit object at all, so all args align directly with params.
860 // Every operator has at least one operand, but guard anyway since
861 // the arithmetic below would underflow on an empty argument list.
862 if (OCE->getNumArgs() == 0)
863 return true;
864 const auto *Method =
865 dyn_cast_or_null<CXXMethodDecl>(OCE->getCalleeDecl());
866 if (Method) {
867 markPossiblyMutatedCallee(OCE->getArg(0), Method);
868 markPossiblyMutatedArgs(llvm::ArrayRef<const Expr *>(
869 OCE->getArgs() + 1, OCE->getNumArgs() - 1),
870 Method);
871 } else {
872 markPossiblyMutatedArgs(
873 llvm::ArrayRef<const Expr *>(OCE->getArgs(), OCE->getNumArgs()),
874 OCE->getDirectCallee());
875 }
876 return true;
877 }
878
879 bool VisitCallExpr(CallExpr *CE) {
880 // Both are already fully handled by their own visitors above,
881 // including their arguments: skip CXXMemberCallExpr here to avoid
882 // redundant work (its getArgs()/getDirectCallee() would otherwise
883 // align fine on their own), and skip CXXOperatorCallExpr because its
884 // getArgs() includes the implicit object as args[0], which would
885 // misalign against a member operator's real parameter list if
886 // checked here too.
887 if (isa<CXXMemberCallExpr>(CE) || isa<CXXOperatorCallExpr>(CE))
888 return true;
889 markPossiblyMutatedArgs(
890 llvm::ArrayRef<const Expr *>(CE->getArgs(), CE->getNumArgs()),
891 CE->getDirectCallee());
892 return true;
893 }
894
895 bool VisitCXXConstructExpr(CXXConstructExpr *CCE) {
896 markPossiblyMutatedArgs(
897 llvm::ArrayRef<const Expr *>(CCE->getArgs(), CCE->getNumArgs()),
898 CCE->getConstructor());
899 return true;
900 }
901
902 bool VisitVarDecl(VarDecl *VD) {
903 // A non-const reference bound to a captured Decl aliases it: treat any
904 // such binding as a possible mutation, without checking whether the
905 // reference itself is later mutated (see markPossiblyMutated). This
906 // also covers reference structured bindings (DecompositionDecl is a
907 // VarDecl), since binding `auto &[a, b] = x` conservatively counts as
908 // aliasing all of `x`.
909 if (VD->getType()->isReferenceType() &&
910 !VD->getType()->getPointeeType().isConstQualified() && VD->hasInit())
911 markPossiblyMutated(VD->getInit()->IgnoreParens());
912 return true;
913 }
914
915 bool VisitLambdaExpr(LambdaExpr *LE) {
916 // Init-captures (`[r = x]`/`[&r = x]`) are handled by VisitVarDecl
917 // above, since they introduce a real VarDecl visited independently.
918 // This only needs to handle plain captures (`[&x]`/`[&]`), which don't.
919 for (const LambdaCapture &C : LE->captures())
920 if (C.capturesVariable() && C.getCaptureKind() == LCK_ByRef)
921 markPossiblyMutated(C.getCapturedVar());
922 return true;
923 }
924
925 bool VisitCXXForRangeStmt(CXXForRangeStmt *FRS) {
926 // Conservatively: a non-const reference (or pointer) loop variable
927 // could mutate the range expression's elements, which -- like a
928 // member/array-element write -- requires the range expression's own
929 // Decl to be non-const if it's a value type (e.g. a plain array).
930 // We don't check whether the loop variable is actually mutated, and we
931 // don't special-case containers with const-qualified begin()/end():
932 // those are visited like any other (possibly non-const) member call.
933 QualType LoopVarType = FRS->getLoopVariable()->getType();
934 if ((LoopVarType->isReferenceType() &&
935 !LoopVarType->getPointeeType().isConstQualified()) ||
936 (LoopVarType->isPointerType() &&
937 !LoopVarType->getPointeeType().isConstQualified()))
938 if (const Expr *RangeInit = FRS->getRangeInit())
939 markPossiblyMutated(RangeInit);
940 return true;
941 }
942
943 bool VisitReturnStmt(ReturnStmt *Return) {
944 if (CurrentLocation == ZoneRelative::Inside) {
945 Info.HasReturnStmt = true;
946 // Conservatively treat returning a captured Decl as a possible
947 // mutation, regardless of whether the return is actually by value
948 // (safe) or by non-const reference (not safe). Telling these apart
949 // needs the extracted function's return type, which is only
950 // decided later in generateReturnProperties(); duplicating its
951 // logic here would couple two distant functions for little gain,
952 // since directly returning a capture is rare.
953 if (const Expr *RV = Return->getRetValue())
954 markPossiblyMutated(RV);
955 }
956 return true;
957 }
958
959 bool VisitBreakStmt(BreakStmt *Break) {
960 // Control flow is broken if break statement is selected without any
961 // parent loop or switch statement.
962 if (CurrentLocation == ZoneRelative::Inside &&
963 !(CurNumberOfNestedLoops || CurNumberOfSwitch))
964 Info.BrokenControlFlow = true;
965 return true;
966 }
967
968 bool VisitContinueStmt(ContinueStmt *Continue) {
969 // Control flow is broken if Continue statement is selected without any
970 // parent loop
971 if (CurrentLocation == ZoneRelative::Inside && !CurNumberOfNestedLoops)
972 Info.BrokenControlFlow = true;
973 return true;
974 }
975 CapturedZoneInfo Info;
976 const ExtractionZone &ExtZone;
977 ZoneRelative CurrentLocation = ZoneRelative::Before;
978 // Number of {loop,switch} statements that are currently in the traversal
979 // stack inside Extraction Zone. Used to check for broken control flow.
980 unsigned CurNumberOfNestedLoops = 0;
981 unsigned CurNumberOfSwitch = 0;
982 };
983 ExtractionZoneVisitor Visitor(ExtZone);
984 CapturedZoneInfo Result = std::move(Visitor.Info);
985 Result.AlwaysReturns = alwaysReturns(ExtZone);
986 return Result;
987}
988
989// Adds parameters to ExtractedFunc.
990// Returns true if able to find the parameters successfully and no hoisting
991// needed.
992// FIXME: Check if the declaration has a local/anonymous type
993bool createParameters(NewFunction &ExtractedFunc,
994 const CapturedZoneInfo &CapturedInfo) {
995 // FIXME: Pass non-mutated parameters of built-in type by value.
996 for (const auto &KeyVal : CapturedInfo.DeclInfoMap) {
997 const auto &DeclInfo = KeyVal.second;
998 // If a Decl was Declared in zone and referenced in post zone, it
999 // needs to be hoisted (we bail out in that case).
1000 // FIXME: Support Decl Hoisting.
1001 if (DeclInfo.DeclaredIn == ZoneRelative::Inside &&
1002 DeclInfo.IsReferencedInPostZone)
1003 return false;
1004 if (!DeclInfo.IsReferencedInZone)
1005 continue; // no need to pass as parameter, not referenced
1006 if (DeclInfo.DeclaredIn == ZoneRelative::Inside ||
1007 DeclInfo.DeclaredIn == ZoneRelative::OutsideFunc)
1008 continue; // no need to pass as parameter, still accessible.
1009 // Parameter specific checks.
1010 const ValueDecl *VD = dyn_cast_or_null<ValueDecl>(DeclInfo.TheDecl);
1011 // Can't parameterise if the Decl isn't a ValueDecl or is a FunctionDecl
1012 // (this includes the case of recursive call to EnclosingFunc in Zone).
1013 if (!VD || isa<FunctionDecl>(DeclInfo.TheDecl))
1014 return false;
1015 // Parameter qualifiers are same as the Decl's qualifiers.
1016 QualType TypeInfo = VD->getType().getNonReferenceType();
1017 // Add const if it's not (conservatively) mutated in the zone: it's
1018 // still passed by reference to avoid a copy, but the reference doesn't
1019 // need to be mutable. Array types are never made const: mutating array
1020 // elements through a non-const-ref loop variable or a decayed pointer
1021 // argument is common and easy to miss conservatively, so we don't try.
1022 if (!DeclInfo.IsPossiblyMutated && !TypeInfo->isArrayType())
1023 TypeInfo.addConst();
1024 // FIXME: check if parameter will be a non l-value reference.
1025 bool IsPassedByReference = true;
1026 // We use the index of declaration as the ordering priority for parameters.
1027 ExtractedFunc.Parameters.push_back({std::string(VD->getName()), TypeInfo,
1028 IsPassedByReference,
1029 DeclInfo.DeclIndex});
1030 }
1031 llvm::sort(ExtractedFunc.Parameters);
1032 return true;
1033}
1034
1035// Clangd uses open ranges while ExtractionSemicolonPolicy (in Clang Tooling)
1036// uses closed ranges. Generates the semicolon policy for the extraction and
1037// extends the ZoneRange if necessary.
1038tooling::ExtractionSemicolonPolicy
1039getSemicolonPolicy(ExtractionZone &ExtZone, const SourceManager &SM,
1040 const LangOptions &LangOpts) {
1041 // Get closed ZoneRange.
1042 SourceRange FuncBodyRange = {ExtZone.ZoneRange.getBegin(),
1043 ExtZone.ZoneRange.getEnd().getLocWithOffset(-1)};
1044 auto SemicolonPolicy = tooling::ExtractionSemicolonPolicy::compute(
1045 ExtZone.getLastRootStmt()->ASTNode.get<Stmt>(), FuncBodyRange, SM,
1046 LangOpts);
1047 // Update ZoneRange.
1048 ExtZone.ZoneRange.setEnd(FuncBodyRange.getEnd().getLocWithOffset(1));
1049 return SemicolonPolicy;
1050}
1051
1052// Generate return type for ExtractedFunc. Return false if unable to do so.
1053bool generateReturnProperties(NewFunction &ExtractedFunc,
1054 const FunctionDecl &EnclosingFunc,
1055 const CapturedZoneInfo &CapturedInfo) {
1056 // If the selected code always returns, we preserve those return statements.
1057 // The return type should be the same as the enclosing function.
1058 // (Others are possible if there are conversions, but this seems clearest).
1059 if (CapturedInfo.HasReturnStmt) {
1060 // If the return is conditional, neither replacing the code with
1061 // `extracted()` nor `return extracted()` is correct.
1062 if (!CapturedInfo.AlwaysReturns)
1063 return false;
1064 QualType Ret = EnclosingFunc.getReturnType();
1065 // Once we support members, it'd be nice to support e.g. extracting a method
1066 // of Foo<T> that returns T. But it's not clear when that's safe.
1067 if (Ret->isDependentType())
1068 return false;
1069 ExtractedFunc.ReturnType = Ret;
1070 return true;
1071 }
1072 // FIXME: Generate new return statement if needed.
1073 ExtractedFunc.ReturnType = EnclosingFunc.getParentASTContext().VoidTy;
1074 return true;
1075}
1076
1077void captureMethodInfo(NewFunction &ExtractedFunc,
1078 const CXXMethodDecl *Method) {
1079 ExtractedFunc.Static = Method->isStatic();
1080 ExtractedFunc.Const = Method->isConst();
1081 ExtractedFunc.EnclosingClass = Method->getParent();
1082}
1083
1084// FIXME: add support for adding other function return types besides void.
1085// FIXME: assign the value returned by non void extracted function.
1086llvm::Expected<NewFunction> getExtractedFunction(ExtractionZone &ExtZone,
1087 const SourceManager &SM,
1088 const LangOptions &LangOpts) {
1089 CapturedZoneInfo CapturedInfo = captureZoneInfo(ExtZone);
1090 // Bail out if any break of continue exists
1091 if (CapturedInfo.BrokenControlFlow)
1092 return error("Cannot extract break/continue without corresponding "
1093 "loop/switch statement.");
1094 NewFunction ExtractedFunc(getSemicolonPolicy(ExtZone, SM, LangOpts),
1095 &LangOpts);
1096
1097 ExtractedFunc.SyntacticDC =
1098 ExtZone.EnclosingFunction->getLexicalDeclContext();
1099 ExtractedFunc.SemanticDC = ExtZone.EnclosingFunction->getDeclContext();
1100 ExtractedFunc.DefinitionQualifier = ExtZone.EnclosingFunction->getQualifier();
1101 ExtractedFunc.Constexpr = ExtZone.EnclosingFunction->getConstexprKind();
1102
1103 if (const auto *Method =
1104 llvm::dyn_cast<CXXMethodDecl>(ExtZone.EnclosingFunction))
1105 captureMethodInfo(ExtractedFunc, Method);
1106
1107 if (ExtZone.EnclosingFunction->isOutOfLine()) {
1108 // FIXME: Put the extracted method in a private section if it's a class or
1109 // maybe in an anonymous namespace
1110 const auto *FirstOriginalDecl =
1111 ExtZone.EnclosingFunction->getCanonicalDecl();
1112 auto DeclPos =
1113 toHalfOpenFileRange(SM, LangOpts, FirstOriginalDecl->getSourceRange());
1114 if (!DeclPos)
1115 return error("Declaration is inside a macro");
1116 ExtractedFunc.ForwardDeclarationPoint = DeclPos->getBegin();
1117 ExtractedFunc.ForwardDeclarationSyntacticDC = ExtractedFunc.SemanticDC;
1118 }
1119
1120 ExtractedFunc.BodyRange = ExtZone.ZoneRange;
1121 ExtractedFunc.DefinitionPoint = ExtZone.getInsertionPoint();
1122
1123 ExtractedFunc.CallerReturnsValue = CapturedInfo.AlwaysReturns;
1124 if (!createParameters(ExtractedFunc, CapturedInfo) ||
1125 !generateReturnProperties(ExtractedFunc, *ExtZone.EnclosingFunction,
1126 CapturedInfo))
1127 return error("Too complex to extract.");
1128 return ExtractedFunc;
1129}
1130
1131class ExtractFunction : public Tweak {
1132public:
1133 const char *id() const final;
1134 bool prepare(const Selection &Inputs) override;
1135 Expected<Effect> apply(const Selection &Inputs) override;
1136 std::string title() const override { return "Extract to function"; }
1137 llvm::StringLiteral kind() const override {
1138 return CodeAction::REFACTOR_KIND;
1139 }
1140
1141private:
1142 ExtractionZone ExtZone;
1143};
1144
1145REGISTER_TWEAK(ExtractFunction)
1146tooling::Replacement replaceWithFuncCall(const NewFunction &ExtractedFunc,
1147 const SourceManager &SM,
1148 const LangOptions &LangOpts) {
1149 std::string FuncCall = ExtractedFunc.renderCall();
1150 return tooling::Replacement(
1151 SM, CharSourceRange(ExtractedFunc.BodyRange, false), FuncCall, LangOpts);
1152}
1153
1154tooling::Replacement createFunctionDefinition(const NewFunction &ExtractedFunc,
1155 const SourceManager &SM) {
1156 FunctionDeclKind DeclKind = InlineDefinition;
1157 if (ExtractedFunc.ForwardDeclarationPoint)
1158 DeclKind = OutOfLineDefinition;
1159 std::string FunctionDef = ExtractedFunc.renderDeclaration(
1160 DeclKind, *ExtractedFunc.SemanticDC, *ExtractedFunc.SyntacticDC, SM);
1161
1162 return tooling::Replacement(SM, ExtractedFunc.DefinitionPoint, 0,
1163 FunctionDef);
1164}
1165
1166tooling::Replacement createForwardDeclaration(const NewFunction &ExtractedFunc,
1167 const SourceManager &SM) {
1168 std::string FunctionDecl = ExtractedFunc.renderDeclaration(
1169 ForwardDeclaration, *ExtractedFunc.SemanticDC,
1170 *ExtractedFunc.ForwardDeclarationSyntacticDC, SM);
1171 SourceLocation DeclPoint = *ExtractedFunc.ForwardDeclarationPoint;
1172
1173 return tooling::Replacement(SM, DeclPoint, 0, FunctionDecl);
1174}
1175
1176// Returns true if ExtZone contains any ReturnStmts.
1177bool hasReturnStmt(const ExtractionZone &ExtZone) {
1178 class ReturnStmtVisitor
1179 : public clang::RecursiveASTVisitor<ReturnStmtVisitor> {
1180 public:
1181 bool VisitReturnStmt(ReturnStmt *Return) {
1182 Found = true;
1183 return false; // We found the answer, abort the scan.
1184 }
1185 bool Found = false;
1186 };
1187
1188 ReturnStmtVisitor V;
1189 for (const Stmt *RootStmt : ExtZone.RootStmts) {
1190 V.TraverseStmt(const_cast<Stmt *>(RootStmt));
1191 if (V.Found)
1192 break;
1193 }
1194 return V.Found;
1195}
1196
1197bool ExtractFunction::prepare(const Selection &Inputs) {
1198 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1199 if (!LangOpts.CPlusPlus)
1200 return false;
1201 const Node *CommonAnc = Inputs.ASTSelection.commonAncestor();
1202 const SourceManager &SM = Inputs.AST->getSourceManager();
1203 auto MaybeExtZone = findExtractionZone(CommonAnc, SM, LangOpts);
1204 if (!MaybeExtZone ||
1205 (hasReturnStmt(*MaybeExtZone) && !alwaysReturns(*MaybeExtZone)))
1206 return false;
1207
1208 // FIXME: Get rid of this check once we support hoisting.
1209 if (MaybeExtZone->requiresHoisting(SM, Inputs.AST->getHeuristicResolver()))
1210 return false;
1211
1212 ExtZone = std::move(*MaybeExtZone);
1213 return true;
1214}
1215
1216Expected<Tweak::Effect> ExtractFunction::apply(const Selection &Inputs) {
1217 const SourceManager &SM = Inputs.AST->getSourceManager();
1218 const LangOptions &LangOpts = Inputs.AST->getLangOpts();
1219 auto ExtractedFunc = getExtractedFunction(ExtZone, SM, LangOpts);
1220 // FIXME: Add more types of errors.
1221 if (!ExtractedFunc)
1222 return ExtractedFunc.takeError();
1223 tooling::Replacements Edit;
1224 if (auto Err = Edit.add(createFunctionDefinition(*ExtractedFunc, SM)))
1225 return std::move(Err);
1226 if (auto Err = Edit.add(replaceWithFuncCall(*ExtractedFunc, SM, LangOpts)))
1227 return std::move(Err);
1228
1229 if (auto FwdLoc = ExtractedFunc->ForwardDeclarationPoint) {
1230 // If the fwd-declaration goes in the same file, merge into Replacements.
1231 // Otherwise it needs to be a separate file edit.
1232 if (SM.isWrittenInSameFile(ExtractedFunc->DefinitionPoint, *FwdLoc)) {
1233 if (auto Err = Edit.add(createForwardDeclaration(*ExtractedFunc, SM)))
1234 return std::move(Err);
1235 } else {
1236 auto MultiFileEffect = Effect::mainFileEdit(SM, std::move(Edit));
1237 if (!MultiFileEffect)
1238 return MultiFileEffect.takeError();
1239
1240 tooling::Replacements OtherEdit(
1241 createForwardDeclaration(*ExtractedFunc, SM));
1242 if (auto PathAndEdit =
1243 Tweak::Effect::fileEdit(SM, SM.getFileID(*FwdLoc), OtherEdit))
1244 MultiFileEffect->ApplyEdits.try_emplace(PathAndEdit->first,
1245 PathAndEdit->second);
1246 else
1247 return PathAndEdit.takeError();
1248 return MultiFileEffect;
1249 }
1250 }
1251 return Effect::mainFileEdit(SM, std::move(Edit));
1252}
1253
1254} // namespace
1255} // namespace clangd
1256} // namespace clang
#define REGISTER_TWEAK(Subclass)
Definition Tweak.h:129
llvm::Error error(std::error_code, std::string &&)
Definition Logger.cpp:80
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
@ Info
An information message.
Definition Protocol.h:755
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
std::string printType(const QualType QT, const DeclContext &CurContext, const llvm::StringRef Placeholder, bool FullyQualify)
Returns a QualType as string.
Definition AST.cpp:417
void findExplicitReferences(const Stmt *S, llvm::function_ref< void(ReferenceLoc)> Out, const HeuristicResolver *Resolver)
Recursively traverse S and report all references explicitly written in the code.
llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R)
Returns the source code covered by the source range.
bool operator<(const Ref &L, const Ref &R)
Definition Ref.h:98
@ Parameter
An inlay hint that is for a parameter.
Definition Protocol.h:1752
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//