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
This commit is contained in:
@@ -148,13 +148,14 @@ make bench-nexus # wrk throughput vs apps/nexus /api/health
|
|||||||
```bash
|
```bash
|
||||||
./buxc build # -O0 -g, #line → .bux (gdb-friendly; bootstrap)
|
./buxc build # -O0 -g, #line → .bux (gdb-friendly; bootstrap)
|
||||||
./buxc build --release # -O2 -DNDEBUG, no #line / -g
|
./buxc build --release # -O2 -DNDEBUG, no #line / -g
|
||||||
# Selfhost (buxc2): same --release / default -O0 -g; #line via HIR line numbers
|
# Selfhost (buxc2): default -O0 -g; multi-file #line from Decl.sourceFile
|
||||||
export BUX_DEBUG_FILE=src/Main.bux # path embedded in #line (selfhost)
|
# (stdlib + each src/*.bux get their own path — no env needed)
|
||||||
export BUX_NO_LINE=1 # disable selfhost #line maps
|
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 --args ./build/myapp
|
||||||
# (gdb) break Main
|
# (gdb) break Main
|
||||||
# (gdb) list # Bux source via #line
|
# (gdb) list # Bux source via #line (correct file per function)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+19
-5
@@ -1,7 +1,7 @@
|
|||||||
# Bux — План към „добър“ език (v0.5 → v1.0)
|
# Bux — План към „добър“ език (v0.5 → v1.0)
|
||||||
|
|
||||||
> **Дата:** 2026-07-19
|
> **Дата:** 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.
|
> **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден 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.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.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` |
|
| 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)
|
1. Selfhost multi-file `#line` paths without BUX_DEBUG_FILE
|
||||||
2. Selfhost multi-file `#line` paths without BUX_DEBUG_FILE
|
2. Wire selfhost smoke for move_field into CI
|
||||||
3. 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)
|
4. Rename of method receivers / qualified module paths (edge cases)
|
||||||
|
|||||||
@@ -299,6 +299,8 @@ module Ast {
|
|||||||
isDrop: int, // @[Drop] attribute (0/1)
|
isDrop: int, // @[Drop] attribute (0/1)
|
||||||
isRelease: int, // @[Release] attribute (0/1)
|
isRelease: int, // @[Release] attribute (0/1)
|
||||||
isConst: int, // const func (0/1)
|
isConst: int, // const func (0/1)
|
||||||
|
// Source file path for #line / diagnostics (multi-file projects)
|
||||||
|
sourceFile: String,
|
||||||
// Names
|
// Names
|
||||||
strValue: String, // decl name
|
strValue: String, // decl name
|
||||||
strValue2: String, // interface name, dll name, module path
|
strValue2: String, // interface name, dll name, module path
|
||||||
@@ -418,6 +420,7 @@ module Ast {
|
|||||||
|
|
||||||
func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl {
|
func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl {
|
||||||
return Decl { kind: kind, line: line, column: col, isPublic: false,
|
return Decl { kind: kind, line: line, column: col, isPublic: false,
|
||||||
|
sourceFile: "",
|
||||||
isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0,
|
isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0,
|
||||||
strValue: "", strValue2: "",
|
strValue: "", strValue2: "",
|
||||||
typeParam0: "", typeParam1: "", typeParamCount: 0,
|
typeParam0: "", typeParam1: "", typeParamCount: 0,
|
||||||
|
|||||||
+12
-1
@@ -1407,7 +1407,7 @@ module CBackend {
|
|||||||
cbe.lastDebugLine = 0;
|
cbe.lastDebugLine = 0;
|
||||||
cbe.emitDebugLines = true;
|
cbe.emitDebugLines = true;
|
||||||
cbe.currentFile = "";
|
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");
|
let dbgFile: String = bux_getenv("BUX_DEBUG_FILE");
|
||||||
if dbgFile != null as String && !String_Eq(dbgFile, "") {
|
if dbgFile != null as String && !String_Eq(dbgFile, "") {
|
||||||
cbe.currentFile = dbgFile;
|
cbe.currentFile = dbgFile;
|
||||||
@@ -1696,6 +1696,17 @@ module CBackend {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
continue;
|
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]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, " {\n");
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
// Capturing closure thunk: materialize env from fat-func env pointer
|
// Capturing closure thunk: materialize env from fat-func env pointer
|
||||||
|
|||||||
+33
@@ -611,6 +611,27 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st
|
|||||||
return count;
|
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 {
|
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
|
||||||
if !FileExists(path) {
|
if !FileExists(path) {
|
||||||
Print("Error: stdlib file not found: ");
|
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; }
|
if Lexer_DiagCount(lex) > 0 { return 0; }
|
||||||
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
|
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
|
||||||
if mod == null as *Module { return 0; }
|
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 added: int = 0;
|
||||||
var decl: *Decl = mod.firstItem;
|
var decl: *Decl = mod.firstItem;
|
||||||
@@ -1431,6 +1458,12 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool)
|
|||||||
PrintLine(path);
|
PrintLine(path);
|
||||||
return 1;
|
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
|
// Merge declarations from this module into userMerged
|
||||||
var decl: *Decl = mod.firstItem;
|
var decl: *Decl = mod.firstItem;
|
||||||
var fileDeclCount: int = 0;
|
var fileDeclCount: int = 0;
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ module Hir {
|
|||||||
envStructName: String;
|
envStructName: String;
|
||||||
envInstanceName: String;
|
envInstanceName: String;
|
||||||
checkedFunc: bool;
|
checkedFunc: bool;
|
||||||
|
// Absolute/relative path for #line maps (multi-file selfhost)
|
||||||
|
sourceFile: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -3423,6 +3423,7 @@ module HirLower {
|
|||||||
f.name = decl.strValue;
|
f.name = decl.strValue;
|
||||||
f.isPublic = decl.isPublic;
|
f.isPublic = decl.isPublic;
|
||||||
f.checkedFunc = ctx.checkedFunc;
|
f.checkedFunc = ctx.checkedFunc;
|
||||||
|
f.sourceFile = decl.sourceFile;
|
||||||
f.paramCount = decl.paramCount;
|
f.paramCount = decl.paramCount;
|
||||||
f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam;
|
f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam;
|
||||||
Lcx_LowerParam(f.param0, &decl.param0, ctx);
|
Lcx_LowerParam(f.param0, &decl.param0, ctx);
|
||||||
|
|||||||
Reference in New Issue
Block a user