arduino / arduino-cli

Arduino command line tool
https://arduino.github.io/arduino-cli/latest/
GNU General Public License v3.0
4.32k stars 373 forks source link

No generated prototype for function with multi-line parameters in unsaved sketch #1800

Open per1234 opened 2 years ago

per1234 commented 2 years ago

Describe the problem

In order to make it easier for beginners to get started with writing Arduino sketches, and for the convenience of all users, Arduino CLI automatically generates and adds prototypes for functions defined in a .ino file of a sketch.

An Arduino IDE editor tab can be in one of two states:

Function prototypes are not generated under the following conditions:

🐛 Compilation of the sketch fails spuriously.

To reproduce

  1. Select File > Preferences from the Arduino IDE menus.
  2. Uncheck the box next to "☐ Auto save".
  3. Click the "OK" button.
  4. Create the following sketch:
    void setup() {
     foo(1, 2);
    }
    void loop() {}
    void foo(int bar,
            int baz) {}

    :warning: Do not save.

  5. Select Sketch > Verify/Compile from the Arduino IDE menus.

:bug: Compilation fails unexpectedly:

C:\Users\per\Documents\Arduino\sketch_dec11b\sketch_dec11b.ino: In function 'void setup()':
C:\Users\per\Documents\Arduino\sketch_dec11b\sketch_dec11b.ino:2:3: error: 'foo' was not declared in this scope
   // put your setup code here, to run once:
   ^~~
C:\Users\per\Documents\Arduino\sketch_dec11b\sketch_dec11b.ino:2:3: note: suggested alternative: 'feof'
   // put your setup code here, to run once:
   ^~~
   feof
Compilation error: exit status 1}

