diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp @@ -179,54 +179,6 @@ return tooling::Replacement(SM, InsertionLoc, 0, ExtractedVarDecl); } -/// Extracts an expression to the variable dummy -/// Before: -/// int x = 5 + 4 * 3; -/// ^^^^^ -/// After: -/// auto dummy = 5 + 4; -/// int x = dummy * 3; -class ExtractVariable : public Tweak { -public: - const char *id() const override final; - bool prepare(const Selection &Inputs) override; - Expected apply(const Selection &Inputs) override; - std::string title() const override { - return "Extract subexpression to variable"; - } - Intent intent() const override { return Refactor; } - // Compute the extraction context for the Selection - bool computeExtractionContext(const SelectionTree::Node *N, - const SourceManager &SM, const ASTContext &Ctx); - -private: - // the expression to extract - std::unique_ptr Target; -}; -REGISTER_TWEAK(ExtractVariable) -bool ExtractVariable::prepare(const Selection &Inputs) { - // we don't trigger on empty selections for now - if (Inputs.SelectionBegin == Inputs.SelectionEnd) - return false; - const ASTContext &Ctx = Inputs.AST.getASTContext(); - const SourceManager &SM = Inputs.AST.getSourceManager(); - const SelectionTree::Node *N = Inputs.ASTSelection.commonAncestor(); - return computeExtractionContext(N, SM, Ctx); -} - -Expected ExtractVariable::apply(const Selection &Inputs) { - tooling::Replacements Result; - // FIXME: get variable name from user or suggest based on type - std::string VarName = "dummy"; - // insert new variable declaration - if (auto Err = Result.add(Target->insertDeclaration(VarName))) - return std::move(Err); - // replace expression with variable name - if (auto Err = Result.add(Target->replaceWithVar(VarName))) - return std::move(Err); - return Effect::applyEdit(Result); -} - // Find the CallExpr whose callee is an ancestor of the DeclRef const SelectionTree::Node *getCallExpr(const SelectionTree::Node *DeclRef) { // we maintain a stack of all exprs encountered while traversing the @@ -256,24 +208,22 @@ return true; } -// Find the node that will form our ExtractionContext. +// Find the Expr node that we're going to extract. // We don't want to trigger for assignment expressions and variable/field // DeclRefs. For function/member function, we want to extract the entire // function call. -bool ExtractVariable::computeExtractionContext(const SelectionTree::Node *N, - const SourceManager &SM, - const ASTContext &Ctx) { +const SelectionTree::Node *computeExtractedExpr(const SelectionTree::Node *N) { if (!N) - return false; + return nullptr; const clang::Expr *SelectedExpr = N->ASTNode.get(); const SelectionTree::Node *TargetNode = N; if (!SelectedExpr) - return false; + return nullptr; // Extracting Exprs like a = 1 gives dummy = a = 1 which isn't useful. if (const BinaryOperator *BinOpExpr = dyn_cast_or_null(SelectedExpr)) { if (BinOpExpr->getOpcode() == BinaryOperatorKind::BO_Assign) - return false; + return nullptr; } // For function and member function DeclRefs, we look for a parent that is a // CallExpr @@ -281,19 +231,65 @@ dyn_cast_or_null(SelectedExpr)) { // Extracting just a variable isn't that useful. if (!isa(DeclRef->getDecl())) - return false; + return nullptr; TargetNode = getCallExpr(N); } if (const MemberExpr *Member = dyn_cast_or_null(SelectedExpr)) { // Extracting just a field member isn't that useful. if (!isa(Member->getMemberDecl())) - return false; + return nullptr; TargetNode = getCallExpr(N); } if (!TargetNode || !canBeAssigned(TargetNode)) + return nullptr; + return TargetNode; +} + +/// Extracts an expression to the variable dummy +/// Before: +/// int x = 5 + 4 * 3; +/// ^^^^^ +/// After: +/// auto dummy = 5 + 4; +/// int x = dummy * 3; +class ExtractVariable : public Tweak { +public: + const char *id() const override final; + bool prepare(const Selection &Inputs) override; + Expected apply(const Selection &Inputs) override; + std::string title() const override { + return "Extract subexpression to variable"; + } + Intent intent() const override { return Refactor; } + +private: + // the expression to extract + std::unique_ptr Target; +}; +REGISTER_TWEAK(ExtractVariable) +bool ExtractVariable::prepare(const Selection &Inputs) { + // we don't trigger on empty selections for now + if (Inputs.SelectionBegin == Inputs.SelectionEnd) return false; - Target = llvm::make_unique(TargetNode, SM, Ctx); - return Target->isExtractable(); + const ASTContext &Ctx = Inputs.AST.getASTContext(); + const SourceManager &SM = Inputs.AST.getSourceManager(); + if (const SelectionTree::Node *N = + computeExtractedExpr(Inputs.ASTSelection.commonAncestor())) + Target = llvm::make_unique(N, SM, Ctx); + return Target && Target->isExtractable(); +} + +Expected ExtractVariable::apply(const Selection &Inputs) { + tooling::Replacements Result; + // FIXME: get variable name from user or suggest based on type + std::string VarName = "dummy"; + // insert new variable declaration + if (auto Err = Result.add(Target->insertDeclaration(VarName))) + return std::move(Err); + // replace expression with variable name + if (auto Err = Result.add(Target->replaceWithVar(VarName))) + return std::move(Err); + return Effect::applyEdit(Result); } } // namespace