From cda20bc8959090898c540496610f1d40b60cdbb2 Mon Sep 17 00:00:00 2001 From: sanjin Date: Fri, 22 May 2026 13:04:49 +0200 Subject: [PATCH] Improve entry main diagnostics --- .llm/BETA_4_LANGUAGE_USABILITY.md | 26 ++++++++++ compiler/src/project.rs | 27 +++++++--- compiler/tests/project_mode.rs | 84 ++++++++++++++++++++++++++++++- docs/POST_BETA_ROADMAP.md | 4 ++ docs/compiler/RELEASE_NOTES.md | 6 ++- 5 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 .llm/BETA_4_LANGUAGE_USABILITY.md diff --git a/.llm/BETA_4_LANGUAGE_USABILITY.md b/.llm/BETA_4_LANGUAGE_USABILITY.md new file mode 100644 index 0000000..bd7a3ab --- /dev/null +++ b/.llm/BETA_4_LANGUAGE_USABILITY.md @@ -0,0 +1,26 @@ +# Beta 4 Language Usability + +## Scope + +This post-`1.0.0-beta.3` slice reduces friction in ordinary project use +without changing the typed core or claiming new syntax stability. + +## Current Work + +- Improve project/workspace build and run entry diagnostics. +- Keep the accepted entry contract unchanged: `(fn main () -> i32 ...)`. +- Use precise diagnostic codes for missing and invalid entry `main` functions. + +## Acceptance Gates + +- `cargo test --test project_mode entry_main` +- `cargo fmt --check` +- `git diff --check` + +## Deferrals + +- No implicit `main` return conversions. +- No argument-taking `main`. +- No async, package registry, or stable ABI/layout promises. +- Broader type aliases, match diagnostics, and destructuring remain candidate + follow-up items in the same language usability roadmap lane. diff --git a/compiler/src/project.rs b/compiler/src/project.rs index c530030..4b7bba7 100644 --- a/compiler/src/project.rs +++ b/compiler/src/project.rs @@ -4156,8 +4156,11 @@ fn add_entry_wrapper( else { diagnostics.push(Diagnostic::new( &entry_module.file, - "MissingImport", - format!("entry module `{}` has no `main` function", manifest.entry), + "ProjectEntryMainMissing", + format!( + "entry module `{}` has no `main` function; build/run require `(fn main () -> i32 ...)`", + manifest.entry + ), )); return; }; @@ -4166,8 +4169,12 @@ fn add_entry_wrapper( diagnostics.push( Diagnostic::new( &entry_module.file, - "MissingImport", - "project entry `main` must have no parameters and return `i32`", + "ProjectEntryMainInvalidSignature", + format!( + "project entry `main` must have signature `(fn main () -> i32 ...)`; found {} parameter(s) and return `{}`", + entry_function.params.len(), + entry_function.return_type + ), ) .with_span(entry_function.span), ); @@ -4229,9 +4236,9 @@ fn add_workspace_entry_wrapper( else { diagnostics.push(Diagnostic::new( &entry_module.file, - "MissingImport", + "WorkspaceEntryMainMissing", format!( - "entry module `{}` in package `{}` has no `main` function", + "entry module `{}` in package `{}` has no `main` function; build/run require `(fn main () -> i32 ...)`", package.manifest.entry, package.manifest.name ), )); @@ -4242,8 +4249,12 @@ fn add_workspace_entry_wrapper( diagnostics.push( Diagnostic::new( &entry_module.file, - "MissingImport", - "workspace entry `main` must have no parameters and return `i32`", + "WorkspaceEntryMainInvalidSignature", + format!( + "workspace entry `main` must have signature `(fn main () -> i32 ...)`; found {} parameter(s) and return `{}`", + entry_function.params.len(), + entry_function.return_type + ), ) .with_span(entry_function.span), ); diff --git a/compiler/tests/project_mode.rs b/compiler/tests/project_mode.rs index 423b53c..6c94ede 100644 --- a/compiler/tests/project_mode.rs +++ b/compiler/tests/project_mode.rs @@ -587,6 +587,68 @@ fn workspace_build_requires_one_entry_package() { ); } +#[test] +fn workspace_build_reports_entry_main_contract() { + let missing_main = write_workspace( + "workspace-missing-entry-main", + "[workspace]\nmembers = [\"packages/app\"]\n", + &[WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n", + modules: &[("main", "(module main)\n\n(test \"ok\"\n true)\n")], + }], + ); + let missing_binary = unique_path("workspace-missing-entry-main-bin"); + let missing_output = run_glagol([ + "build".as_ref(), + "-o".as_ref(), + missing_binary.as_os_str(), + missing_main.as_os_str(), + ]); + assert_exit_code("workspace missing entry main", &missing_output, 1); + assert_stderr_contains( + "workspace missing entry main", + &missing_output, + "WorkspaceEntryMainMissing", + ); + assert_stderr_contains( + "workspace missing entry main", + &missing_output, + "build/run require `(fn main () -> i32 ...)`", + ); + + let bad_signature = write_workspace( + "workspace-bad-entry-main", + "[workspace]\nmembers = [\"packages/app\"]\n", + &[WorkspacePackageSpec { + member: "packages/app", + manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n", + modules: &[( + "main", + "(module main)\n\n(fn main () -> string\n \"bad\")\n", + )], + }], + ); + let bad_binary = unique_path("workspace-bad-entry-main-bin"); + let bad_output = run_glagol([ + "build".as_ref(), + "-o".as_ref(), + bad_binary.as_os_str(), + bad_signature.as_os_str(), + ]); + assert_exit_code("workspace bad entry main", &bad_output, 1); + assert_stderr_contains( + "workspace bad entry main", + &bad_output, + "WorkspaceEntryMainInvalidSignature", + ); + assert_stderr_contains( + "workspace bad entry main", + &bad_output, + "found 0 parameter(s) and return `string`", + ); +} + #[test] fn workspace_package_boundaries_are_diagnostics() { let missing = write_workspace( @@ -1244,7 +1306,16 @@ fn project_build_requires_entry_main_contract() { missing_main.as_os_str(), ]); assert_exit_code("missing entry main", &missing_output, 1); - assert_stderr_contains("missing entry main", &missing_output, "MissingImport"); + assert_stderr_contains( + "missing entry main", + &missing_output, + "ProjectEntryMainMissing", + ); + assert_stderr_contains( + "missing entry main", + &missing_output, + "build/run require `(fn main () -> i32 ...)`", + ); let bad_signature = write_project( "bad-main-signature", @@ -1259,7 +1330,16 @@ fn project_build_requires_entry_main_contract() { bad_signature.as_os_str(), ]); assert_exit_code("bad entry main signature", &bad_output, 1); - assert_stderr_contains("bad entry main signature", &bad_output, "MissingImport"); + assert_stderr_contains( + "bad entry main signature", + &bad_output, + "ProjectEntryMainInvalidSignature", + ); + assert_stderr_contains( + "bad entry main signature", + &bad_output, + "found 1 parameter(s) and return `i32`", + ); } #[test] diff --git a/docs/POST_BETA_ROADMAP.md b/docs/POST_BETA_ROADMAP.md index 16c9c12..fd67179 100644 --- a/docs/POST_BETA_ROADMAP.md +++ b/docs/POST_BETA_ROADMAP.md @@ -113,6 +113,10 @@ Candidate features: Why fourth: these features improve real Slovo code while keeping generics and traits deferred until the core has more feedback. +Started on `main` after `1.0.0-beta.3`: project/workspace build and run entry +diagnostics now use entry-specific codes and explicitly show the required +`(fn main () -> i32 ...)` contract. + ### 5. Package And Workspace Discipline Goal: make multi-package local development predictable before remote registry diff --git a/docs/compiler/RELEASE_NOTES.md b/docs/compiler/RELEASE_NOTES.md index 04a0b29..b7b6c29 100644 --- a/docs/compiler/RELEASE_NOTES.md +++ b/docs/compiler/RELEASE_NOTES.md @@ -10,7 +10,11 @@ integration/readiness release, not the first real beta. ## Unreleased -No unreleased changes yet. +- Project build/run entry diagnostics now use entry-specific codes: + `ProjectEntryMainMissing`, `ProjectEntryMainInvalidSignature`, + `WorkspaceEntryMainMissing`, and `WorkspaceEntryMainInvalidSignature`. + Messages now spell out the required `(fn main () -> i32 ...)` contract and + include the found parameter count and return type for invalid signatures. ## 1.0.0-beta.3