arduino / arduino-language-server

An Arduino Language Server based on Clangd to Arduino code autocompletion
GNU Affero General Public License v3.0
133 stars 11 forks source link

"Go to Definition" sometimes leads to declaration instead of definition #167

Open ZedOud opened 1 year ago

ZedOud commented 1 year ago

Describe the problem

On the right-click menu, we have two references to "Definitions": Go to Definition Peek > Peek Definition

However, these options, respectively, brings you to the subject's declaration in the relevant header file or shows a preview of its declaration in the relevant header.

Rarely, though, a "declaration that is also a definition" might be seen in the header (to loosely quote [this reference])(https://learn.microsoft.com/en-us/cpp/cpp/declarations-and-definitions-cpp?view=msvc-170#definitions). Though, except for one-liners, this is rare in the header. We can safely state that this feature only, at least functionally, results in declarations.

I thought I was doing something incorrectly: something wrong with my board packages, or with my libraries. However this occurs many boards I tried, even with the Arduino Uno, and it occurs for the default libraries and other widely used libraries.

I figured I must be operating the feature incorrectly, as I assumed I should be able to reach the Definition.

After a few hours investigating this, and looking through the Issues, I can only conclude that this is a typo, as either:

Thank you for your time. I hope this minor bug can be resolved.

To reproduce

Right-click a symbol, especially a function. "Go to Definition" Arrive in a header file most likely viewing a declaration.

Expected behavior

I would expect to see a definition, not just a declaration, which would take me to a .cpp file instead of a .h file.

Arduino IDE version

2.2.2-nightly-20230916

Operating system

macOS

Operating system version

13.5.2

Additional context

Additional reports

Issue checklist

per1234 commented 1 year ago

Hi @ZedOud. Please add a comment here that provides detailed instructions I can follow to reproduce the problem.


Here is an example of the sort of instructions I need:

  1. Create the following sketch:
    void setup() {
     pinMode(LED_BUILTIN, OUTPUT);
    }
    void loop() {}
  2. Select Tools > Board > Arduino AVR Boards > Arduino Uno from the Arduino IDE menus.
  3. Wait for the sketch processing to finish, as indicated by the disappearance of the messages from the left side of the status bar.
  4. Right click on the pinMode call at line 2. A context menu will open.
  5. Select "Go to Definition" from the context menu.

If I follow the above instructions, I am taken to the definition of pinMode in wiring_digital.c, not to the declaration in Arduino.h. So everything is working as expected for me with that procedure.


Something to note is that the clangd C++ language server that provides the "context aware" features like "Go to Definition" for Arduino IDE falls back to the declaration when the definition is not available:

https://github.com/llvm/llvm-project/blob/llvmorg-14.0.0/clang-tools-extra/clangd/ClangdLSPServer.cpp#L1111

An example of when the definition would not be available is when the implementation is in a precompiled object file. This is done in some cores and libraries in order to reduce the initial compilation time which might be quite considerable in the case of the very significant quantities of code that are present in some of the more advanced cores (e.g., Arduino Mbed OS boards) and libraries (e.g., Arduino_TensorFlowLite).


There is also a "toggle" implemented that causes it to switch between the definition and declaration:

https://github.com/llvm/llvm-project/blob/llvmorg-14.0.0/clang-tools-extra/clangd/ClangdLSPServer.cpp#L1079-L1097

I'm actually not experiencing that toggling behavior when following the steps I describe above. I consistently get the definition even when I trigger a "Go to Definition" repeatedly on the pinMode call. I do remember encountering it at some point in the past.

ZedOud commented 1 year ago

Hi @per1234 This is fascinating. There is too much variation and not enough signals for me to understand the pattern of failure. I had tested many examples, but none of the included/default ones like pinMode. I attempted a few more examples: library -> object -> destination

These were tested with the Uno selected. It seems like only a very limited number of lookups go to the definition. Each of the libraries I tested have their .h and .cpp or .c files included, I didn't see any of them include the mentioned precombiled binary files, unless they are not stored with the rest of the library's files?

P.S. accidentally misclicked it as "closed"

ZedOud commented 1 year ago

I don't know if I can provide any more information. It seems evident that most libraries, and all of those I've tried, move to the declaration.

The only exception seems to be for the global namespace, which I'm not sure if that is what it is precisely called in Arduino C++, but I mean keywords like pinMode.

per1234 commented 1 year ago

The problem seems to be specific to libraries. Here is a minimal reproduction:

Sketch:

#include <Foo.h>
void setup() {
  foo();
}
void loop() {}

Library:

Foo.h:

void foo();

Foo.cpp:

void foo() {}

Trigger a "Go to Definition" (textDocument/definition) on the foo call in the sketch and you are taken to the foo declaration in Foo.h.

But if you move Foo.h and Foo.cpp to the sketch folder and adjust the #include directive in the sketch accordingly (#include "Foo.h") then the same operation takes you to the definition of foo in Foo.cpp as expected.