From 70321075a6864822498474f068150bbf07d32374 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Sun, 19 Jul 2026 23:13:02 +0300 Subject: [PATCH] feat(selfhost): multi-file #line paths from Decl.sourceFile Stamp each merged .bux path onto decls, propagate to HirFunc, and emit #line N \"path\" per function without requiring BUX_DEBUG_FILE. - Decl.sourceFile + Cli_StampSourceFile on merge/project parse - HirFunc.sourceFile; CBE switches currentFile per function - Stdlib and multi-file user packages get distinct paths automatically --- docs/BuildAndTest.md | 9 +++++---- docs/QUALITY_PLAN.md | 24 +++++++++++++++++++----- src/ast.bux | 3 +++ src/c_backend.bux | 13 ++++++++++++- src/cli.bux | 33 +++++++++++++++++++++++++++++++++ src/hir.bux | 2 ++ src/hir_lower.bux | 1 + 7 files changed, 75 insertions(+), 10 deletions(-) diff --git a/docs/BuildAndTest.md b/docs/BuildAndTest.md index 50f2ed6..ad7108e 100644 --- a/docs/BuildAndTest.md +++ b/docs/BuildAndTest.md @@ -148,13 +148,14 @@ make bench-nexus # wrk throughput vs apps/nexus /api/health ```bash ./buxc build # -O0 -g, #line → .bux (gdb-friendly; bootstrap) ./buxc build --release # -O2 -DNDEBUG, no #line / -g -# Selfhost (buxc2): same --release / default -O0 -g; #line via HIR line numbers -export BUX_DEBUG_FILE=src/Main.bux # path embedded in #line (selfhost) +# Selfhost (buxc2): default -O0 -g; multi-file #line from Decl.sourceFile +# (stdlib + each src/*.bux get their own path — no env needed) export BUX_NO_LINE=1 # disable selfhost #line maps -export BUX_CFLAGS="-fno-omit-frame-pointer" # optional extra cc flags +export BUX_DEBUG_FILE=/abs/path.bux # optional: force all #line to one path +export BUX_CFLAGS="-fno-omit-frame-pointer" gdb --args ./build/myapp # (gdb) break Main -# (gdb) list # Bux source via #line +# (gdb) list # Bux source via #line (correct file per function) ``` diff --git a/docs/QUALITY_PLAN.md b/docs/QUALITY_PLAN.md index 823f365..835d76c 100644 --- a/docs/QUALITY_PLAN.md +++ b/docs/QUALITY_PLAN.md @@ -1,7 +1,7 @@ # Bux — План към „добър“ език (v0.5 → v1.0) > **Дата:** 2026-07-19 -> **Текущо:** v0.5.x — field-move, selfhost #line, Nexus KA, **LSP 0.8 call hierarchy** +> **Текущо:** v0.5.x — field-move, **multi-file #line**, Nexus KA, LSP 0.8 call hierarchy > **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain. --- @@ -78,7 +78,7 @@ | # | Задача | Защо | Статус | |---|--------|------|--------| -| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ v0.7.0: + **field/variant rename** + workspace/symbol | +| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ v0.8.0: + **call hierarchy** + deeper rename + workspace/symbol | | D.2 | `bux fmt` стабилен + CI check | Единен style | ✅ full-tree format + `make fmt-check` enforce | | D.3 | `bux test` с `--filter`, exit codes, summary table | CI-friendly | ✅ `--filter` / summary / exit 0\|1 | | D.4 | `bux doc` от `///` comments | Самодокументиращ се stdlib | ✅ bootstrap+selfhost + `make docs` | @@ -642,9 +642,23 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth) --- +## Сесия 40 (LSP 0.8 call hierarchy) + +1. **Providers:** + - `textDocument/prepareCallHierarchy` + - `callHierarchy/incomingCalls` — who calls F + - `callHierarchy/outgoingCalls` — what F calls +2. **Graph:** textual scan of known `func` symbols + `Name(` call sites; + enclosing function via nearest prior `func` decl line +3. Skips the declaration itself; workspace disk scan for other `.bux` files +4. Smoke: `tools/smoke_lsp_call_hierarchy.sh` (Add ← Compute; Compute → Add/Mul) +5. Version **bux-lsp 0.8.0**; `make test-lsp` + +--- + ## Следващи стъпки -1. LSP call hierarchy (optional) -2. Selfhost multi-file `#line` paths without BUX_DEBUG_FILE -3. Wire selfhost smoke for move_field into CI +1. Selfhost multi-file `#line` paths without BUX_DEBUG_FILE +2. Wire selfhost smoke for move_field into CI +3. Method call hierarchy (receiver methods / interface dispatch) 4. Rename of method receivers / qualified module paths (edge cases) diff --git a/src/ast.bux b/src/ast.bux index 12005df..f0d88f0 100644 --- a/src/ast.bux +++ b/src/ast.bux @@ -299,6 +299,8 @@ module Ast { isDrop: int, // @[Drop] attribute (0/1) isRelease: int, // @[Release] attribute (0/1) isConst: int, // const func (0/1) + // Source file path for #line / diagnostics (multi-file projects) + sourceFile: String, // Names strValue: String, // decl name strValue2: String, // interface name, dll name, module path @@ -418,6 +420,7 @@ module Ast { func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl { return Decl { kind: kind, line: line, column: col, isPublic: false, + sourceFile: "", isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0, strValue: "", strValue2: "", typeParam0: "", typeParam1: "", typeParamCount: 0, diff --git a/src/c_backend.bux b/src/c_backend.bux index b72016f..196293c 100644 --- a/src/c_backend.bux +++ b/src/c_backend.bux @@ -1407,7 +1407,7 @@ module CBackend { cbe.lastDebugLine = 0; cbe.emitDebugLines = true; cbe.currentFile = ""; - // Optional: BUX_DEBUG_FILE sets the #line path for gdb (selfhost has no multi-file loc yet) + // Optional override for all #line paths (default: per-func Decl.sourceFile) let dbgFile: String = bux_getenv("BUX_DEBUG_FILE"); if dbgFile != null as String && !String_Eq(dbgFile, "") { cbe.currentFile = dbgFile; @@ -1696,6 +1696,17 @@ module CBackend { i = i + 1; continue; } + // Per-function source path for multi-file #line (overrides BUX_DEBUG_FILE only if set) + if !String_Eq(mod.funcs[i].sourceFile, "") { + cbe.currentFile = mod.funcs[i].sourceFile; + } + cbe.lastDebugLine = 0; + // #line before the function itself + if cbe.emitDebugLines && !String_Eq(cbe.currentFile, "") { + StringBuilder_Append(&cbe.sb, "#line 1 \""); + StringBuilder_Append(&cbe.sb, cbe.currentFile); + StringBuilder_Append(&cbe.sb, "\"\n"); + } CBE_EmitFuncDecl(cbe, &mod.funcs[i]); StringBuilder_Append(&cbe.sb, " {\n"); // Capturing closure thunk: materialize env from fat-func env pointer diff --git a/src/cli.bux b/src/cli.bux index 2736fce..baca902 100644 --- a/src/cli.bux +++ b/src/cli.bux @@ -611,6 +611,27 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st return count; } +/// Stamp sourceFile on a decl and nested module items (for multi-file #line). +func Cli_StampSourceFile(decl: *Decl, path: String) { + if decl == null as *Decl { return; } + decl.sourceFile = path; + if decl.kind == dkModule { + var inner: *Decl = decl.childDecl1; + while inner != null as *Decl { + Cli_StampSourceFile(inner, path); + inner = inner.childDecl2; + } + } + // Impl methods + if decl.kind == dkImpl { + var m: *Decl = decl.childDecl1; + while m != null as *Decl { + Cli_StampSourceFile(m, path); + m = m.childDecl2; + } + } +} + func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int { if !FileExists(path) { Print("Error: stdlib file not found: "); @@ -623,6 +644,12 @@ func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCo if Lexer_DiagCount(lex) > 0 { return 0; } let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); if mod == null as *Module { return 0; } + // Tag every decl from this file for #line maps + var stamp: *Decl = mod.firstItem; + while stamp != null as *Decl { + Cli_StampSourceFile(stamp, path); + stamp = stamp.childDecl2; + } var added: int = 0; var decl: *Decl = mod.firstItem; @@ -1431,6 +1458,12 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool) PrintLine(path); return 1; } + // Tag decls with this source path for multi-file #line + var stampUser: *Decl = mod.firstItem; + while stampUser != null as *Decl { + Cli_StampSourceFile(stampUser, path); + stampUser = stampUser.childDecl2; + } // Merge declarations from this module into userMerged var decl: *Decl = mod.firstItem; var fileDeclCount: int = 0; diff --git a/src/hir.bux b/src/hir.bux index 40aa6c4..6ea38fb 100644 --- a/src/hir.bux +++ b/src/hir.bux @@ -115,6 +115,8 @@ module Hir { envStructName: String; envInstanceName: String; checkedFunc: bool; + // Absolute/relative path for #line maps (multi-file selfhost) + sourceFile: String; } // --------------------------------------------------------------------------- diff --git a/src/hir_lower.bux b/src/hir_lower.bux index 0a89224..e068c17 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -3423,6 +3423,7 @@ module HirLower { f.name = decl.strValue; f.isPublic = decl.isPublic; f.checkedFunc = ctx.checkedFunc; + f.sourceFile = decl.sourceFile; f.paramCount = decl.paramCount; f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param0, &decl.param0, ctx);