Skip to content

Commit 9ed23df

Browse files
authored
v3: expand checker fixture compatibility (#27954)
1 parent 09062b4 commit 9ed23df

34 files changed

Lines changed: 35685 additions & 8315 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
vlib/v/checker/tests/generics_struct_init_same_line_err.vv:18:10: error: could not infer generic type `T` in generic struct `Left[T]`
2+
16 |
3+
17 | fn main() {
4+
18 | println(Left{value: 1}.left_value() + Right{value: 2}.right_value())
5+
| ~~~~~~~~~~~~~~
6+
19 | }
7+
vlib/v/checker/tests/generics_struct_init_same_line_err.vv:18:25: error: could not infer generic type `T` in call to `left_value`
8+
16 |
9+
17 | fn main() {
10+
18 | println(Left{value: 1}.left_value() + Right{value: 2}.right_value())
11+
| ~~~~~~~~~~~~
12+
19 | }
13+
vlib/v/checker/tests/generics_struct_init_same_line_err.vv:18:40: error: could not infer generic type `T` in generic struct `Right[T]`
14+
16 |
15+
17 | fn main() {
16+
18 | println(Left{value: 1}.left_value() + Right{value: 2}.right_value())
17+
| ~~~~~~~~~~~~~~~
18+
19 | }
19+
vlib/v/checker/tests/generics_struct_init_same_line_err.vv:18:56: error: could not infer generic type `T` in call to `right_value`
20+
16 |
21+
17 | fn main() {
22+
18 | println(Left{value: 1}.left_value() + Right{value: 2}.right_value())
23+
| ~~~~~~~~~~~~~
24+
19 | }
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct Left[T] {
2+
value int
3+
}
4+
5+
struct Right[T] {
6+
value int
7+
}
8+
9+
fn (left Left[T]) left_value() int {
10+
return left.value
11+
}
12+
13+
fn (right Right[T]) right_value() int {
14+
return right.value
15+
}
16+
17+
fn main() {
18+
println(Left{value: 1}.left_value() + Right{value: 2}.right_value())
19+
}

vlib/v3/bench/bench.v

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import os
44
import runtime
55
import time
66

7-
const default_memory_limit_kb = i64(10) * 1024 * 1024
7+
const default_memory_limit_kb = i64(2) * 1024 * 1024
88
const memory_monitor_interval = 100 * time.millisecond
99

1010
// Step represents step data used by bench.
@@ -49,6 +49,7 @@ mut:
4949
last_allocation_count u64
5050
last_allocated_bytes u64
5151
memory_limit_kb i64
52+
quiet bool
5253
}
5354

5455
// new creates a new value for bench.
@@ -68,6 +69,11 @@ pub fn (mut b Bench) disable_memory_limit() {
6869
b.memory_limit_kb = 0
6970
}
7071

