From a72a784ee3b66252e3f32b7bb52815e9dda96d67 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 4 Jun 2026 23:00:42 +0300 Subject: [PATCH] tools: store the V source line of a C error in a bug_reports.v_lines column (#27342) --- .../modules/vbugreport/c_error_storage.v | 18 ++++-------- .../modules/vbugreport/c_error_storage_test.v | 3 +- cmd/tools/vbug-report.v | 28 +++++++++++++++---- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/cmd/tools/modules/vbugreport/c_error_storage.v b/cmd/tools/modules/vbugreport/c_error_storage.v index c86fbbbb8..48bbb6651 100644 --- a/cmd/tools/modules/vbugreport/c_error_storage.v +++ b/cmd/tools/modules/vbugreport/c_error_storage.v @@ -9,16 +9,21 @@ pub: ccompiler string error_string string lines string + v_lines string } // new_stored_c_error_report builds the fields stored by the C error report receiver. +// The generated C context is stored in `lines`, while the corresponding V source +// context (the V line that caused the C error and its surrounding lines) is stored +// separately in `v_lines`. pub fn new_stored_c_error_report(c_file string, target_os string, ccompiler string, c_error string, c_lines []string, v_lines []string) StoredCErrorReport { return StoredCErrorReport{ c_file_name: normalized_file_name(c_file) target_os: target_os ccompiler: ccompiler error_string: c_error_string(c_error) - lines: c_error_report_lines(c_lines, v_lines) + lines: c_lines.join('\n') + v_lines: v_lines.join('\n') } } @@ -40,17 +45,6 @@ fn c_error_string(c_output string) string { return '' } -fn c_error_report_lines(c_lines []string, v_lines []string) string { - mut lines := []string{cap: c_lines.len + v_lines.len} - for line in c_lines { - lines << line - } - for line in v_lines { - lines << line - } - return lines.join('\n') -} - fn normalized_error_string(trimmed string, lower string) ?string { if lower.starts_with('fatal error:') || lower.starts_with('fatal error ') { return 'error: ${trimmed}' diff --git a/cmd/tools/modules/vbugreport/c_error_storage_test.v b/cmd/tools/modules/vbugreport/c_error_storage_test.v index d5b5924d2..d1083918c 100644 --- a/cmd/tools/modules/vbugreport/c_error_storage_test.v +++ b/cmd/tools/modules/vbugreport/c_error_storage_test.v @@ -12,7 +12,8 @@ fn test_new_stored_c_error_report_extracts_sql_fields() { assert report.target_os == 'linux' assert report.ccompiler == 'clang' assert report.error_string == 'error: unknown type name "Foo"' - assert report.lines == 'void main__main(void) {\n\tFoo x;\nfoo := Foo{}' + assert report.lines == 'void main__main(void) {\n\tFoo x;' + assert report.v_lines == 'foo := Foo{}' } fn test_new_stored_c_error_report_handles_windows_c_file_path() { diff --git a/cmd/tools/vbug-report.v b/cmd/tools/vbug-report.v index 1191f781e..8764eab7d 100644 --- a/cmd/tools/vbug-report.v +++ b/cmd/tools/vbug-report.v @@ -188,7 +188,7 @@ fn connect_db(db_path string) !sqlite.DB { } fn init_db(db sqlite.DB) ! { - db.exec('create table if not exists bug_reports ( + db.exec("create table if not exists bug_reports ( id text primary key, delete_token text not null, created_at text not null, @@ -198,12 +198,29 @@ fn init_db(db sqlite.DB) ! { target_os text not null, ccompiler text not null, error_string text not null, - lines text not null - )')! + lines text not null, + v_lines text not null default '' + )")! + // Add columns that were introduced after the table already existed, so older + // databases pick them up without losing their stored reports. + ensure_column(db, 'bug_reports', 'v_lines', "text not null default ''")! db.exec('create index if not exists idx_bug_reports_created_at on bug_reports(created_at)')! } +// ensure_column adds `column` to `table` when it is not present yet. SQLite has no +// `add column if not exists`, so the existing columns are inspected first. +fn ensure_column(db sqlite.DB, table string, column string, definition string) ! { + rows := db.exec('pragma table_info(${table})')! + for row in rows { + // pragma table_info columns are: cid, name, type, notnull, dflt_value, pk + if row.vals.len > 1 && row.vals[1] == column { + return + } + } + db.exec('alter table ${table} add column ${column} ${definition}')! +} + // create stores a compiler bug report and returns its deletion token. @['/bug-report'; post] pub fn (mut app App) create(mut ctx Context) veb.Result { @@ -227,8 +244,8 @@ pub fn (mut app App) create(mut ctx Context) veb.Result { delete_token := rand.uuid_v4() app.db.exec_param_many('insert into bug_reports ( id, delete_token, created_at, remote_ip, user_agent, - c_file_name, target_os, ccompiler, error_string, lines - ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ + c_file_name, target_os, ccompiler, error_string, lines, v_lines + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ id, delete_token, time.utc().format_rfc3339(), @@ -239,6 +256,7 @@ pub fn (mut app App) create(mut ctx Context) veb.Result { stored_report.ccompiler, stored_report.error_string, stored_report.lines, + stored_report.v_lines, ]) or { return ctx.server_error('could not store report') } return ctx.json(CreateBugReportResponse{ id: id -- 2.39.5