llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangd

Author: Christian Kandeler (ckandeler)

<details>
<summary>Changes</summary>

Selecting just the initializer of a variable declaration, e.g.
`auto A = [[func()]];`, was incorrectly offered for extraction,
producing a void-returning function called where a value is required:

```cpp
void extracted() { func(); }
void f() { auto A = extracted(); }
```

This doesn't compile (auto can't deduce from void; a fixed type would
likewise fail to convert from void).

`getEnclosingStmt()` ascends from the selected node to what should be
its enclosing statement, rejecting the case where that turns out to be
an `Expr` (meaning the selection is a subexpression of a larger
expression, not a genuine statement). A `VarDecl`'s initializer isn't
caught by that check, though: ascending from it lands on the `VarDecl`
itself, which is a `Decl`, not a `Stmt` or `Expr`. Broaden the check to
reject unless the ascended-to parent is a `Stmt` (note `Expr` is itself
a `Stmt` subtype in Clang's AST, so this still needs the separate
`Expr` check to catch the original case).

Assisted-by: Claude

---
Full diff: https://github.com/llvm/llvm-project/pull/225434.diff


2 Files Affected:

- (modified) clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp 
(+11-5) 
- (modified) clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp 
(+29) 


``````````diff
diff --git a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp 
b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
index a68b48536a22b..eb7a9faa65a81 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
+++ b/clang-tools-extra/clangd/refactor/tweaks/ExtractFunction.cpp
@@ -170,15 +170,21 @@ bool isRootStmt(const Node *N) {
 // VarDecls claim the entire selection range of the Declaration and DeclStmt
 // is always unselected.
 //
-// Returns null if the (possibly DeclStmt-adjusted) parent is an Expr: this
-// means Child is merely a subexpression of a larger expression rather than
-// a genuine standalone statement, e.g. selecting just the "3" in
-// `stream << 3;`, and extracting it would produce broken code.
+// Returns null if the (possibly DeclStmt-adjusted) parent isn't a "plain"
+// Stmt, or is an Expr: this means Child is merely a subexpression of a
+// larger expression or declaration rather than a genuine standalone
+// statement, and extracting it would produce broken code. This covers two
+// distinct cases:
+//  - Parent is an Expr, e.g. selecting just the "3" in `stream << 3;`
+//    (Child is a subexpression of a larger expression).
+//  - Parent isn't a Stmt at all, e.g. selecting just the "func()" in
+//    `auto A = func();` (Child is a VarDecl's initializer, so Parent is
+//    that VarDecl -- a Decl, not a Stmt).
 const Node *getEnclosingStmt(const Node *Child) {
   const Node *Parent = Child->Parent;
   if (Parent->ASTNode.get<DeclStmt>())
     Parent = Parent->Parent;
-  if (Parent->ASTNode.get<Expr>())
+  if (!Parent->ASTNode.get<Stmt>() || Parent->ASTNode.get<Expr>())
     return nullptr;
   return Parent;
 }
diff --git a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp 
b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
index 02e99b45cc263..c55fc17ae83e9 100644
--- a/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/tweaks/ExtractFunctionTests.cpp
@@ -748,6 +748,35 @@ TEST_F(ExtractFunctionTest, RangeBasedFor) {
               HasSubstr("extracted"));
 }
 
+TEST_F(ExtractFunctionTest, VarDeclInitializer) {
+  Context = File;
+  // The initializer of a variable declaration is not a discardable
+  // statement either: its value is required to initialize the variable, so
+  // replacing it with a call to a void-returning extracted function would
+  // not compile. Ascending from the initializer lands on the VarDecl itself (a
+  // Decl, not a Stmt or Expr), which the general "is this a genuine statement"
+  // check must also reject.
+  EXPECT_EQ(apply(R"cpp(
+    int func();
+    void f() { auto A = [[func()]]; }
+  )cpp"),
+            "unavailable");
+  // Same without `auto`: the type doesn't matter, only that a value is
+  // required.
+  EXPECT_EQ(apply(R"cpp(
+    int func();
+    void f() { int A = [[func()]]; }
+  )cpp"),
+            "unavailable");
+  // Sanity check: extracting the whole declaration statement (as opposed to
+  // just its initializer) is unaffected.
+  EXPECT_THAT(apply(R"cpp(
+    int func();
+    void f() { [[int A = func();]] }
+  )cpp"),
+              HasSubstr("extracted"));
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang

``````````

</details>


https://github.com/llvm/llvm-project/pull/225434
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to