Skip to content

Generated function prototype injected before declaration of custom parameter type for code in secondary .ino file #2946

Open
@per1234

Description

@per1234

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.

Arduino sketches may consist of multiple .ino files. This is useful because it allows large sketches to be split up to facilitate navigation of the codebase. These files are concatenated into a single file during sketch preprocessing. We expect that a sketch consisting of multiple .ino files will be functionally identical to a sketch that contains the same code in a single .ino file in the same order as the multi-file concatenation order.

🐛 Arduino CLI chooses an incorrect point to inject the generated function prototype under the following conditions:

  • The sketch contains a function with a parameter of a custom type defined in the sketch.
  • The function that depends on the custom type definition is in a supplemental .ino file.
  • The primary .ino file does not contain any function definitions.

This causes compilation to fail with a cryptic error.

To reproduce

$ arduino-cli version

arduino-cli  Version: git-snapshot Commit: 20c9dd446 Date: 2025-07-09T02:51:31Z

$ mkdir -p "/tmp/FooSketch"

$ echo '
enum foo_t {};
void bar(foo_t baz) {
  (void)baz;  // Fix "unused parameter" warning
}
void setup() {}
void loop() {}
' > "/tmp/FooSketch/FooSketch.ino"

$ arduino-cli compile --fqbn arduino:avr:uno "/tmp/FooSketch"

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.

🙂 The sketch compiles successfully because the void bar(foo_t baz) prototype was correctly injected after the definition of the foo_t type.

$ echo '
enum foo_t {};
' > "/tmp/FooSketch/FooSketch.ino"

$ echo '
void bar(foo_t baz) {
  (void)baz;  // Fix "unused parameter" warning
}
void setup() {}
void loop() {}
' > "/tmp/FooSketch/Supplemental.ino"

$ arduino-cli compile --fqbn arduino:avr:uno "/tmp/FooSketch"

C:\Users\per\AppData\Local\Temp\FooSketch\Supplemental.ino:2:10: error: variable or field 'bar' declared void
 void bar(foo_t baz) {
          ^~~~~
C:\Users\per\AppData\Local\Temp\FooSketch\Supplemental.ino:2:10: error: 'foo_t' was not declared in this scope

[...]

$ arduino-cli compile --fqbn arduino:avr:uno --preprocess "/tmp/FooSketch"

#include <Arduino.h>
#line 2 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\Supplemental.ino"
void bar(foo_t baz);
#line 5 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\Supplemental.ino"
void setup();
#line 6 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\Supplemental.ino"
void loop();
#line 0 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\Supplemental.ino"
#line 1 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\FooSketch.ino"

enum foo_t {};


#line 1 "C:\\Users\\per\\AppData\\Local\\Temp\\FooSketch\\Supplemental.ino"

void bar(foo_t baz) {
  (void)baz;  // Fix "unused parameter" warning
}
void setup() {}
void loop() {}

🐛 The compilation failed because the void bar(foo_t baz) prototype was correctly injected before the definition of the foo_t type.

Expected behavior

The system that is able to inject the prototype at the correct position in a single file sketch also works for the same code in a multi-file sketch.

Arduino CLI version

20c9dd4

Operating system

Windows

Operating system version

11

Additional context

There is another report of prototype injection before its type declaration dependency at #2696. However, the conditions under which this occurs are fairly distinct from the conditions under which #2696 occurs, so it is not clear to me whether they are both caused by the same bug.


Originally reported by @sterretjeToo at https://forum.arduino.cc/t/tabbed-sketches-suggestion-or-how-to/1393854/32

Workaround

Manually add a function prototype at the appropriate location:

enum foo_t {};
void bar(foo_t baz);  // Manually added function prototype to work around prototype generator bug.

Related

Issue checklist

  • I searched for previous reports in the issue tracker
  • I verified the problem still occurs when using the nightly build
  • My report contains all necessary details

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions