feat(selfhost-cli): surface parser diagnostics — syntax errors no longer pass check silently
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "parse_recovery"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
func Good() -> int {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bad() -> int {
|
||||||
|
let x: int = ;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func AlsoBad() -> int {
|
||||||
|
let y: int = 5 + ;
|
||||||
|
let z: int = 6;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
func AlsoGood() -> int {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
+12
@@ -373,6 +373,16 @@ module Ast {
|
|||||||
// Struct fields (up to 256)
|
// Struct fields (up to 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Parser diagnostic (recoverable errors collected during parsing)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
struct ParserDiag {
|
||||||
|
line: uint32,
|
||||||
|
column: uint32,
|
||||||
|
message: String,
|
||||||
|
severity: int, /* 0=error (fatal), 1=warning (recoverable) */
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Module — AST root
|
// Module — AST root
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -381,6 +391,8 @@ module Ast {
|
|||||||
path: String, // path segments joined
|
path: String, // path segments joined
|
||||||
itemCount: int,
|
itemCount: int,
|
||||||
firstItem: *Decl,
|
firstItem: *Decl,
|
||||||
|
diagCount: int,
|
||||||
|
diags: *ParserDiag,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+34
@@ -358,6 +358,28 @@ module Cli {
|
|||||||
// Import the compiler pipeline
|
// Import the compiler pipeline
|
||||||
// In self-hosting mode, these are compiled together from src/
|
// In self-hosting mode, these are compiled together from src/
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Report parser diagnostics attached to a module (Rust-style, with snippets).
|
||||||
|
// Returns the number of diagnostics reported (0 = clean).
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Cli_ReportParseDiags(mod: *Module, sourceName: String) -> int {
|
||||||
|
if mod == null as *Module { return 0; }
|
||||||
|
if mod.diagCount == 0 { return 0; }
|
||||||
|
var i: int = 0;
|
||||||
|
while i < mod.diagCount {
|
||||||
|
let diag: Diagnostic = Diagnostic {
|
||||||
|
message: mod.diags[i].message,
|
||||||
|
line: mod.diags[i].line,
|
||||||
|
column: mod.diags[i].column,
|
||||||
|
severity: 0,
|
||||||
|
};
|
||||||
|
Diagnostic_Print(&diag, sourceName);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return mod.diagCount;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Compile a single .bux source file
|
// Compile a single .bux source file
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -389,6 +411,7 @@ func Cli_Compile(source: String, sourceName: String, targetTriple: String) -> St
|
|||||||
PrintLine("Parse failed");
|
PrintLine("Parse failed");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
if Cli_ReportParseDiags(mod, sourceName) > 0 { return ""; }
|
||||||
PrintLine(" Parse done");
|
PrintLine(" Parse done");
|
||||||
|
|
||||||
// Flatten module wrappers: find module decl and hoist its children
|
// Flatten module wrappers: find module decl and hoist its children
|
||||||
@@ -551,6 +574,7 @@ func Cli_Check(srcPath: String) -> int {
|
|||||||
PrintLine("Parse failed");
|
PrintLine("Parse failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if Cli_ReportParseDiags(mod, srcPath) > 0 { return 1; }
|
||||||
PrintLine(" Parse done");
|
PrintLine(" Parse done");
|
||||||
|
|
||||||
// Flatten module wrappers
|
// Flatten module wrappers
|
||||||
@@ -635,6 +659,7 @@ func Cli_CompileSource(source: String, sourceName: String) -> *HirModule {
|
|||||||
PrintLine(sourceName);
|
PrintLine(sourceName);
|
||||||
return null as *HirModule;
|
return null as *HirModule;
|
||||||
}
|
}
|
||||||
|
if Cli_ReportParseDiags(mod, sourceName) > 0 { return null as *HirModule; }
|
||||||
|
|
||||||
// Phase 2b: macro expand
|
// Phase 2b: macro expand
|
||||||
let macEx3: *MacroExpander = MacroExpand_ExpandModule(mod);
|
let macEx3: *MacroExpander = MacroExpand_ExpandModule(mod);
|
||||||
@@ -836,6 +861,10 @@ 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; }
|
||||||
|
// Report parse diags but keep merging the recovered decls: this path
|
||||||
|
// handles stdlib/dependency files, and the parser currently emits
|
||||||
|
// recoverable diags for valid multi-line braced imports there.
|
||||||
|
discard Cli_ReportParseDiags(mod, path);
|
||||||
// Tag every decl from this file for #line maps
|
// Tag every decl from this file for #line maps
|
||||||
var stamp: *Decl = mod.firstItem;
|
var stamp: *Decl = mod.firstItem;
|
||||||
while stamp != null as *Decl {
|
while stamp != null as *Decl {
|
||||||
@@ -1928,6 +1957,8 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
|||||||
userMerged.path = "";
|
userMerged.path = "";
|
||||||
userMerged.itemCount = 0;
|
userMerged.itemCount = 0;
|
||||||
userMerged.firstItem = null as *Decl;
|
userMerged.firstItem = null as *Decl;
|
||||||
|
userMerged.diagCount = 0;
|
||||||
|
userMerged.diags = null as *ParserDiag;
|
||||||
|
|
||||||
// Parse each file and merge declarations into userMerged module
|
// Parse each file and merge declarations into userMerged module
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
@@ -1962,6 +1993,7 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
|||||||
PrintLine(path);
|
PrintLine(path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if Cli_ReportParseDiags(mod, path) > 0 { return 1; }
|
||||||
// Tag decls with this source path for multi-file #line
|
// Tag decls with this source path for multi-file #line
|
||||||
var stampUser: *Decl = mod.firstItem;
|
var stampUser: *Decl = mod.firstItem;
|
||||||
while stampUser != null as *Decl {
|
while stampUser != null as *Decl {
|
||||||
@@ -2031,6 +2063,8 @@ func Cli_BuildProject(projectDir: String, targetTriple: String, isRelease: bool,
|
|||||||
merged.path = "";
|
merged.path = "";
|
||||||
merged.itemCount = 0;
|
merged.itemCount = 0;
|
||||||
merged.firstItem = null as *Decl;
|
merged.firstItem = null as *Decl;
|
||||||
|
merged.diagCount = 0;
|
||||||
|
merged.diags = null as *ParserDiag;
|
||||||
|
|
||||||
// Find and merge ALL stdlib declarations
|
// Find and merge ALL stdlib declarations
|
||||||
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
|
||||||
|
|||||||
+3
-35
@@ -30,13 +30,6 @@ module Parser {
|
|||||||
macroTemplateMode: bool, // allows $(…)* in macro! bodies
|
macroTemplateMode: bool, // allows $(…)* in macro! bodies
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParserDiag {
|
|
||||||
line: uint32,
|
|
||||||
column: uint32,
|
|
||||||
message: String,
|
|
||||||
severity: int, /* 0=error (fatal), 1=warning (recoverable) */
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Token helpers
|
// Token helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -2827,34 +2820,9 @@ module Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print fatal parser diagnostics (severity == 0) or if nothing valid was parsed */
|
// Attach parser diagnostics to the module; the CLI reports them
|
||||||
if p.diagCount > 0 && mod.itemCount == 0 {
|
mod.diagCount = p.diagCount;
|
||||||
var di: int = 0;
|
mod.diags = p.diags;
|
||||||
while di < p.diagCount {
|
|
||||||
let d: ParserDiag = p.diags[di];
|
|
||||||
Print("error: ");
|
|
||||||
PrintLine(d.message);
|
|
||||||
Print(" --> <input>:");
|
|
||||||
PrintInt(d.line as int64);
|
|
||||||
Print(":");
|
|
||||||
PrintInt(d.column as int64);
|
|
||||||
PrintLine("");
|
|
||||||
Print(" |");
|
|
||||||
PrintLine("");
|
|
||||||
Print(" ");
|
|
||||||
PrintInt(d.line as int64);
|
|
||||||
Print(" | <source unavailable>");
|
|
||||||
PrintLine("");
|
|
||||||
Print(" | ");
|
|
||||||
var sp: uint32 = 0;
|
|
||||||
while sp < d.column - 1 && sp < 120 {
|
|
||||||
Print(" ");
|
|
||||||
sp = sp + 1;
|
|
||||||
}
|
|
||||||
PrintLine("^");
|
|
||||||
di = di + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user