]> granicus.if.org Git - clang/commitdiff
[refactor][selection] canonicalize decl ref callee to the call expr
authorAlex Lorenz <arphaman@gmail.com>
Tue, 14 Nov 2017 23:10:50 +0000 (23:10 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Tue, 14 Nov 2017 23:10:50 +0000 (23:10 +0000)
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

lib/Tooling/Refactoring/ASTSelection.cpp
unittests/Tooling/ASTSelectionTest.cpp

index ae8a3cb7c58b26f4a9dacf6e69d363ad4df3e531..7123fc32cec9b421a79bbfb7e17a0e7c6e1ccd11 100644 (file)
@@ -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<Stmt>();
-  assert(S && "non statement selection!");
-  const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>();
-  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<StringLiteral>(S) && isa<ObjCStringLiteral>(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<MemberExpr>(S) && isa<CXXMemberCallExpr>(Parent) &&
-           cast<CXXMemberCallExpr>(Parent)->getCallee() == S)
-    Node = Parents.pop_back_val();
+  //    func(args)  becomes  func(args)
+  //    ~~~~                 ~~~~~~~~~~
+  else if (const auto *CE = dyn_cast<CallExpr>(Parent)) {
+    if ((isa<MemberExpr>(S) || isa<DeclRefExpr>(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<Stmt>();
+  assert(S && "non statement selection!");
+  const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>();
+  if (!Parent)
+    return;
+
+  // Look through the implicit casts in the parents.
+  unsigned ParentIndex = 1;
+  for (; (ParentIndex + 1) <= Parents.size() && isa<ImplicitCastExpr>(Parent);
+       ++ParentIndex) {
+    const Stmt *NewParent =
+        Parents[Parents.size() - ParentIndex - 1].get().Node.get<Stmt>();
+    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
index fc5186ea208c73b65e771b55c4f3d2a2f55f8ee2..2f5df8f430096c470799234cd1f302a36b728aa4 100644 (file)
@@ -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<SelectedASTNode> Node) {
+        EXPECT_TRUE(Node);
+        Node->dump();
+        Optional<CodeRangeASTSelection> SelectedCode =
+            CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
+        EXPECT_TRUE(SelectedCode);
+        EXPECT_EQ(SelectedCode->size(), 1u);
+        EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
+        EXPECT_TRUE(isa<CompoundStmt>(
+            SelectedCode->getParents()[SelectedCode->getParents().size() - 1]
+                .get()
+                .Node.get<Stmt>()));
+      });
+}
+
 } // end anonymous namespace