Skip to content

[clangd] Handle lambda scopes inside Node::getDeclContext() #76329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 11, 2024

Conversation

zyn0217
Copy link
Contributor

@zyn0217 zyn0217 commented Dec 24, 2023

We used to consider the DeclContext for selection nodes inside a lambda as the enclosing scope of the lambda expression, rather than the lambda itself.

For example,

void foo();
auto lambda = [] {
  return ^foo();
};

where N is the selection node for the expression foo(), N.getDeclContext() returns the TranslationUnitDecl previously, which IMO is wrong, since the method operator() of the lambda is closer.

Incidentally, this fixes a glitch in add-using-declaration tweaks. (Thanks @HighCommander4 for the test case.)

@llvmbot
Copy link
Member

llvmbot commented Dec 24, 2023

@llvm/pr-subscribers-clangd

Author: Younan Zhang (zyn0217)

Changes

We used to consider the DeclContext for selection nodes inside a lambda as the enclosing scope of the lambda expression, rather than the lambda itself.

For example,

void foo();
auto lambda = [] {
  return ^foo();
};

where N is the selection node for the expression foo(), N.getDeclContext() returns the TranslationUnitDecl previously, which IMO is wrong, since the RecordDecl of the lambda is closer.

Seemingly, this affects nothing currently.


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

2 Files Affected:

  • (modified) clang-tools-extra/clangd/Selection.cpp (+3)
  • (modified) clang-tools-extra/clangd/unittests/SelectionTests.cpp (+13)
diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp
index 8c6d5750ecefdb..a7413485b04ac3 100644
--- a/clang-tools-extra/clangd/Selection.cpp
+++ b/clang-tools-extra/clangd/Selection.cpp
@@ -1113,6 +1113,9 @@ const DeclContext &SelectionTree::Node::getDeclContext() const {
           return *DC;
       return *Current->getLexicalDeclContext();
     }
+    if (auto *LE = CurrentNode->ASTNode.get<LambdaExpr>())
+      if (CurrentNode != this)
+        return *LE->getLambdaClass();
   }
   llvm_unreachable("A tree must always be rooted at TranslationUnitDecl.");
 }
diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp
index 4c019a1524f3c3..cdb50dfc1c3095 100644
--- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp
@@ -880,6 +880,19 @@ TEST(SelectionTest, DeclContextIsLexical) {
   }
 }
 
+TEST(SelectionTest, DeclContextLambda) {
+  llvm::Annotations Test(R"cpp(
+    void foo();
+    auto lambda = [] {
+      return $1^foo();
+    };
+  )cpp");
+  auto AST = TestTU::withCode(Test.code()).build();
+  auto ST = SelectionTree::createRight(AST.getASTContext(), AST.getTokens(),
+                                       Test.point("1"), Test.point("1"));
+  EXPECT_FALSE(ST.commonAncestor()->getDeclContext().isTranslationUnit());
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang

@zyn0217
Copy link
Contributor Author

zyn0217 commented Jan 1, 2024

Ping. And Happy New Year 2024!

We used to consider the DeclContext for selection nodes inside
a lambda as the scope of the lambda expression locates in.

For example,

```cpp
void foo();
auto lambda = [] {
  return ^foo();
};
```

where N is the selection node for the expression `foo()`,
`N.getDeclContext()` returns the TranslationUnitDecl previously,
which IMO is wrong, since the RecordDecl of the lambda is closer.
Copy link
Collaborator

@HighCommander4 HighCommander4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the change looks good to me!

I went through the existing callers of Node::getDeclContext(), and I was able to construct a test case where the patch actually changes behaviour (in a good way):

namespace NS {
void unrelated();
void foo();
}

auto L = [] {
  using NS::unrelated;
  NS::foo();
};

Here, if the "add using-declaration" code action is invoked on NS::foo, before this patch the result is:

namespace NS {
void unrelated();
void foo();
}

using NS::foo;

auto L = [] {
  using NS::unrelated;
  foo();
};

but after this patch the result is:

namespace NS {
void unrelated();
void foo();
}

auto L = [] {
  using NS::foo;
  using NS::unrelated;
  foo();
};

Let's add this test case to AddUsingTests while we're at it.

@HighCommander4
Copy link
Collaborator

Happy New Year 2024!

You too!

@zyn0217
Copy link
Contributor Author

zyn0217 commented Jan 11, 2024

Sorry for being silent for a few days, I've been a bit occupied recently and hopefully I could get back to the heuristic patch for templates soon.

I went through the existing callers of Node::getDeclContext(), and I was able to construct a test case where the patch actually changes behaviour (in a good way):

Thank you for the exploration! I've added the test case, although it appears a bit odd:

auto L = [] {
  using NS::foo;using NS::unrelated; // Shouldn't it be placed on a new line?
  foo();
};)cpp

However, I've seen a similar tweaking style (e.g. namespace {using one::two::ff;) around these tests, so maybe this is intended?

@zyn0217 zyn0217 force-pushed the selection-tree-lambda branch from 18274ed to e289f7f Compare January 11, 2024 05:57
@zyn0217
Copy link
Contributor Author

zyn0217 commented Jan 11, 2024

(I'll land it once the CI turns green.)

@HighCommander4
Copy link
Collaborator

Shouldn't it be placed on a new line?

I think what may be happening here is that the refactorings try to avoid duplicating the work of the formatter, and just generate syntactically valid code that the formatter will then format according to the codebase's clang-format settings.

@zyn0217
Copy link
Contributor Author

zyn0217 commented Jan 11, 2024

The CI failure on windows seems irrelevant, so I'm landing it anyway.

FAILED: tools/clang/tools/extra/clangd/CompletionModel.h tools/clang/tools/extra/clangd/CompletionModel.cpp
cmd.exe /C "cd /D C:\ws\src\build\tools\clang\tools\extra\clangd && C:\Python39\python.exe C:/ws/src/clang-tools-extra/clangd/quality/CompletionModelCodegen.py --model C:/ws/src/clang-tools-extra/clangd/quality/model --output_dir C:/ws/src/build/tools/clang/tools/extra/clangd --filename CompletionModel --cpp_class clang::clangd::Example"
C:\Python39\python.exe: can't open file 'C:\ws\src\clang-tools-extra\clangd\quality\CompletionModelCodegen.py': [Errno 22] Invalid argument

@zyn0217 zyn0217 merged commit 9ef2ac3 into llvm:main Jan 11, 2024
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
We used to consider the `DeclContext` for selection nodes inside a
lambda as the enclosing scope of the lambda expression, rather than the
lambda itself.

For example,

```cpp
void foo();
auto lambda = [] {
  return ^foo();
};
```

where `N` is the selection node for the expression `foo()`,
`N.getDeclContext()` returns the `TranslationUnitDecl` previously, which
IMO is wrong, since the method `operator()` of the lambda is closer.

Incidentally, this fixes a glitch in add-using-declaration tweaks.
(Thanks @HighCommander4 for the test case.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants