From 592deda7fd378512345e74a19956f468ced8ef82 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 25 Apr 2026 22:50:46 +0300 Subject: [PATCH] fmt: orm fix (fixes #26968) --- vlib/v/ast/ast.v | 20 +++-- vlib/v/fmt/fmt.v | 81 ++++++++++++++------ vlib/v/fmt/tests/conditions_expected.vv | 20 ++++- vlib/v/fmt/tests/conditions_input.vv | 12 +++ vlib/v/parser/orm.v | 99 ++++++++++++++++++++----- 5 files changed, 179 insertions(+), 53 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 89886b6a2..013b1604d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -2318,31 +2318,37 @@ pub struct SqlQueryDataLeaf { pub: pos token.Pos pub mut: - expr Expr + expr Expr + pre_comments []Comment + end_comments []Comment } pub struct SqlQueryDataBranch { pub: pos token.Pos pub mut: - cond Expr - items []SqlQueryDataItem + cond Expr + items []SqlQueryDataItem + end_comments []Comment } pub struct SqlQueryDataIf { pub: pos token.Pos pub mut: - branches []SqlQueryDataBranch - has_else bool + branches []SqlQueryDataBranch + has_else bool + pre_comments []Comment + end_comments []Comment } pub struct SqlQueryDataExpr { pub: pos token.Pos pub mut: - items []SqlQueryDataItem - typ Type + items []SqlQueryDataItem + typ Type + end_comments []Comment } pub struct SqlStmt { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 13496b561..80ea3c2e1 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -2635,7 +2635,8 @@ fn branch_is_single_line(b ast.IfBranch) bool { fn sql_query_data_item_is_single_line(item ast.SqlQueryDataItem) bool { return match item { ast.SqlQueryDataLeaf { - item.pos.line_nr == item.pos.last_line && expr_is_single_line(item.expr) + item.pre_comments.len == 0 && item.end_comments.len == 0 + && item.pos.line_nr == item.pos.last_line && expr_is_single_line(item.expr) } ast.SqlQueryDataIf { false @@ -2644,8 +2645,29 @@ fn sql_query_data_item_is_single_line(item ast.SqlQueryDataItem) bool { } fn sql_query_data_branch_is_single_line(branch ast.SqlQueryDataBranch) bool { - return branch.pos.line_nr == branch.pos.last_line && branch.items.len == 1 - && sql_query_data_item_is_single_line(branch.items[0]) + return branch.end_comments.len == 0 && branch.pos.line_nr == branch.pos.last_line + && branch.items.len == 1 && sql_query_data_item_is_single_line(branch.items[0]) +} + +fn sql_query_data_item_pre_comments(item ast.SqlQueryDataItem) []ast.Comment { + return match item { + ast.SqlQueryDataLeaf { item.pre_comments } + ast.SqlQueryDataIf { item.pre_comments } + } +} + +fn sql_query_data_item_end_comments(item ast.SqlQueryDataItem) []ast.Comment { + return match item { + ast.SqlQueryDataLeaf { item.end_comments } + ast.SqlQueryDataIf { item.end_comments } + } +} + +fn sql_query_data_item_last_line(item ast.SqlQueryDataItem) int { + return match item { + ast.SqlQueryDataLeaf { item.pos.last_line } + ast.SqlQueryDataIf { item.pos.last_line } + } } pub fn (mut f Fmt) if_guard_expr(node ast.IfGuardExpr) { @@ -3416,24 +3438,44 @@ pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) { } pub fn (mut f Fmt) sql_query_data_expr(node ast.SqlQueryDataExpr) { - if node.items.len == 0 { + if node.items.len == 0 && node.end_comments.len == 0 { f.write('{}') return } f.writeln('{') f.indent++ - for idx, item in node.items { - f.write_indent() + f.sql_query_data_items(node.items, node.end_comments) + f.indent-- + f.write('}') +} + +fn (mut f Fmt) sql_query_data_items(items []ast.SqlQueryDataItem, end_comments []ast.Comment) { + for idx, item in items { + f.sql_query_data_comment_lines(sql_query_data_item_pre_comments(item)) f.sql_query_data_item(item) - if idx < node.items.len - 1 { - f.writeln(',') + if idx < items.len - 1 || end_comments.len > 0 { + f.write(',') + } + item_end_comments := sql_query_data_item_end_comments(item) + if item_end_comments.len > 0 { + if item_end_comments[0].pos.line_nr == sql_query_data_item_last_line(item) { + f.comments(item_end_comments, same_line: true, has_nl: true, level: .keep) + } else { + f.writeln('') + f.sql_query_data_comment_lines(item_end_comments) + } } else { f.writeln('') } } - f.indent-- - f.write_indent() - f.write('}') + f.sql_query_data_comment_lines(end_comments) +} + +fn (mut f Fmt) sql_query_data_comment_lines(comments []ast.Comment) { + for comment in comments { + f.comment(comment) + f.writeln('') + } } fn (mut f Fmt) sql_query_data_item(item ast.SqlQueryDataItem) { @@ -3454,7 +3496,7 @@ fn (mut f Fmt) sql_query_data_item(item ast.SqlQueryDataItem) { f.expr(branch.cond) f.write(' ') } - f.sql_query_data_branch_items(branch.items, + f.sql_query_data_branch_items(branch.items, branch.end_comments, sql_query_data_branch_is_single_line(branch)) if idx < item.branches.len - 1 { f.write(' ') @@ -3464,8 +3506,8 @@ fn (mut f Fmt) sql_query_data_item(item ast.SqlQueryDataItem) { } } -fn (mut f Fmt) sql_query_data_branch_items(items []ast.SqlQueryDataItem, keep_single_line bool) { - if items.len == 0 { +fn (mut f Fmt) sql_query_data_branch_items(items []ast.SqlQueryDataItem, end_comments []ast.Comment, keep_single_line bool) { + if items.len == 0 && end_comments.len == 0 { f.write('{}') return } @@ -3484,17 +3526,8 @@ fn (mut f Fmt) sql_query_data_branch_items(items []ast.SqlQueryDataItem, keep_si } f.writeln('{') f.indent++ - for idx, item in items { - f.write_indent() - f.sql_query_data_item(item) - if idx < items.len - 1 { - f.writeln(',') - } else { - f.writeln('') - } - } + f.sql_query_data_items(items, end_comments) f.indent-- - f.write_indent() f.write('}') } diff --git a/vlib/v/fmt/tests/conditions_expected.vv b/vlib/v/fmt/tests/conditions_expected.vv index 71d068133..1b15035aa 100644 --- a/vlib/v/fmt/tests/conditions_expected.vv +++ b/vlib/v/fmt/tests/conditions_expected.vv @@ -22,9 +22,21 @@ fn short_if_stmts(a int) { fn dynamic_where_expr(name_filter string, min_age int, status_filter string) { dynamic_where := { - if name_filter != '' { name == name_filter }, - if min_age > 0 { age >= min_age }, - if status_filter != '' { status == status_filter } - } + if name_filter != '' { name == name_filter }, + if min_age > 0 { age >= min_age }, + if status_filter != '' { status == status_filter } + } + _ = dynamic_where +} + +fn dynamic_where_expr_comments(mobile ?string) { + dynamic_where := { + // mobile update + if m := mobile { + phone == m, + // nested comment + }, + // name == name_dyn + } _ = dynamic_where } diff --git a/vlib/v/fmt/tests/conditions_input.vv b/vlib/v/fmt/tests/conditions_input.vv index 36511049b..18a8329d1 100644 --- a/vlib/v/fmt/tests/conditions_input.vv +++ b/vlib/v/fmt/tests/conditions_input.vv @@ -19,3 +19,15 @@ fn dynamic_where_expr(name_filter string, min_age int, status_filter string) { dynamic_where := {if name_filter != '' {name == name_filter}, if min_age > 0 {age >= min_age}, if status_filter != '' {status == status_filter}} _ = dynamic_where } + +fn dynamic_where_expr_comments(mobile ?string) { + dynamic_where := { + // mobile update + if m := mobile { + phone == m, + // nested comment + }, + //name == name_dyn + } + _ = dynamic_where +} diff --git a/vlib/v/parser/orm.v b/vlib/v/parser/orm.v index 64d7b6767..12b00a920 100644 --- a/vlib/v/parser/orm.v +++ b/vlib/v/parser/orm.v @@ -13,6 +13,11 @@ struct SqlPrefix { is_dynamic bool } +struct SqlQueryDataItemsBlock { + items []ast.SqlQueryDataItem + end_comments []ast.Comment +} + fn sql_aggregate_kind_from_name(name string) ast.SqlAggregateKind { return match name { 'count' { .count } @@ -580,18 +585,19 @@ fn is_sql_query_data_operator(kind token.Kind) bool { fn (mut p Parser) sql_query_data_expr() ast.Expr { start_pos := p.tok.pos() - items := p.parse_sql_query_data_items_block() + block := p.parse_sql_query_data_items_block() return ast.SqlQueryDataExpr{ - pos: start_pos.extend(p.prev_tok.pos()) - items: items + pos: start_pos.extend(p.prev_tok.pos()) + items: block.items + end_comments: block.end_comments } } -fn (mut p Parser) parse_sql_query_data_items_block() []ast.SqlQueryDataItem { +fn (mut p Parser) parse_sql_query_data_items_block() SqlQueryDataItemsBlock { p.check(.lcbr) mut items := []ast.SqlQueryDataItem{} + mut pending_comments := p.eat_comments() for { - p.eat_comments() if p.tok.kind == .rcbr { break } @@ -599,19 +605,29 @@ fn (mut p Parser) parse_sql_query_data_items_block() []ast.SqlQueryDataItem { p.unexpected(got: 'eof, while parsing a dynamic ORM expression') break } - items << p.parse_sql_query_data_item() - p.eat_comments() + mut item := p.parse_sql_query_data_item() + item = sql_query_data_item_with_pre_comments(item, pending_comments) + pending_comments = [] + item = sql_query_data_item_with_end_comments(item, p.eat_comments(same_line: true)) if p.tok.kind == .comma { p.next() + item = sql_query_data_item_with_end_comments(item, p.eat_comments(same_line: true)) + items << item + pending_comments = p.eat_comments() continue } + items << item + pending_comments = p.eat_comments() if p.tok.kind != .rcbr { p.error('expected `,` or `}` in a dynamic ORM expression') break } } p.check(.rcbr) - return items + return SqlQueryDataItemsBlock{ + items: items + end_comments: pending_comments + } } fn (mut p Parser) parse_sql_query_data_item() ast.SqlQueryDataItem { @@ -625,6 +641,42 @@ fn (mut p Parser) parse_sql_query_data_item() ast.SqlQueryDataItem { } } +fn sql_query_data_item_with_pre_comments(item ast.SqlQueryDataItem, comments []ast.Comment) ast.SqlQueryDataItem { + if comments.len == 0 { + return item + } + return match item { + ast.SqlQueryDataLeaf { + mut leaf := item + leaf.pre_comments << comments + leaf + } + ast.SqlQueryDataIf { + mut if_item := item + if_item.pre_comments << comments + if_item + } + } +} + +fn sql_query_data_item_with_end_comments(item ast.SqlQueryDataItem, comments []ast.Comment) ast.SqlQueryDataItem { + if comments.len == 0 { + return item + } + return match item { + ast.SqlQueryDataLeaf { + mut leaf := item + leaf.end_comments << comments + leaf + } + ast.SqlQueryDataIf { + mut if_item := item + if_item.end_comments << comments + if_item + } + } +} + fn (mut p Parser) parse_sql_query_data_if_guard_expr() ast.IfGuardExpr { p.open_scope() mut vars := []ast.IfGuardVar{} @@ -694,19 +746,29 @@ fn (mut p Parser) parse_sql_query_data_if_item() ast.SqlQueryDataItem { prev_guard = false } p.open_scope() - items := p.parse_sql_query_data_items_block() + block := p.parse_sql_query_data_items_block() p.close_scope() if has_guard_scope { p.close_scope() } branches << ast.SqlQueryDataBranch{ - pos: branch_pos.extend(p.prev_tok.pos()) - cond: cond - items: items + pos: branch_pos.extend(p.prev_tok.pos()) + cond: cond + items: block.items + end_comments: block.end_comments } - p.eat_comments() + comments_before_else := p.eat_comments() if p.tok.kind != .key_else { - break + return ast.SqlQueryDataIf{ + pos: start_pos.extend(p.prev_tok.pos()) + branches: branches + has_else: has_else + end_comments: comments_before_else + } + } + if comments_before_else.len > 0 { + last_idx := branches.len - 1 + branches[last_idx].end_comments << comments_before_else } has_else = true else_pos := p.tok.pos() @@ -725,12 +787,13 @@ fn (mut p Parser) parse_sql_query_data_if_item() ast.SqlQueryDataItem { is_special: true }) } - else_items := p.parse_sql_query_data_items_block() + else_block := p.parse_sql_query_data_items_block() p.close_scope() branches << ast.SqlQueryDataBranch{ - pos: else_pos.extend(p.prev_tok.pos()) - cond: ast.empty_expr - items: else_items + pos: else_pos.extend(p.prev_tok.pos()) + cond: ast.empty_expr + items: else_block.items + end_comments: else_block.end_comments } break } -- 2.39.5