72+
// set_quiet suppresses benchmark output while retaining timing and memory checks.
73+
pub fn (mut b Bench) set_quiet() {
74+
b.quiet = true
75+
}
76+
7177
// start_memory_monitor starts the compiler memory safety watchdog.
7278
pub fn (b &Bench) start_memory_monitor() {
7379
if b.memory_limit_kb > 0 {
@@ -174,7 +180,9 @@ fn (mut b Bench) report_step(name string, parallel bool, elapsed_us i64, allocat
174180
} else {
175181
''
176182
}
177-
println(' ${label:-20s} ${ms:8.2f} ms ${ram_mb:6.0f} MB RSS${footprint_suffix} ${peak_ram_mb:6.0f} MB peak${allocation_suffix}')
183+
if !b.quiet {
184+
println(' ${label:-20s} ${ms:8.2f} ms ${ram_mb:6.0f} MB RSS${footprint_suffix} ${peak_ram_mb:6.0f} MB peak${allocation_suffix}')
185+
}
178186
b.steps << Step{
179187
name: label
180188
time_us: elapsed_us
@@ -221,7 +229,10 @@ pub fn (mut b Bench) metric(name string, value i64, unit string) {
221229
}
222230

223231
// metric_items prints a structural counter and a one-line list of its items immediately.
224-
pub fn (_ &Bench) metric_items(name string, value i64, unit string, items_label string, items []string) {
232+
pub fn (b &Bench) metric_items(name string, value i64, unit string, items_label string, items []string) {
233+
if b.quiet {
234+
return
235+
}
225236
metric := Metric{
226237
name: name
227238
value: value
@@ -239,6 +250,9 @@ fn print_metric(metric Metric) {
239250

240251
// print_report updates print report state for Bench.
241252
pub fn (b &Bench) print_report() {
253+
if b.quiet {
254+
return
255+
}
242256
total_ms := f64(b.total_sw.elapsed().microseconds()) / 1000.0
243257
println(' ${'total':-20s} ${total_ms:8.2f} ms')
244258
if b.metrics.len > 0 {

vlib/v3/bench/bench_test.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ fn test_memory_limit_error_starts_at_limit() {
1010

1111
message := memory_limit_error(default_memory_limit_kb, default_memory_limit_kb, 'after parse',
1212
'RSS')
13-
assert message.contains('10240 MiB RSS after parse')
14-
assert message.contains('limit: 10 GiB')
13+
assert message.contains('2048 MiB RSS after parse')
14+
assert message.contains('limit: 2 GiB')
1515
assert message.contains('`-no-memory-limit`')
1616
}
1717

vlib/v3/errors/format.v

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
module errors
2+
3+
import encoding.utf8.east_asian
4+
import os
5+
import strings
6+
import v3.flat
7+
import v3.token
8+
9+
const source_context_before = 2
10+
const source_context_after = 2
11+
12+
// formatted_error renders a compiler diagnostic with v1-compatible source context.
13+
pub fn formatted_error(kind string, message string, a &flat.FlatAst, node flat.NodeId, pos token.Pos) string {
14+
if pos.is_valid() {
15+
file := a.source_files[pos.id] or { return '${kind} ${message}' }
16+
return formatted_source_error(kind, message, file, pos)
17+
}
18+
if int(node) < 0 || int(node) >= a.nodes.len {
19+
return '${kind} ${message}'
20+
}
21+
n := a.nodes[int(node)]
22+
file := a.source_files[n.pos.id] or { return '${kind} ${message}' }
23+
return formatted_source_error(kind, message, file, n.pos)
24+
}
25+
26+
// formatted_source_error renders a diagnostic for a source file and byte span.
27+
pub fn formatted_source_error(kind string, message string, file &token.File, pos token.Pos) string {
28+
position := file.position(pos)
29+
path := relative_error_path(file.name)
30+
mut result := strings.new_builder(message.len + 256)
31+
reported_column := if pos.reported_column > 0 {
32+
pos.reported_column
33+
} else {
34+
position.column
35+
}
36+
result.writeln('${path}:${position.line}:${reported_column}: ${kind} ${message}')
37+
source := os.read_file(file.name) or { return result.str().trim_right('\n') }
38+
lines := source.split_into_lines()
39+
if lines.len == 0 {
40+
return result.str().trim_right('\n')
41+
}
42+
first_line := int_max(1, position.line - source_context_before)
43+
last_line := int_min(lines.len, position.line + source_context_after)
44+
for line_number := first_line; line_number <= last_line; line_number++ {
45+
line := lines[line_number - 1]
46+
result.writeln('${line_number:5d} | ${line.replace('\t', ' ')}')
47+
if line_number == position.line {
48+
line_start := file.line_start(position.line)
49+
start_byte := int_max(0, int_min(pos.offset - line_start, line.len))
50+
span_end := int_max(pos.offset + 1, pos.end)
51+
end_byte := int_min(line.len, int_max(start_byte + 1, int_min(span_end - line_start,
52+
line.len)))
53+
mut pointer := strings.new_builder(line.len + 8)
54+
prefix := line[..start_byte].replace('\t', ' ')
55+
pointer.write_string(' '.repeat(diagnostic_display_width(prefix)))
56+
underline_len := int_max(1, diagnostic_display_width(line[start_byte..end_byte]))
57+
pointer.write_string(if underline_len > 1 {
58+
'~'.repeat(underline_len)
59+
} else {
60+
'^'
61+
})
62+
result.writeln(' | ${pointer.str().replace('\t', ' ')}')
63+
}
64+
}
65+
return result.str().trim_right('\n')
66+
}
67+
68+
fn diagnostic_display_width(text string) int {
69+
mut width := 0
70+
mut valid_start := 0
71+
mut i := 0
72+
for i < text.len {
73+
sequence_len := valid_utf8_sequence_len(text, i)
74+
if sequence_len > 0 {
75+
i += sequence_len
76+
continue
77+
}
78+
if valid_start < i {
79+
width += east_asian.display_width(text[valid_start..i], 1)
80+
}
81+
width++
82+
i++
83+
valid_start = i
84+
}
85+
if valid_start < text.len {
86+
width += east_asian.display_width(text[valid_start..], 1)
87+
}
88+
return width
89+
}
90+
91+
fn valid_utf8_sequence_len(text string, index int) int {
92+
first := text[index]
93+
if first < 0x80 {
94+
return 1
95+
}
96+
mut length := 0
97+
mut second_min := u8(0x80)
98+
mut second_max := u8(0xbf)
99+
if first >= 0xc2 && first <= 0xdf {
100+
length = 2
101+
} else if first >= 0xe0 && first <= 0xef {
102+
length = 3
103+
if first == 0xe0 {
104+
second_min = 0xa0
105+
} else if first == 0xed {
106+
second_max = 0x9f
107+
}
108+
} else if first >= 0xf0 && first <= 0xf4 {
109+
length = 4
110+
if first == 0xf0 {
111+
second_min = 0x90
112+
} else if first == 0xf4 {
113+
second_max = 0x8f
114+
}
115+
} else {
116+
return 0
117+
}
118+
if index + length > text.len || text[index + 1] < second_min || text[index + 1] > second_max {
119+
return 0
120+
}
121+
for i in index + 2 .. index + length {
122+
if text[i] < 0x80 || text[i] > 0xbf {
123+
return 0
124+
}
125+
}
126+
return length
127+
}
128+
129+
fn relative_error_path(path string) string {
130+
mut normalized := os.real_path(path).replace('\\', '/')
131+
if os.getenv('VERROR_PATHS') == 'absolute' {
132+
return normalized
133+
}
134+
workdir := os.getwd().replace('\\', '/').trim_right('/') + '/'
135+
if normalized.starts_with(workdir) {
136+
normalized = normalized[workdir.len..]
137+
}
138+
return normalized
139+
}

vlib/v3/errors/format_test.v

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module errors
2+
3+
import os
4+
5+
fn test_relative_error_path_honors_absolute_path_requests() {
6+
old_value := os.getenv_opt('VERROR_PATHS')
7+
defer {
8+
if value := old_value {
9+
os.setenv('VERROR_PATHS', value, true)
10+
} else {
11+
os.unsetenv('VERROR_PATHS')
12+
}
13+
}
14+
path := os.join_path(os.getwd(), 'vlib', 'v3', 'errors', 'format.v')
15+
absolute_path := os.real_path(path).replace('\\', '/')
16+
os.setenv('VERROR_PATHS', 'absolute', true)
17+
assert relative_error_path(path) == absolute_path
18+
os.unsetenv('VERROR_PATHS')
19+
assert relative_error_path(path) == 'vlib/v3/errors/format.v'
20+
}

0 commit comments

Comments
 (0)