In the C++ programming language Arduino is based on, a name must be declared before it is referenced. Since a definition is also a declaration, you can accomplish this by ordering the definitions so that each name is defined before it is referenced in the program (as you have done). However, that approach is not always convenient for the organization of the program, and sometimes it's not even possible.
The alternative is to use function prototypes, where you declare only the signature of the function. So you only need to put all the prototypes at the top of your program and then you can arrange the definitions in any way you like.
This is a complex subject for the people Arduino is intended to make embedded systems accessible to, so the approach Arduino used is to automatically generate prototypes for all the functions in the .ino/.pde files of the sketch which don't already have a prototype, inserting those generated prototypes before the code is compiled as C++.
That prototype generation system works perfectly 99.99% of the time, but it turns out that it's actually a very complex task. So in some cases the prototype generation system doesn't get it right, which results in there being bugs in invisible code.
If you turn on verbose output during compilation and then check the last commands in the black console pane at the bottom of the Arduino IDE window after compiling, you'll see the path to the temporary build folder that contains the preprocessed sketch files. This is what your sketch looks like after preprocessing:
#include <Arduino.h>
#line 1 "E:\\scope_test\\scope_test.ino"
#line 1 "E:\\scope_test\\scope_test.ino"
void setup();
#line 6 "E:\\scope_test\\scope_test.ino"
void loop();
#line 6 "E:\\scope_test\\second_tab.ino"
void function(str_t* s);
#line 1 "E:\\scope_test\\scope_test.ino"
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
#line 1 "E:\\scope_test\\second_tab.ino"
typedef struct str {
char c;
str* next;
} str_t;
void function(str_t* s) {
return;
}
This line is the generated prototype for your "function" function:
void function(str_t* s);
You can see it was added above the declaration of the str_t type. This is the cause of the error.
The sketch preprocessor only generates prototypes when the sketch author hasn't already provided their own, so the workaround for this issue is to add the prototype for your "function" function to your sketch at the appropriate location, after the declaration of the str_t type. Something like this:
typedef struct str {
char c;
str* next;
} str_t;
void function(str_t* s);