From: Alex Lorenz Date: Tue, 14 Nov 2017 23:10:50 +0000 (+0000) Subject: [refactor][selection] canonicalize decl ref callee to the call expr X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1fb38815df38b0bde5284be09e33aa8750e36650;p=clang [refactor][selection] canonicalize decl ref callee to the call expr We would like to extract the full call when just the callee function is selected git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@318215 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Tooling/Refactoring/ASTSelection.cpp b/lib/Tooling/Refactoring/ASTSelection.cpp index ae8a3cb7c5..7123fc32ce 100644 --- a/lib/Tooling/Refactoring/ASTSelection.cpp +++ b/lib/Tooling/Refactoring/ASTSelection.cpp @@ -259,29 +259,64 @@ struct SelectedNodeWithParents { /// when it makes sense to do so. void canonicalize(); }; -} // end anonymous namespace -void SelectedNodeWithParents::canonicalize() { - const Stmt *S = Node.get().Node.get(); - assert(S && "non statement selection!"); - const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get(); - if (!Parent) - return; +enum SelectionCanonicalizationAction { KeepSelection, SelectParent }; + +/// Returns the canonicalization action which should be applied to the +/// selected statement. +SelectionCanonicalizationAction +getSelectionCanonizalizationAction(const Stmt *S, const Stmt *Parent) { // Select the parent expression when: // - The string literal in ObjC string literal is selected, e.g.: // @"test" becomes @"test" // ~~~~~~ ~~~~~~~ if (isa(S) && isa(Parent)) - Node = Parents.pop_back_val(); + return SelectParent; // The entire call should be selected when just the member expression - // that refers to the method is selected. + // that refers to the method or the decl ref that refers to the function + // is selected. // f.call(args) becomes f.call(args) // ~~~~ ~~~~~~~~~~~~ - else if (isa(S) && isa(Parent) && - cast(Parent)->getCallee() == S) - Node = Parents.pop_back_val(); + // func(args) becomes func(args) + // ~~~~ ~~~~~~~~~~ + else if (const auto *CE = dyn_cast(Parent)) { + if ((isa(S) || isa(S)) && + CE->getCallee()->IgnoreImpCasts() == S) + return SelectParent; + } // FIXME: Syntactic form -> Entire pseudo-object expr. - // FIXME: Callee -> Call. + return KeepSelection; +} + +} // end anonymous namespace + +void SelectedNodeWithParents::canonicalize() { + const Stmt *S = Node.get().Node.get(); + assert(S && "non statement selection!"); + const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get(); + if (!Parent) + return; + + // Look through the implicit casts in the parents. + unsigned ParentIndex = 1; + for (; (ParentIndex + 1) <= Parents.size() && isa(Parent); + ++ParentIndex) { + const Stmt *NewParent = + Parents[Parents.size() - ParentIndex - 1].get().Node.get(); + if (!NewParent) + break; + Parent = NewParent; + } + + switch (getSelectionCanonizalizationAction(S, Parent)) { + case SelectParent: + Node = Parents[Parents.size() - ParentIndex]; + for (; ParentIndex != 0; --ParentIndex) + Parents.pop_back(); + break; + case KeepSelection: + break; + } } /// Finds the set of bottom-most selected AST nodes that are in the selection diff --git a/unittests/Tooling/ASTSelectionTest.cpp b/unittests/Tooling/ASTSelectionTest.cpp index fc5186ea20..2f5df8f430 100644 --- a/unittests/Tooling/ASTSelectionTest.cpp +++ b/unittests/Tooling/ASTSelectionTest.cpp @@ -1056,4 +1056,30 @@ void dontSelectArgument(AClass &a) { }); } +TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) { + StringRef Source = R"( +void function(); + +void test() { + function(); +} + )"; + // Just 'function': + findSelectedASTNodesWithRange( + Source, {5, 3}, FileRange{{5, 3}, {5, 11}}, + [](SourceRange SelectionRange, Optional Node) { + EXPECT_TRUE(Node); + Node->dump(); + Optional SelectedCode = + CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); + EXPECT_TRUE(SelectedCode); + EXPECT_EQ(SelectedCode->size(), 1u); + EXPECT_TRUE(isa((*SelectedCode)[0])); + EXPECT_TRUE(isa( + SelectedCode->getParents()[SelectedCode->getParents().size() - 1] + .get() + .Node.get())); + }); +} + } // end anonymous namespace