(Outdated sketch source in compilation output tracked here: https://github.com/arduino/arduino-cli/issues/1185)

:bug: Note that the preprocessed sketch program does not contain a function prototype for void foo(int, int):

#include <Arduino.h>
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void setup();
#line 4 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void loop();
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void setup() {
  foo(1, 2);
}
void loop() {}
void foo(int bar,
         int baz) {}

If you compile the identical sketch while it is in a saved state, the function prototype is generated as expected 🙂, so this is not about a fundamental limitation of the prototype generation system of Arduino CLI:

#include <Arduino.h>
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void setup();
#line 4 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void loop();
#line 5 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void foo(int bar, int baz);
#line 1 "C:\\Users\\per\\Documents\\Arduino\\sketch_dec11b\\sketch_dec11b.ino"
void setup() {
  foo(1, 2);
}
void loop() {}
void foo(int bar,
         int baz) {}

Expected behavior

Prototypes for functions in .ino files are always generated.

Arduino CLI version

Original report

0.20.2, bundled with Arduino IDE a8ae0bb

Last verified with

0.36.0-rc.1, bundled with Arduino IDE aa9b10d

Operating system

Windows

Operating system version

Additional context

This issue does not occur in Arduino IDE 1.8.16


Originally reported by @UKHeliBob at https://forum.arduino.cc/t/auto-format-using-clang-format-file-produces-code-that-cannot-be-compiled/934610

Related

Workaround

Add a prototype for the function in the sketch before the first reference:

void foo(int bar, int baz);
void setup() {
  foo(1, 2);
}
void loop() {}
void foo(int bar,
         int baz) {}

- OR -

Save the sketch before compiling.

Issue checklist

kittaakos commented 2 years ago

I went through the steps and found the followings:

I did:

https://user-images.githubusercontent.com/1405703/171666125-e76cc428-3603-4ff7-9d4b-95eb1e766c84.mp4

First off, the LS unexpectedly complains about the missing foo when I paste the snippet. The IDE2 is not involved in this part. If absent foo is a problem for both the LS and the CLI compiler, the bug could be outside the IDE2 code-base.

After the compile request, the IDE2 frontend (FE) correctly detects the dirty state and builds the following source-override map:

{
    "file:///private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a/sketch_jun2a.ino": "void setup() {\n     foo(1, 2);\n   }\n   void loop() {}\n   void foo(int bar,\n            int baz) {}"
}

The FE sends the source-override map as part of the compile request to the backend (BE). The BE builds this JSON object for the gRPC request and sends it to the CLI:

{
    "instance": {
        "id": 1
    },
    "fqbn": "arduino:avr:uno",
    "sketchPath": "/private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a",
    "showProperties": false,
    "preprocess": false,
    "buildCachePath": "",
    "buildPath": "",
    "buildPropertiesList": [],
    "warnings": "none",
    "verbose": false,
    "quiet": false,
    "vidPid": "",
    "jobs": 0,
    "librariesList": [],
    "optimizeForDebug": false,
    "exportDir": "",
    "clean": false,
    "createCompilationDatabaseOnly": false,
    "sourceOverrideMap": [
        [
            "sketch_jun2a.ino",
            "   void setup() {\n     foo(1, 2);\n   }\n   void loop() {}\n   void foo(int bar,\n            int baz) {}"
        ]
    ],
    "libraryList": []
}

FE: "void setup() {\n foo(1, 2);\n }\n void loop() {}\n void foo(int bar,\n int baz) {}" BE: " void setup() {\n foo(1, 2);\n }\n void loop() {}\n void foo(int bar,\n int baz) {}"

The FE and BE content difference is odd but must not be crucial. 👆 I will investigate why there are two leading space characters in the BE version of the content, but I doubt this is the root cause of the defect.

/private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a/sketch_jun2a.ino: In function 'void setup()':
/private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a/sketch_jun2a.ino:2:6: error: 'foo' was not declared in this scope
   // put your setup code here, to run once:
      ^~~
/private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a/sketch_jun2a.ino:2:6: note: suggested alternative: 'feof'
   // put your setup code here, to run once:
      ^~~
      feof

Compilation error: exit status 1

Update:

I will investigate why there are two leading space characters in the BE version of the content, but I doubt this is the root cause of the defect.

The FE and BE contents are the same for the second attempt. I must have made a mistake while recording the screencast or documenting the step. I leave the original comment untouched, but below is the JSON of the second attempt.

{
    "instance": {
        "id": 1
    },
    "fqbn": "arduino:avr:uno",
    "sketchPath": "/private/var/folders/z1/xkw1yh5n7rz4n8djprp1mdn80000gn/T/.arduinoIDE-unsaved202252-19670-84ll7l.icynr/sketch_jun2a",
    "showProperties": false,
    "preprocess": false,
    "buildCachePath": "",
    "buildPath": "",
    "buildPropertiesList": [],
    "warnings": "none",
    "verbose": false,
    "quiet": false,
    "vidPid": "",
    "jobs": 0,
    "librariesList": [],
    "optimizeForDebug": false,
    "exportDir": "",
    "clean": false,
    "createCompilationDatabaseOnly": false,
    "sourceOverrideMap": [
        [
            "sketch_jun2a.ino",
            "void setup() {\n  foo(1, 2);\n}\nvoid loop() {}\nvoid foo(int bar,\n         int baz) {}"
        ]
    ],
    "libraryList": []
}
per1234 commented 2 years ago

Thanks so much for taking the time to investigate this @kittaakos!

Using the insight you shared, I am now able to reproduce the issue using Arduino CLI directly, so can verify that this is not anything related to the Arduino IDE code base. I have moved the issue to the appropriate repository.

Set up

$ arduino-cli version
arduino-cli.exe  Version: 0.25.0-rc1 Commit: 63b53c0f Date: 2022-07-12T01:46:05Z

$ mkdir /tmp/SourceOverridePrototypeGenBug

$ printf "void setup() {}\nvoid loop() {}\n" > "/tmp/SourceOverridePrototypeGenBug/SourceOverridePrototypeGenBug.ino"

$ arduino-cli daemon

Demo

Use grpcurl to run the following commands:

$ grpcurl \
  -plaintext \
  -import-path ./rpc \
  -proto cc/arduino/cli/commands/v1/commands.proto \
  127.0.0.1:50051 \
  cc.arduino.cli.commands.v1.ArduinoCoreService.Create

{
  "instance": {
    "id": 1
  }
}

$ grpcurl \
  -plaintext \
  -import-path ./rpc \
  -proto cc/arduino/cli/commands/v1/commands.proto \
  -d '{"instance": {"id": 1}}' \
  127.0.0.1:50051 \

$ grpcurl \
  -plaintext \
  -import-path ./rpc \
  -proto cc/arduino/cli/commands/v1/commands.proto \
  -d '{"instance": {"id": 1}, "fqbn": "arduino:avr:uno", "sketch_path": "/tmp/SourceOverridePrototypeGenBug", "source_override": [{"key": "SourceOverridePrototypeGenBug.ino", "value": "void setup() {\n  foo(1, 2);\n}\nvoid loop() {}\nvoid foo(int bar,\n         int baz) {}"}]}' \
  127.0.0.1:50051 \
  cc.arduino.cli.commands.v1.ArduinoCoreService.Compile

[...]
{
  "errStream": "QzpcVXNlcnNccGVyXEFwcERhdGFcTG9jYWxcVGVtcFxTb3VyY2VPdmVycmlkZVByb3RvdHlwZUdlbkJ1Z1xTb3VyY2VPdmVycmlkZVByb3RvdHlwZUdlbkJ1Zy5pbm86IEluIGZ1bmN0aW9uICd2b2lkIHNldHVwKCknOg0KQzpcVXNlcnNccGVyXEFwcERhdGFcTG9jYWxcVGVtcFxTb3VyY2VPdmVycmlkZVByb3RvdHlwZUdlbkJ1Z1xTb3VyY2VPdmVycmlkZVByb3RvdHlwZUdlbkJ1Zy5pbm86MjozOiBlcnJvcjogJ2Zvbycgd2FzIG5vdCBkZWNsYXJlZCBpbiB0aGlzIHNjb3BlDQogdm9pZCBsb29wKCkge30NCiAgIF5+fg0K"
}
[...]
ERROR:
  Code: Internal
  Message: exit status 1

$ grpcurl \
  -plaintext \
  -import-path ./rpc \
  -proto cc/arduino/cli/commands/v1/commands.proto \
  -d '{"instance": {"id": 1}, "fqbn": "arduino:avr:uno", "sketch_path": "/tmp/SourceOverridePrototypeGenBug", "source_override": [{"key": "SourceOverridePrototypeGenBug.ino", "value": "void setup() {\n  foo(1, 2);\n}\nvoid loop() {}\nvoid foo(int bar, int baz) {}"}]}' \
  127.0.0.1:50051 \
  cc.arduino.cli.commands.v1.ArduinoCoreService.Compile

[...]

$ echo "$?"
0

Notes

The sketch code compiles fine when it is saved to disk:

$ printf "void setup() {\n  foo(1, 2);\n}\nvoid loop() {}\nvoid foo(int bar,\n         int baz) {}" > "/tmp/SourceOverridePrototypeGenBug/SourceOverridePrototypeGenBug.ino"

$ arduino-cli compile --fqbn arduino:avr:uno "/tmp/SourceOverridePrototypeGenBug"
Sketch uses 444 bytes (1%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.

Used platform Version Path
arduino:avr   1.8.99  C:\Users\per\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.99