herb 0.8.7-x86_64-linux-gnu → 0.8.8-x86_64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +7 -0
- data/config.yml +12 -0
- data/ext/herb/extconf.rb +0 -4
- data/ext/herb/nodes.c +17 -9
- data/lib/herb/3.0/herb.so +0 -0
- data/lib/herb/3.1/herb.so +0 -0
- data/lib/herb/3.2/herb.so +0 -0
- data/lib/herb/3.3/herb.so +0 -0
- data/lib/herb/3.4/herb.so +0 -0
- data/lib/herb/4.0/herb.so +0 -0
- data/lib/herb/ast/nodes.rb +32 -8
- data/lib/herb/engine/debug_visitor.rb +1 -1
- data/lib/herb/version.rb +1 -1
- data/lib/herb.rb +30 -3
- data/sig/herb/ast/nodes.rbs +16 -8
- data/sig/serialized_ast_nodes.rbs +4 -0
- data/src/analyze.c +110 -15
- data/src/analyze_helpers.c +80 -12
- data/src/analyzed_ruby.c +1 -0
- data/src/ast_nodes.c +12 -4
- data/src/ast_pretty_print.c +52 -0
- data/src/include/analyze_helpers.h +7 -0
- data/src/include/analyzed_ruby.h +1 -0
- data/src/include/ast_nodes.h +8 -4
- data/src/include/location.h +4 -0
- data/src/include/prism_helpers.h +6 -0
- data/src/include/version.h +1 -1
- data/src/location.c +16 -0
- data/src/prism_helpers.c +188 -0
- data/templates/ext/herb/nodes.c.erb +2 -0
- data/templates/java/nodes.c.erb +3 -1
- data/templates/java/org/herb/ast/Nodes.java.erb +11 -0
- data/templates/javascript/packages/core/src/nodes.ts.erb +14 -0
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +9 -0
- data/templates/lib/herb/ast/nodes.rb.erb +4 -0
- data/templates/rust/src/ast/nodes.rs.erb +10 -0
- data/templates/rust/src/nodes.rs.erb +4 -0
- data/templates/src/ast_nodes.c.erb +4 -0
- data/templates/src/ast_pretty_print.c.erb +14 -0
- data/templates/template.rb +11 -0
- data/templates/wasm/nodes.cpp.erb +6 -0
- data/vendor/prism/include/prism/version.h +2 -2
- data/vendor/prism/src/prism.c +48 -27
- data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
- data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
- data/vendor/prism/templates/lib/prism/compiler.rb.erb +2 -2
- data/vendor/prism/templates/lib/prism/node.rb.erb +24 -1
- data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
- data/vendor/prism/templates/lib/prism/visitor.rb.erb +2 -2
- data/vendor/prism/templates/sig/prism/node.rbs.erb +1 -0
- metadata +1 -1
data/src/analyze.c
CHANGED
|
@@ -43,6 +43,7 @@ static analyzed_ruby_T* herb_analyze_ruby(hb_string_T source) {
|
|
|
43
43
|
search_rescue_nodes(analyzed);
|
|
44
44
|
search_ensure_nodes(analyzed);
|
|
45
45
|
search_yield_nodes(analyzed->root, analyzed);
|
|
46
|
+
search_then_keywords(analyzed->root, analyzed);
|
|
46
47
|
search_block_closing_nodes(analyzed);
|
|
47
48
|
|
|
48
49
|
if (!analyzed->valid) { pm_visit_node(analyzed->root, search_unclosed_control_flows, analyzed); }
|
|
@@ -112,6 +113,14 @@ typedef struct {
|
|
|
112
113
|
const uint8_t* source_start;
|
|
113
114
|
} location_walker_context_t;
|
|
114
115
|
|
|
116
|
+
static bool control_type_is_block(control_type_t type) {
|
|
117
|
+
return type == CONTROL_TYPE_BLOCK;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static bool control_type_is_yield(control_type_t type) {
|
|
121
|
+
return type == CONTROL_TYPE_YIELD;
|
|
122
|
+
}
|
|
123
|
+
|
|
115
124
|
static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* data) {
|
|
116
125
|
if (!node) { return true; }
|
|
117
126
|
|
|
@@ -194,14 +203,12 @@ static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* da
|
|
|
194
203
|
|
|
195
204
|
if (call->block != NULL && call->block->type == PM_BLOCK_NODE) {
|
|
196
205
|
pm_block_node_t* block_node = (pm_block_node_t*) call->block;
|
|
197
|
-
|
|
198
|
-
bool has_do_opening =
|
|
199
|
-
|
|
200
|
-
bool
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (has_do_opening || (has_brace_opening && !has_closing_location)) {
|
|
206
|
+
|
|
207
|
+
bool has_do_opening = is_do_block(block_node->opening_loc);
|
|
208
|
+
bool has_brace_opening = is_brace_block(block_node->opening_loc);
|
|
209
|
+
bool has_valid_brace_closing = is_closing_brace(block_node->closing_loc);
|
|
210
|
+
|
|
211
|
+
if (has_do_opening || (has_brace_opening && !has_valid_brace_closing)) {
|
|
205
212
|
current_type = CONTROL_TYPE_BLOCK;
|
|
206
213
|
keyword_offset = (uint32_t) (node->location.start - context->source_start);
|
|
207
214
|
}
|
|
@@ -221,7 +228,17 @@ static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* da
|
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
if (keyword_offset != UINT32_MAX) {
|
|
224
|
-
|
|
231
|
+
bool should_update = !result->found;
|
|
232
|
+
|
|
233
|
+
if (result->found) {
|
|
234
|
+
if (control_type_is_block(current_type) && control_type_is_yield(result->type)) {
|
|
235
|
+
should_update = true;
|
|
236
|
+
} else if (!(control_type_is_yield(current_type) && control_type_is_block(result->type))) {
|
|
237
|
+
should_update = keyword_offset < result->offset;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (should_update) {
|
|
225
242
|
result->type = current_type;
|
|
226
243
|
result->offset = keyword_offset;
|
|
227
244
|
result->found = true;
|
|
@@ -319,6 +336,34 @@ static AST_NODE_T* create_control_node(
|
|
|
319
336
|
token_T* content = erb_node->content;
|
|
320
337
|
token_T* tag_closing = erb_node->tag_closing;
|
|
321
338
|
|
|
339
|
+
location_T* then_keyword = NULL;
|
|
340
|
+
|
|
341
|
+
if (control_type == CONTROL_TYPE_IF || control_type == CONTROL_TYPE_ELSIF || control_type == CONTROL_TYPE_UNLESS
|
|
342
|
+
|| control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
|
|
343
|
+
const char* source = content ? content->value : NULL;
|
|
344
|
+
|
|
345
|
+
if (control_type == CONTROL_TYPE_WHEN || control_type == CONTROL_TYPE_IN) {
|
|
346
|
+
if (source != NULL && strstr(source, "then") != NULL) {
|
|
347
|
+
then_keyword = get_then_keyword_location_wrapped(source, control_type == CONTROL_TYPE_IN);
|
|
348
|
+
}
|
|
349
|
+
} else if (control_type == CONTROL_TYPE_ELSIF) {
|
|
350
|
+
if (source != NULL && strstr(source, "then") != NULL) {
|
|
351
|
+
then_keyword = get_then_keyword_location_elsif_wrapped(source);
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
then_keyword = get_then_keyword_location(erb_node->analyzed_ruby, source);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (then_keyword != NULL && content != NULL) {
|
|
358
|
+
position_T content_start = content->location.start;
|
|
359
|
+
|
|
360
|
+
then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
|
|
361
|
+
then_keyword->start.column = content_start.column + then_keyword->start.column;
|
|
362
|
+
then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
|
|
363
|
+
then_keyword->end.column = content_start.column + then_keyword->end.column;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
322
367
|
switch (control_type) {
|
|
323
368
|
case CONTROL_TYPE_IF:
|
|
324
369
|
case CONTROL_TYPE_ELSIF: {
|
|
@@ -326,6 +371,7 @@ static AST_NODE_T* create_control_node(
|
|
|
326
371
|
tag_opening,
|
|
327
372
|
content,
|
|
328
373
|
tag_closing,
|
|
374
|
+
then_keyword,
|
|
329
375
|
children,
|
|
330
376
|
subsequent,
|
|
331
377
|
end_node,
|
|
@@ -398,15 +444,29 @@ static AST_NODE_T* create_control_node(
|
|
|
398
444
|
}
|
|
399
445
|
|
|
400
446
|
case CONTROL_TYPE_WHEN: {
|
|
401
|
-
return (
|
|
402
|
-
|
|
403
|
-
|
|
447
|
+
return (AST_NODE_T*) ast_erb_when_node_init(
|
|
448
|
+
tag_opening,
|
|
449
|
+
content,
|
|
450
|
+
tag_closing,
|
|
451
|
+
then_keyword,
|
|
452
|
+
children,
|
|
453
|
+
start_position,
|
|
454
|
+
end_position,
|
|
455
|
+
errors
|
|
456
|
+
);
|
|
404
457
|
}
|
|
405
458
|
|
|
406
459
|
case CONTROL_TYPE_IN: {
|
|
407
|
-
return (
|
|
408
|
-
|
|
409
|
-
|
|
460
|
+
return (AST_NODE_T*) ast_erb_in_node_init(
|
|
461
|
+
tag_opening,
|
|
462
|
+
content,
|
|
463
|
+
tag_closing,
|
|
464
|
+
then_keyword,
|
|
465
|
+
children,
|
|
466
|
+
start_position,
|
|
467
|
+
end_position,
|
|
468
|
+
errors
|
|
469
|
+
);
|
|
410
470
|
}
|
|
411
471
|
|
|
412
472
|
case CONTROL_TYPE_BEGIN: {
|
|
@@ -471,6 +531,7 @@ static AST_NODE_T* create_control_node(
|
|
|
471
531
|
tag_opening,
|
|
472
532
|
content,
|
|
473
533
|
tag_closing,
|
|
534
|
+
then_keyword,
|
|
474
535
|
children,
|
|
475
536
|
else_clause,
|
|
476
537
|
end_node,
|
|
@@ -599,10 +660,27 @@ static size_t process_control_structure(
|
|
|
599
660
|
hb_array_T* when_errors = erb_content->base.errors;
|
|
600
661
|
erb_content->base.errors = NULL;
|
|
601
662
|
|
|
663
|
+
location_T* then_keyword = NULL;
|
|
664
|
+
const char* source = erb_content->content ? erb_content->content->value : NULL;
|
|
665
|
+
|
|
666
|
+
if (source != NULL && strstr(source, "then") != NULL) {
|
|
667
|
+
then_keyword = get_then_keyword_location_wrapped(source, false);
|
|
668
|
+
|
|
669
|
+
if (then_keyword != NULL && erb_content->content != NULL) {
|
|
670
|
+
position_T content_start = erb_content->content->location.start;
|
|
671
|
+
|
|
672
|
+
then_keyword->start.line = content_start.line + then_keyword->start.line - 1;
|
|
673
|
+
then_keyword->start.column = content_start.column + then_keyword->start.column;
|
|
674
|
+
then_keyword->end.line = content_start.line + then_keyword->end.line - 1;
|
|
675
|
+
then_keyword->end.column = content_start.column + then_keyword->end.column;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
602
679
|
AST_ERB_WHEN_NODE_T* when_node = ast_erb_when_node_init(
|
|
603
680
|
erb_content->tag_opening,
|
|
604
681
|
erb_content->content,
|
|
605
682
|
erb_content->tag_closing,
|
|
683
|
+
then_keyword,
|
|
606
684
|
when_statements,
|
|
607
685
|
erb_content->tag_opening->location.start,
|
|
608
686
|
erb_content->tag_closing->location.end,
|
|
@@ -623,10 +701,27 @@ static size_t process_control_structure(
|
|
|
623
701
|
hb_array_T* in_errors = erb_content->base.errors;
|
|
624
702
|
erb_content->base.errors = NULL;
|
|
625
703
|
|
|
704
|
+
location_T* in_then_keyword = NULL;
|
|
705
|
+
const char* in_source = erb_content->content ? erb_content->content->value : NULL;
|
|
706
|
+
|
|
707
|
+
if (in_source != NULL && strstr(in_source, "then") != NULL) {
|
|
708
|
+
in_then_keyword = get_then_keyword_location_wrapped(in_source, true);
|
|
709
|
+
|
|
710
|
+
if (in_then_keyword != NULL && erb_content->content != NULL) {
|
|
711
|
+
position_T content_start = erb_content->content->location.start;
|
|
712
|
+
|
|
713
|
+
in_then_keyword->start.line = content_start.line + in_then_keyword->start.line - 1;
|
|
714
|
+
in_then_keyword->start.column = content_start.column + in_then_keyword->start.column;
|
|
715
|
+
in_then_keyword->end.line = content_start.line + in_then_keyword->end.line - 1;
|
|
716
|
+
in_then_keyword->end.column = content_start.column + in_then_keyword->end.column;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
626
720
|
AST_ERB_IN_NODE_T* in_node = ast_erb_in_node_init(
|
|
627
721
|
erb_content->tag_opening,
|
|
628
722
|
erb_content->content,
|
|
629
723
|
erb_content->tag_closing,
|
|
724
|
+
in_then_keyword,
|
|
630
725
|
in_statements,
|
|
631
726
|
erb_content->tag_opening->location.start,
|
|
632
727
|
erb_content->tag_closing->location.end,
|
data/src/analyze_helpers.c
CHANGED
|
@@ -76,6 +76,10 @@ bool has_yield_node(analyzed_ruby_T* analyzed) {
|
|
|
76
76
|
return analyzed->yield_node_count > 0;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
bool has_then_keyword(analyzed_ruby_T* analyzed) {
|
|
80
|
+
return analyzed->then_keyword_count > 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
79
83
|
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
|
|
80
84
|
for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
|
|
81
85
|
error = (const pm_diagnostic_t*) error->node.next) {
|
|
@@ -102,13 +106,13 @@ bool search_if_nodes(const pm_node_t* node, void* data) {
|
|
|
102
106
|
return false;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
bool is_do_block(pm_location_t opening_location) {
|
|
106
110
|
size_t length = opening_location.end - opening_location.start;
|
|
107
111
|
|
|
108
112
|
return length == 2 && opening_location.start[0] == 'd' && opening_location.start[1] == 'o';
|
|
109
113
|
}
|
|
110
114
|
|
|
111
|
-
|
|
115
|
+
bool is_brace_block(pm_location_t opening_location) {
|
|
112
116
|
size_t length = opening_location.end - opening_location.start;
|
|
113
117
|
|
|
114
118
|
return length == 1 && opening_location.start[0] == '{';
|
|
@@ -118,6 +122,32 @@ static bool has_location(pm_location_t location) {
|
|
|
118
122
|
return location.start != NULL && location.end != NULL && (location.end - location.start) > 0;
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
static bool is_end_keyword(pm_location_t location) {
|
|
126
|
+
if (location.start == NULL || location.end == NULL) { return false; }
|
|
127
|
+
|
|
128
|
+
size_t length = location.end - location.start;
|
|
129
|
+
|
|
130
|
+
return length == 3 && location.start[0] == 'e' && location.start[1] == 'n' && location.start[2] == 'd';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
bool is_closing_brace(pm_location_t location) {
|
|
134
|
+
if (location.start == NULL || location.end == NULL) { return false; }
|
|
135
|
+
|
|
136
|
+
size_t length = location.end - location.start;
|
|
137
|
+
|
|
138
|
+
return length == 1 && location.start[0] == '}';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
bool has_valid_block_closing(pm_location_t opening_loc, pm_location_t closing_loc) {
|
|
142
|
+
if (is_do_block(opening_loc)) {
|
|
143
|
+
return is_end_keyword(closing_loc);
|
|
144
|
+
} else if (is_brace_block(opening_loc)) {
|
|
145
|
+
return is_closing_brace(closing_loc);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
121
151
|
bool search_block_nodes(const pm_node_t* node, void* data) {
|
|
122
152
|
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
123
153
|
|
|
@@ -125,7 +155,7 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
|
|
|
125
155
|
pm_block_node_t* block_node = (pm_block_node_t*) node;
|
|
126
156
|
|
|
127
157
|
bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
|
|
128
|
-
bool is_unclosed = !
|
|
158
|
+
bool is_unclosed = !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc);
|
|
129
159
|
|
|
130
160
|
if (has_opening && is_unclosed) { analyzed->block_node_count++; }
|
|
131
161
|
}
|
|
@@ -299,6 +329,42 @@ bool search_yield_nodes(const pm_node_t* node, void* data) {
|
|
|
299
329
|
return false;
|
|
300
330
|
}
|
|
301
331
|
|
|
332
|
+
bool search_then_keywords(const pm_node_t* node, void* data) {
|
|
333
|
+
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
334
|
+
|
|
335
|
+
switch (node->type) {
|
|
336
|
+
case PM_IF_NODE: {
|
|
337
|
+
const pm_if_node_t* if_node = (const pm_if_node_t*) node;
|
|
338
|
+
if (if_node->then_keyword_loc.start != NULL && if_node->then_keyword_loc.end != NULL) {
|
|
339
|
+
analyzed->then_keyword_count++;
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
case PM_UNLESS_NODE: {
|
|
345
|
+
const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
|
|
346
|
+
if (unless_node->then_keyword_loc.start != NULL && unless_node->then_keyword_loc.end != NULL) {
|
|
347
|
+
analyzed->then_keyword_count++;
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case PM_WHEN_NODE: {
|
|
353
|
+
const pm_when_node_t* when_node = (const pm_when_node_t*) node;
|
|
354
|
+
if (when_node->then_keyword_loc.start != NULL && when_node->then_keyword_loc.end != NULL) {
|
|
355
|
+
analyzed->then_keyword_count++;
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
default: break;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
pm_visit_child_nodes(node, search_then_keywords, analyzed);
|
|
364
|
+
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
|
|
302
368
|
static bool is_postfix_conditional(const pm_statements_node_t* statements, pm_location_t keyword_location) {
|
|
303
369
|
if (statements == NULL) { return false; }
|
|
304
370
|
|
|
@@ -314,7 +380,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
314
380
|
case PM_IF_NODE: {
|
|
315
381
|
const pm_if_node_t* if_node = (const pm_if_node_t*) node;
|
|
316
382
|
|
|
317
|
-
if (has_location(if_node->if_keyword_loc) && !
|
|
383
|
+
if (has_location(if_node->if_keyword_loc) && !is_end_keyword(if_node->end_keyword_loc)) {
|
|
318
384
|
if (!is_postfix_conditional(if_node->statements, if_node->if_keyword_loc)) {
|
|
319
385
|
analyzed->unclosed_control_flow_count++;
|
|
320
386
|
}
|
|
@@ -326,7 +392,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
326
392
|
case PM_UNLESS_NODE: {
|
|
327
393
|
const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
|
|
328
394
|
|
|
329
|
-
if (has_location(unless_node->keyword_loc) && !
|
|
395
|
+
if (has_location(unless_node->keyword_loc) && !is_end_keyword(unless_node->end_keyword_loc)) {
|
|
330
396
|
if (!is_postfix_conditional(unless_node->statements, unless_node->keyword_loc)) {
|
|
331
397
|
analyzed->unclosed_control_flow_count++;
|
|
332
398
|
}
|
|
@@ -338,7 +404,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
338
404
|
case PM_CASE_NODE: {
|
|
339
405
|
const pm_case_node_t* case_node = (const pm_case_node_t*) node;
|
|
340
406
|
|
|
341
|
-
if (has_location(case_node->case_keyword_loc) && !
|
|
407
|
+
if (has_location(case_node->case_keyword_loc) && !is_end_keyword(case_node->end_keyword_loc)) {
|
|
342
408
|
analyzed->unclosed_control_flow_count++;
|
|
343
409
|
}
|
|
344
410
|
|
|
@@ -348,7 +414,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
348
414
|
case PM_CASE_MATCH_NODE: {
|
|
349
415
|
const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
|
|
350
416
|
|
|
351
|
-
if (has_location(case_match_node->case_keyword_loc) && !
|
|
417
|
+
if (has_location(case_match_node->case_keyword_loc) && !is_end_keyword(case_match_node->end_keyword_loc)) {
|
|
352
418
|
analyzed->unclosed_control_flow_count++;
|
|
353
419
|
}
|
|
354
420
|
|
|
@@ -358,7 +424,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
358
424
|
case PM_WHILE_NODE: {
|
|
359
425
|
const pm_while_node_t* while_node = (const pm_while_node_t*) node;
|
|
360
426
|
|
|
361
|
-
if (has_location(while_node->keyword_loc) && !
|
|
427
|
+
if (has_location(while_node->keyword_loc) && !is_end_keyword(while_node->closing_loc)) {
|
|
362
428
|
analyzed->unclosed_control_flow_count++;
|
|
363
429
|
}
|
|
364
430
|
|
|
@@ -368,7 +434,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
368
434
|
case PM_UNTIL_NODE: {
|
|
369
435
|
const pm_until_node_t* until_node = (const pm_until_node_t*) node;
|
|
370
436
|
|
|
371
|
-
if (has_location(until_node->keyword_loc) && !
|
|
437
|
+
if (has_location(until_node->keyword_loc) && !is_end_keyword(until_node->closing_loc)) {
|
|
372
438
|
analyzed->unclosed_control_flow_count++;
|
|
373
439
|
}
|
|
374
440
|
|
|
@@ -378,7 +444,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
378
444
|
case PM_FOR_NODE: {
|
|
379
445
|
const pm_for_node_t* for_node = (const pm_for_node_t*) node;
|
|
380
446
|
|
|
381
|
-
if (has_location(for_node->for_keyword_loc) && !
|
|
447
|
+
if (has_location(for_node->for_keyword_loc) && !is_end_keyword(for_node->end_keyword_loc)) {
|
|
382
448
|
analyzed->unclosed_control_flow_count++;
|
|
383
449
|
}
|
|
384
450
|
|
|
@@ -388,7 +454,7 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
388
454
|
case PM_BEGIN_NODE: {
|
|
389
455
|
const pm_begin_node_t* begin_node = (const pm_begin_node_t*) node;
|
|
390
456
|
|
|
391
|
-
if (has_location(begin_node->begin_keyword_loc) && !
|
|
457
|
+
if (has_location(begin_node->begin_keyword_loc) && !is_end_keyword(begin_node->end_keyword_loc)) {
|
|
392
458
|
analyzed->unclosed_control_flow_count++;
|
|
393
459
|
}
|
|
394
460
|
|
|
@@ -399,7 +465,9 @@ bool search_unclosed_control_flows(const pm_node_t* node, void* data) {
|
|
|
399
465
|
const pm_block_node_t* block_node = (const pm_block_node_t*) node;
|
|
400
466
|
bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
|
|
401
467
|
|
|
402
|
-
if (has_opening && !
|
|
468
|
+
if (has_opening && !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc)) {
|
|
469
|
+
analyzed->unclosed_control_flow_count++;
|
|
470
|
+
}
|
|
403
471
|
break;
|
|
404
472
|
}
|
|
405
473
|
|
data/src/analyzed_ruby.c
CHANGED
|
@@ -30,6 +30,7 @@ analyzed_ruby_T* init_analyzed_ruby(hb_string_T source) {
|
|
|
30
30
|
analyzed->ensure_node_count = 0;
|
|
31
31
|
analyzed->unless_node_count = 0;
|
|
32
32
|
analyzed->yield_node_count = 0;
|
|
33
|
+
analyzed->then_keyword_count = 0;
|
|
33
34
|
analyzed->unclosed_control_flow_count = 0;
|
|
34
35
|
|
|
35
36
|
return analyzed;
|
data/src/ast_nodes.c
CHANGED
|
@@ -220,7 +220,7 @@ AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* conte
|
|
|
220
220
|
return erb_else_node;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
223
|
+
AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
224
224
|
AST_ERB_IF_NODE_T* erb_if_node = malloc(sizeof(AST_ERB_IF_NODE_T));
|
|
225
225
|
|
|
226
226
|
ast_node_init(&erb_if_node->base, AST_ERB_IF_NODE, start_position, end_position, errors);
|
|
@@ -228,6 +228,7 @@ AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content,
|
|
|
228
228
|
erb_if_node->tag_opening = token_copy(tag_opening);
|
|
229
229
|
erb_if_node->content = token_copy(content);
|
|
230
230
|
erb_if_node->tag_closing = token_copy(tag_closing);
|
|
231
|
+
erb_if_node->then_keyword = then_keyword;
|
|
231
232
|
erb_if_node->statements = statements;
|
|
232
233
|
erb_if_node->subsequent = subsequent;
|
|
233
234
|
erb_if_node->end_node = end_node;
|
|
@@ -249,7 +250,7 @@ AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* con
|
|
|
249
250
|
return erb_block_node;
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
253
|
+
AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
253
254
|
AST_ERB_WHEN_NODE_T* erb_when_node = malloc(sizeof(AST_ERB_WHEN_NODE_T));
|
|
254
255
|
|
|
255
256
|
ast_node_init(&erb_when_node->base, AST_ERB_WHEN_NODE, start_position, end_position, errors);
|
|
@@ -257,6 +258,7 @@ AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* conte
|
|
|
257
258
|
erb_when_node->tag_opening = token_copy(tag_opening);
|
|
258
259
|
erb_when_node->content = token_copy(content);
|
|
259
260
|
erb_when_node->tag_closing = token_copy(tag_closing);
|
|
261
|
+
erb_when_node->then_keyword = then_keyword;
|
|
260
262
|
erb_when_node->statements = statements;
|
|
261
263
|
|
|
262
264
|
return erb_when_node;
|
|
@@ -380,7 +382,7 @@ AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* con
|
|
|
380
382
|
return erb_begin_node;
|
|
381
383
|
}
|
|
382
384
|
|
|
383
|
-
AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
385
|
+
AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
384
386
|
AST_ERB_UNLESS_NODE_T* erb_unless_node = malloc(sizeof(AST_ERB_UNLESS_NODE_T));
|
|
385
387
|
|
|
386
388
|
ast_node_init(&erb_unless_node->base, AST_ERB_UNLESS_NODE, start_position, end_position, errors);
|
|
@@ -388,6 +390,7 @@ AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* c
|
|
|
388
390
|
erb_unless_node->tag_opening = token_copy(tag_opening);
|
|
389
391
|
erb_unless_node->content = token_copy(content);
|
|
390
392
|
erb_unless_node->tag_closing = token_copy(tag_closing);
|
|
393
|
+
erb_unless_node->then_keyword = then_keyword;
|
|
391
394
|
erb_unless_node->statements = statements;
|
|
392
395
|
erb_unless_node->else_clause = else_clause;
|
|
393
396
|
erb_unless_node->end_node = end_node;
|
|
@@ -407,7 +410,7 @@ AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* con
|
|
|
407
410
|
return erb_yield_node;
|
|
408
411
|
}
|
|
409
412
|
|
|
410
|
-
AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
413
|
+
AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors) {
|
|
411
414
|
AST_ERB_IN_NODE_T* erb_in_node = malloc(sizeof(AST_ERB_IN_NODE_T));
|
|
412
415
|
|
|
413
416
|
ast_node_init(&erb_in_node->base, AST_ERB_IN_NODE, start_position, end_position, errors);
|
|
@@ -415,6 +418,7 @@ AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content,
|
|
|
415
418
|
erb_in_node->tag_opening = token_copy(tag_opening);
|
|
416
419
|
erb_in_node->content = token_copy(content);
|
|
417
420
|
erb_in_node->tag_closing = token_copy(tag_closing);
|
|
421
|
+
erb_in_node->then_keyword = then_keyword;
|
|
418
422
|
erb_in_node->statements = statements;
|
|
419
423
|
|
|
420
424
|
return erb_in_node;
|
|
@@ -726,6 +730,7 @@ static void ast_free_erb_if_node(AST_ERB_IF_NODE_T* erb_if_node) {
|
|
|
726
730
|
if (erb_if_node->tag_opening != NULL) { token_free(erb_if_node->tag_opening); }
|
|
727
731
|
if (erb_if_node->content != NULL) { token_free(erb_if_node->content); }
|
|
728
732
|
if (erb_if_node->tag_closing != NULL) { token_free(erb_if_node->tag_closing); }
|
|
733
|
+
if (erb_if_node->then_keyword != NULL) { free(erb_if_node->then_keyword); }
|
|
729
734
|
if (erb_if_node->statements != NULL) {
|
|
730
735
|
for (size_t i = 0; i < hb_array_size(erb_if_node->statements); i++) {
|
|
731
736
|
AST_NODE_T* child = hb_array_get(erb_if_node->statements, i);
|
|
@@ -761,6 +766,7 @@ static void ast_free_erb_when_node(AST_ERB_WHEN_NODE_T* erb_when_node) {
|
|
|
761
766
|
if (erb_when_node->tag_opening != NULL) { token_free(erb_when_node->tag_opening); }
|
|
762
767
|
if (erb_when_node->content != NULL) { token_free(erb_when_node->content); }
|
|
763
768
|
if (erb_when_node->tag_closing != NULL) { token_free(erb_when_node->tag_closing); }
|
|
769
|
+
if (erb_when_node->then_keyword != NULL) { free(erb_when_node->then_keyword); }
|
|
764
770
|
if (erb_when_node->statements != NULL) {
|
|
765
771
|
for (size_t i = 0; i < hb_array_size(erb_when_node->statements); i++) {
|
|
766
772
|
AST_NODE_T* child = hb_array_get(erb_when_node->statements, i);
|
|
@@ -933,6 +939,7 @@ static void ast_free_erb_unless_node(AST_ERB_UNLESS_NODE_T* erb_unless_node) {
|
|
|
933
939
|
if (erb_unless_node->tag_opening != NULL) { token_free(erb_unless_node->tag_opening); }
|
|
934
940
|
if (erb_unless_node->content != NULL) { token_free(erb_unless_node->content); }
|
|
935
941
|
if (erb_unless_node->tag_closing != NULL) { token_free(erb_unless_node->tag_closing); }
|
|
942
|
+
if (erb_unless_node->then_keyword != NULL) { free(erb_unless_node->then_keyword); }
|
|
936
943
|
if (erb_unless_node->statements != NULL) {
|
|
937
944
|
for (size_t i = 0; i < hb_array_size(erb_unless_node->statements); i++) {
|
|
938
945
|
AST_NODE_T* child = hb_array_get(erb_unless_node->statements, i);
|
|
@@ -959,6 +966,7 @@ static void ast_free_erb_in_node(AST_ERB_IN_NODE_T* erb_in_node) {
|
|
|
959
966
|
if (erb_in_node->tag_opening != NULL) { token_free(erb_in_node->tag_opening); }
|
|
960
967
|
if (erb_in_node->content != NULL) { token_free(erb_in_node->content); }
|
|
961
968
|
if (erb_in_node->tag_closing != NULL) { token_free(erb_in_node->tag_closing); }
|
|
969
|
+
if (erb_in_node->then_keyword != NULL) { free(erb_in_node->then_keyword); }
|
|
962
970
|
if (erb_in_node->statements != NULL) {
|
|
963
971
|
for (size_t i = 0; i < hb_array_size(erb_in_node->statements); i++) {
|
|
964
972
|
AST_NODE_T* child = hb_array_get(erb_in_node->statements, i);
|
data/src/ast_pretty_print.c
CHANGED
|
@@ -265,6 +265,19 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
|
265
265
|
pretty_print_token_property(erb_if_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer);
|
|
266
266
|
pretty_print_token_property(erb_if_node->content, hb_string("content"), indent, relative_indent, false, buffer);
|
|
267
267
|
pretty_print_token_property(erb_if_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
|
|
268
|
+
pretty_print_label(hb_string("then_keyword"), indent, relative_indent, false, buffer);
|
|
269
|
+
if (erb_if_node->then_keyword) {
|
|
270
|
+
char then_keyword_location_string[128];
|
|
271
|
+
sprintf(then_keyword_location_string, " (location: (%u:%u)-(%u:%u))\n",
|
|
272
|
+
erb_if_node->then_keyword->start.line,
|
|
273
|
+
erb_if_node->then_keyword->start.column,
|
|
274
|
+
erb_if_node->then_keyword->end.line,
|
|
275
|
+
erb_if_node->then_keyword->end.column);
|
|
276
|
+
hb_buffer_append(buffer, then_keyword_location_string);
|
|
277
|
+
} else {
|
|
278
|
+
hb_buffer_append(buffer, " ∅\n");
|
|
279
|
+
}
|
|
280
|
+
|
|
268
281
|
pretty_print_array(hb_string("statements"), erb_if_node->statements, indent, relative_indent, false, buffer);
|
|
269
282
|
|
|
270
283
|
pretty_print_label(hb_string("subsequent"), indent, relative_indent, false, buffer);
|
|
@@ -330,6 +343,19 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
|
330
343
|
pretty_print_token_property(erb_when_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer);
|
|
331
344
|
pretty_print_token_property(erb_when_node->content, hb_string("content"), indent, relative_indent, false, buffer);
|
|
332
345
|
pretty_print_token_property(erb_when_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
|
|
346
|
+
pretty_print_label(hb_string("then_keyword"), indent, relative_indent, false, buffer);
|
|
347
|
+
if (erb_when_node->then_keyword) {
|
|
348
|
+
char then_keyword_location_string[128];
|
|
349
|
+
sprintf(then_keyword_location_string, " (location: (%u:%u)-(%u:%u))\n",
|
|
350
|
+
erb_when_node->then_keyword->start.line,
|
|
351
|
+
erb_when_node->then_keyword->start.column,
|
|
352
|
+
erb_when_node->then_keyword->end.line,
|
|
353
|
+
erb_when_node->then_keyword->end.column);
|
|
354
|
+
hb_buffer_append(buffer, then_keyword_location_string);
|
|
355
|
+
} else {
|
|
356
|
+
hb_buffer_append(buffer, " ∅\n");
|
|
357
|
+
}
|
|
358
|
+
|
|
333
359
|
pretty_print_array(hb_string("statements"), erb_when_node->statements, indent, relative_indent, true, buffer);
|
|
334
360
|
} break;
|
|
335
361
|
|
|
@@ -602,6 +628,19 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
|
602
628
|
pretty_print_token_property(erb_unless_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer);
|
|
603
629
|
pretty_print_token_property(erb_unless_node->content, hb_string("content"), indent, relative_indent, false, buffer);
|
|
604
630
|
pretty_print_token_property(erb_unless_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
|
|
631
|
+
pretty_print_label(hb_string("then_keyword"), indent, relative_indent, false, buffer);
|
|
632
|
+
if (erb_unless_node->then_keyword) {
|
|
633
|
+
char then_keyword_location_string[128];
|
|
634
|
+
sprintf(then_keyword_location_string, " (location: (%u:%u)-(%u:%u))\n",
|
|
635
|
+
erb_unless_node->then_keyword->start.line,
|
|
636
|
+
erb_unless_node->then_keyword->start.column,
|
|
637
|
+
erb_unless_node->then_keyword->end.line,
|
|
638
|
+
erb_unless_node->then_keyword->end.column);
|
|
639
|
+
hb_buffer_append(buffer, then_keyword_location_string);
|
|
640
|
+
} else {
|
|
641
|
+
hb_buffer_append(buffer, " ∅\n");
|
|
642
|
+
}
|
|
643
|
+
|
|
605
644
|
pretty_print_array(hb_string("statements"), erb_unless_node->statements, indent, relative_indent, false, buffer);
|
|
606
645
|
|
|
607
646
|
pretty_print_label(hb_string("else_clause"), indent, relative_indent, false, buffer);
|
|
@@ -651,6 +690,19 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
|
651
690
|
pretty_print_token_property(erb_in_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer);
|
|
652
691
|
pretty_print_token_property(erb_in_node->content, hb_string("content"), indent, relative_indent, false, buffer);
|
|
653
692
|
pretty_print_token_property(erb_in_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
|
|
693
|
+
pretty_print_label(hb_string("then_keyword"), indent, relative_indent, false, buffer);
|
|
694
|
+
if (erb_in_node->then_keyword) {
|
|
695
|
+
char then_keyword_location_string[128];
|
|
696
|
+
sprintf(then_keyword_location_string, " (location: (%u:%u)-(%u:%u))\n",
|
|
697
|
+
erb_in_node->then_keyword->start.line,
|
|
698
|
+
erb_in_node->then_keyword->start.column,
|
|
699
|
+
erb_in_node->then_keyword->end.line,
|
|
700
|
+
erb_in_node->then_keyword->end.column);
|
|
701
|
+
hb_buffer_append(buffer, then_keyword_location_string);
|
|
702
|
+
} else {
|
|
703
|
+
hb_buffer_append(buffer, " ∅\n");
|
|
704
|
+
}
|
|
705
|
+
|
|
654
706
|
pretty_print_array(hb_string("statements"), erb_in_node->statements, indent, relative_indent, true, buffer);
|
|
655
707
|
} break;
|
|
656
708
|
|
|
@@ -25,9 +25,15 @@ bool has_rescue_node(analyzed_ruby_T* analyzed);
|
|
|
25
25
|
bool has_ensure_node(analyzed_ruby_T* analyzed);
|
|
26
26
|
bool has_unless_node(analyzed_ruby_T* analyzed);
|
|
27
27
|
bool has_yield_node(analyzed_ruby_T* analyzed);
|
|
28
|
+
bool has_then_keyword(analyzed_ruby_T* analyzed);
|
|
28
29
|
|
|
29
30
|
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message);
|
|
30
31
|
|
|
32
|
+
bool is_do_block(pm_location_t opening_location);
|
|
33
|
+
bool is_brace_block(pm_location_t opening_location);
|
|
34
|
+
bool is_closing_brace(pm_location_t location);
|
|
35
|
+
bool has_valid_block_closing(pm_location_t opening_loc, pm_location_t closing_loc);
|
|
36
|
+
|
|
31
37
|
bool search_if_nodes(const pm_node_t* node, void* data);
|
|
32
38
|
bool search_block_nodes(const pm_node_t* node, void* data);
|
|
33
39
|
bool search_case_nodes(const pm_node_t* node, void* data);
|
|
@@ -46,6 +52,7 @@ bool search_in_nodes(analyzed_ruby_T* analyzed);
|
|
|
46
52
|
bool search_rescue_nodes(analyzed_ruby_T* analyzed);
|
|
47
53
|
bool search_ensure_nodes(analyzed_ruby_T* analyzed);
|
|
48
54
|
bool search_yield_nodes(const pm_node_t* node, void* data);
|
|
55
|
+
bool search_then_keywords(const pm_node_t* node, void* data);
|
|
49
56
|
bool search_unclosed_control_flows(const pm_node_t* node, void* data);
|
|
50
57
|
|
|
51
58
|
void check_erb_node_for_missing_end(const AST_NODE_T* node);
|