summaryrefslogtreecommitdiffhomepage
path: root/src/checkauto.l
blob: a8f14abb1ffc30b6d07c644f8da0bd56b22fd546 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/* Check for illegal return/break/continue usage inside a STC-lib c_auto* block (RAII).
 * Copyright Tyge Løvset, (c) 2023.
 */
%{
#include <stdbool.h>
enum { LOOP=1<<0, AUTO=1<<1 };
enum { NORMAL, BRACES, BRACESDONE };
static int braces_lev = 0, block_lev = 0;
static int state = NORMAL;
static unsigned int block[64] = {0}, block_type = 0;
const char* fname;
int errors = 0, warnings = 0;
%}

ID [_a-zA-Z][_a-zA-Z0-9]*
STR \"([^"\\]|\\.)*\"

%option never-interactive noyymore noyywrap nounistd
%x cmt
%x prep

%%
\/\/.*          ; // line cmt
\/\*            BEGIN(cmt);
<cmt>\n         ++yylineno;
<cmt>\*\/       BEGIN(INITIAL);
<cmt>.          ;
^[ \t]*#.*\\\n  { ++yylineno; BEGIN(prep); }
<prep>.*\\\n    ++yylineno;
<prep>.*        BEGIN(INITIAL);
^[ \t]*#.*      ;
{STR}           ;
'\\?.'          ;
c_foreach       |
c_forpair       |
c_forrange      |
c_forfilter     |
c_formatch      |
c_fortoken      |
for             |
while           |
switch          { block_type |= LOOP; state = BRACES; }
do              { block_type |= LOOP; state = BRACESDONE; }
c_defer         |
c_scope         |
c_with          |
c_auto          { block_type = AUTO; state = BRACES; }
\(              { if (state == BRACES) ++braces_lev; }
\)              { if (state == BRACES && --braces_lev == 0) {
                    state = BRACESDONE;
                  }
                }
if              { if (state == BRACESDONE) {
                    if (block_type == AUTO) {
                      printf("%s:%d: warning: 'if' after c_auto* not enclosed in curly braces.\n"
                             "    Make sure to enclose 'if - else' statement in { } after c_auto*.\n",
                             fname, yylineno);
                      ++warnings;                    
                    }
                    state = BRACES;
                  }
                }
;[ \t]*else     ;
;               { if (state == BRACESDONE) {
                    block_type = block[block_lev];
                    state = NORMAL;
                  }
                }
\{              { if (state != BRACES) { block[++block_lev] = block_type; state = NORMAL; } }
\}              { if (state != BRACES) block_type = block[--block_lev]; }
return          { if (block_type == AUTO) {
                    printf("%s:%d: error: 'return' used inside a c_auto* scope.\n"
                           "    Use 'continue' to exit the current c_auto* scope before return.\n"
                           , fname, yylineno);
                    ++errors;
                  } else if (block_type & AUTO) {
                    printf("%s:%d: error: 'return' used in a loop inside a c_auto* scope.\n"
                           "    Use 'break' to exit loop, then 'continue' to exit c_auto*.\n"
                           , fname, yylineno);
                    ++errors;
                  }
                }
break           { if (block_type == AUTO) {
                    printf("%s:%d: error: 'break' used inside a c_auto* scope.\n"
                           "    Use 'continue' to exit the current c_auto* scope.\n"
                           , fname, yylineno);
                    ++errors;
                  }
                }
{ID}            ;
\n              ++yylineno;
.               ;

%%

#include <string.h>

int main(int argc, char **argv)
{
    if (argc == 1 || strcmp(argv[1], "--help") == 0) {
        printf("usage: %s [--help] {C-file... | -}\n", argv[0]);
        return 0;
    }

    for (int i=1; i<argc; ++i) {
        if (strcmp(argv[i], "-") == 0) {
            fname = "<stdin>";
            yyin = stdin;
        } else {
            fname = argv[i];
            yyin = fopen(fname, "r");
        }
        yylineno = 1;
        braces_lev = 0, block_lev = 0;
        block_type = 0, state = NORMAL;

        yylex();

        fclose(yyin);
    }

    if (errors + warnings)
        printf("%d error%s, %d warning%s.\n", errors, errors == 1? "":"s",
                                              warnings, warnings == 1? "":"s");
    return errors;
}