diff --git a/.cargo/config.toml b/.cargo/config.toml index be883d3e2fd..e604c11a108 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,12 +1,8 @@ [build] rustflags = [ - "-Zproc-macro-backtrace", # Flag to make build.rs scripts generate docs. Should only be used in this repository # internally, not by dependants. '--cfg=HYDROFLOW_GENERATE_DOCS', - # https://github.com/rust-lang/rust-clippy/issues/10087 - ## TODO(mingwei): Need rust-analyzer support: - # "-Aclippy::uninlined-format-args", ] [target.x86_64-apple-darwin] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7af071b226a..40e4c9ad330 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,13 +36,13 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - rust_release: [pinned-nightly, latest-nightly] + rust_release: [pinned-nightly, latest-stable] exclude: # For non-pull requests, event_name != 'pull_request' will be true, and 'nothing' is # truthy, so the entire && operator will resolve to 'nothing'. Then the || operator will # resolve to 'nothing' so we will exclude 'nothing'. https://stackoverflow.com/a/73822998 - rust_release: ${{ (needs.pre_job.outputs.should_skip != 'true' && 'nothing') || 'pinned-nightly' }} - - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-nightly' }} + - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-stable' }} env: CARGO_TERM_COLOR: always @@ -55,12 +55,12 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Install nightly toolchain + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly - override: ${{ matrix.rust_release == 'latest-nightly' }} + toolchain: stable + override: ${{ matrix.rust_release == 'latest-stable' }} components: rustfmt, clippy - name: Run sccache-cache @@ -96,25 +96,25 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust_release: [pinned-nightly, latest-nightly] + rust_release: [pinned-nightly, latest-stable] exclude: # For non-pull requests, event_name != 'pull_request' will be true, and 'nothing' is # truthy, so the entire && operator will resolve to 'nothing'. Then the || operator will # resolve to 'nothing' so we will exclude 'nothing'. https://stackoverflow.com/a/73822998 - rust_release: ${{ (needs.pre_job.outputs.should_skip != 'true' && 'nothing') || 'pinned-nightly' }} - - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-nightly' }} + - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-stable' }} steps: - name: Checkout sources uses: actions/checkout@v3 - - name: Install nightly toolchain + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: stable target: wasm32-unknown-unknown - override: ${{ matrix.rust_release == 'latest-nightly' }} + override: ${{ matrix.rust_release == 'latest-stable' }} - name: Check hydroflow_lang uses: actions-rs/cargo@v1 @@ -132,13 +132,13 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - rust_release: [pinned-nightly, latest-nightly] + rust_release: [pinned-nightly, latest-stable] exclude: # For non-pull requests, event_name != 'pull_request' will be true, and 'nothing' is # truthy, so the entire && operator will resolve to 'nothing'. Then the || operator will # resolve to 'nothing' so we will exclude 'nothing'. https://stackoverflow.com/a/73822998 - rust_release: ${{ (needs.pre_job.outputs.should_skip != 'true' && 'nothing') || 'pinned-nightly' }} - - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-nightly' }} + - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-stable' }} - os: ${{ (github.event_name != 'pull_request' && 'nothing') || 'windows-latest' }} env: @@ -152,12 +152,12 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Install nightly toolchain + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly - override: ${{ matrix.rust_release == 'latest-nightly' }} + toolchain: stable + override: ${{ matrix.rust_release == 'latest-stable' }} - name: Run sccache-cache if: matrix.rust_release == 'pinned-nightly' @@ -182,6 +182,10 @@ jobs: - name: Run cargo nextest on all targets run: cargo nextest run --no-fail-fast --features python --features deploy --all-targets + env: + # On stable the output messages will often not match pinned-nightly, so we set to 'overwrite'. + TRYBUILD: ${{ matrix.rust_release == 'pinned-nightly' && 'wip' || 'overwrite' }} + HYDROFLOW_EXPECT_WARNINGS: ${{ matrix.rust_release == 'pinned-nightly' && 'noop' || 'ignore' }} - name: Run doctests run: cargo test --no-fail-fast --features python --features deploy --doc @@ -217,7 +221,7 @@ jobs: cd python_tests pip install -r requirements.txt RUST_BACKTRACE=1 pytest - + - name: Run Python tests (Windows) if: ${{ matrix.os == 'windows-latest' }} run: | @@ -235,25 +239,25 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust_release: [pinned-nightly, latest-nightly] + rust_release: [pinned-nightly, latest-stable] exclude: # For non-pull requests, event_name != 'pull_request' will be true, and 'nothing' is # truthy, so the entire && operator will resolve to 'nothing'. Then the || operator will # resolve to 'nothing' so we will exclude 'nothing'. https://stackoverflow.com/a/73822998 - rust_release: ${{ (needs.pre_job.outputs.should_skip != 'true' && 'nothing') || 'pinned-nightly' }} - - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-nightly' }} + - rust_release: ${{ (github.event_name != 'pull_request' && 'nothing') || 'latest-stable' }} steps: - name: Checkout sources uses: actions/checkout@v3 - - name: Install nightly toolchain + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly + toolchain: stable target: wasm32-unknown-unknown - override: ${{ matrix.rust_release == 'latest-nightly' }} + override: ${{ matrix.rust_release == 'latest-stable' }} - name: Get wasm-bindgen version id: wasm-bindgen-version @@ -366,11 +370,10 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Install nightly toolchain + - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly - name: Checkout gh-pages shell: bash diff --git a/Cargo.lock b/Cargo.lock index ca8aab2608b..d97a41f0818 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1560,6 +1560,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", + "rustc_version 0.4.1", "serde", "serde_json", "slotmap", @@ -1576,6 +1577,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", + "rustc_version 0.4.1", "syn 2.0.75", ] @@ -2633,7 +2635,7 @@ dependencies = [ "byteorder", "libc", "nom 2.2.1", - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -3067,6 +3069,15 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", +] + [[package]] name = "rustix" version = "0.38.34" diff --git a/benches/benches/fork_join.rs b/benches/benches/fork_join.rs index a65171c5143..4e3ba4c776d 100644 --- a/benches/benches/fork_join.rs +++ b/benches/benches/fork_join.rs @@ -86,9 +86,7 @@ fn benchmark_hydroflow(c: &mut Criterion) { fn benchmark_hydroflow_surface(c: &mut Criterion) { c.bench_function("fork_join/hydroflow/surface", |b| { b.iter(|| { - let mut hf = hydroflow_syntax! { - source_iter(0..NUM_INTS) -> import!("fork_join_20.hf") -> for_each(|x| { black_box(x); }); - }; + let mut hf = include!("fork_join_20.hf"); hf.run_available(); }) }); diff --git a/benches/build.rs b/benches/build.rs index 90ded262aea..fecfaec8674 100644 --- a/benches/build.rs +++ b/benches/build.rs @@ -19,8 +19,8 @@ pub fn fork_join() -> std::io::Result<()> { let file = File::create(path)?; let mut write = BufWriter::new(file); - writeln!(write, "a0 = mod -> tee();")?; - + writeln!(write, "hydroflow_syntax! {{")?; + writeln!(write, "a0 = source_iter(0..NUM_INTS) -> tee();")?; for i in 0..NUM_OPS { if i > 0 { writeln!(write, "a{} = union() -> tee();", i)?; @@ -28,8 +28,12 @@ pub fn fork_join() -> std::io::Result<()> { writeln!(write, "a{} -> filter(|x| x % 2 == 0) -> a{};", i, i + 1)?; writeln!(write, "a{} -> filter(|x| x % 2 == 1) -> a{};", i, i + 1)?; } - - writeln!(write, "a{} = union() -> mod;", NUM_OPS)?; + writeln!( + write, + "a{} = union() -> for_each(|x| {{ black_box(x); }});", + NUM_OPS + )?; + writeln!(write, "}}")?; write.flush()?; diff --git a/build_docs.bash b/build_docs.bash index 478f93614b0..dd5e17516c6 100644 --- a/build_docs.bash +++ b/build_docs.bash @@ -1,8 +1,8 @@ set -e -PLATFORM=${1:-"x86_64-linux-gnu-ubuntu-16.04"} +PLATFORM=${1:-"x86_64-linux-gnu-ubuntu-20.04"} -wget -qO- https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-$PLATFORM.tar.xz | tar xJ +wget -qO- https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-$PLATFORM.tar.xz | tar xJ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/hydroflow/Cargo.toml b/hydroflow/Cargo.toml index 056d96b1c3d..e8d74c3a8a5 100644 --- a/hydroflow/Cargo.toml +++ b/hydroflow/Cargo.toml @@ -11,9 +11,8 @@ description = "Hydro's low-level dataflow runtime and IR" workspace = true [features] -default = [ "macros", "nightly", "debugging" ] +default = [ "macros", "debugging" ] -nightly = [ "hydroflow_macro", "hydroflow_macro/diagnostics" ] macros = [ "hydroflow_macro", "hydroflow_datalog" ] hydroflow_macro = [ "dep:hydroflow_macro" ] hydroflow_datalog = [ "dep:hydroflow_datalog" ] @@ -29,14 +28,6 @@ required-features = [ "nightly" ] name = "python_udf" required-features = [ "python" ] -[[example]] -name = "modules_outer_join" -required-features = [ "debugging" ] - -[[example]] -name = "modules_triple_cross_join" -required-features = [ "debugging" ] - [dependencies] bincode = "1.3.1" byteorder = "1.3.2" diff --git a/hydroflow/examples/modules_outer_join/full_outer_join.hf b/hydroflow/examples/modules_outer_join/full_outer_join.hf deleted file mode 100644 index 5d88150dd1a..00000000000 --- a/hydroflow/examples/modules_outer_join/full_outer_join.hf +++ /dev/null @@ -1,23 +0,0 @@ -lhs = mod[0] -> tee(); -rhs = mod[1] -> tee(); - -lhs -> [0]joined; -rhs -> [1]joined; - -joined = join() -> map(|(k, (lhs, rhs))| (k, (Some(lhs), Some(rhs)))) -> combined; - -lhs -> [pos]missed_lhs; -rhs -> map(|(k, _v)| k) -> [neg]missed_lhs; - -missed_lhs = anti_join() - -> map(|(k, v)| (k, (Some(v), None))) - -> combined; - -rhs -> [pos]missed_rhs; -lhs -> map(|(k, _v)| k) -> [neg]missed_rhs; - -missed_rhs = anti_join() - -> map(|(k, v)| (k, (None, Some(v)))) - -> combined; - -combined = union() -> mod; diff --git a/hydroflow/examples/modules_outer_join/left_outer_join.hf b/hydroflow/examples/modules_outer_join/left_outer_join.hf deleted file mode 100644 index c6e159ed286..00000000000 --- a/hydroflow/examples/modules_outer_join/left_outer_join.hf +++ /dev/null @@ -1,16 +0,0 @@ -lhs = mod[0] -> tee(); -rhs = mod[1] -> tee(); - -lhs -> [0]joined; -rhs -> [1]joined; - -joined = join() -> map(|(k, (lhs, rhs))| (k, (lhs, Some(rhs)))) -> combined; - -lhs -> [pos]missed; -rhs -> map(|(k, _v)| k) -> [neg]missed; - -missed = anti_join() - -> map(|(k, v)| (k, (v, None))) - -> combined; - -combined = union() -> mod; diff --git a/hydroflow/examples/modules_outer_join/main.rs b/hydroflow/examples/modules_outer_join/main.rs deleted file mode 100644 index 7d84b529687..00000000000 --- a/hydroflow/examples/modules_outer_join/main.rs +++ /dev/null @@ -1,30 +0,0 @@ -use hydroflow::hydroflow_syntax; - -pub fn main() { - let mut df = hydroflow_syntax! { - lhs = source_iter([("a", 0), ("b", 1)]) -> tee(); - rhs = source_iter([("a", 2), ("c", 3)]) -> tee(); - - lhs -> [0]inner_join; - rhs -> [1]inner_join; - inner_join = join() -> assert_eq([("a", (0, 2))]); - - lhs -> [0]left_outer_join; - rhs -> [1]left_outer_join; - left_outer_join = import!("left_outer_join.hf") -> assert_eq([("a", (0, Some(2))), ("b", (1, None))]); - - lhs -> [0]right_outer_join; - rhs -> [1]right_outer_join; - right_outer_join = import!("right_outer_join.hf") -> assert_eq([("a", (Some(0), 2)), ("c", (None, 3))]); - - lhs -> [0]full_outer_join; - rhs -> [1]full_outer_join; - full_outer_join = import!("full_outer_join.hf") -> assert_eq([("a", (Some(0), Some(2))), ("b", (Some(1), None)), ("c", (None, Some(3)))]); - }; - df.run_available(); -} - -#[test] -fn test() { - main(); -} diff --git a/hydroflow/examples/modules_outer_join/right_outer_join.hf b/hydroflow/examples/modules_outer_join/right_outer_join.hf deleted file mode 100644 index e90597b39a6..00000000000 --- a/hydroflow/examples/modules_outer_join/right_outer_join.hf +++ /dev/null @@ -1,6 +0,0 @@ -// flip the lhs and rhs -mod[0] -> [1]left_outer_join; -mod[1] -> [0]left_outer_join; - -// flip them back -left_outer_join = import!("left_outer_join.hf") -> map(|(k, (v1, v2))| (k, (v2, v1))) -> mod; diff --git a/hydroflow/examples/modules_triple_cross_join/main.rs b/hydroflow/examples/modules_triple_cross_join/main.rs deleted file mode 100644 index d6084e0af28..00000000000 --- a/hydroflow/examples/modules_triple_cross_join/main.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::cell::RefCell; -use std::rc::Rc; - -use hydroflow::hydroflow_syntax; -use hydroflow::scheduled::graph::Hydroflow; -use hydroflow::util::multiset::HashMultiSet; - -pub fn main() { - let output = Rc::new(RefCell::new( - HashMultiSet::<(usize, usize, usize)>::default(), - )); - - let mut df: Hydroflow = { - let output = output.clone(); - hydroflow_syntax! { - source_iter(0..2) -> [0]cj; - source_iter(0..2) -> [1]cj; - source_iter(0..2) -> [2]cj; - - cj = import!("triple_cross_join.hf") - -> for_each(|x| output.borrow_mut().insert(x)); - } - }; - - println!( - "{}", - df.meta_graph().unwrap().to_mermaid(&Default::default()) - ); - - df.run_available(); - - #[rustfmt::skip] - assert_eq!( - *output.borrow(), - HashMultiSet::from_iter([ - (0, 0, 0), - (0, 0, 1), - (0, 1, 0), - (0, 1, 1), - (1, 0, 0), - (1, 0, 1), - (1, 1, 0), - (1, 1, 1), - ]) - ); -} - -#[test] -fn test() { - main(); -} diff --git a/hydroflow/examples/modules_triple_cross_join/triple_cross_join.hf b/hydroflow/examples/modules_triple_cross_join/triple_cross_join.hf deleted file mode 100644 index a5126ec2e34..00000000000 --- a/hydroflow/examples/modules_triple_cross_join/triple_cross_join.hf +++ /dev/null @@ -1,15 +0,0 @@ -mod[0] - -> [0]cj1; - -mod[1] - -> [1]cj1; - -cj1 = cross_join() - -> [0]cj2; - -mod[2] - -> [1]cj2; - -cj2 = cross_join() - -> map(|((a, b), c)| (a, b, c)) - -> mod; diff --git a/hydroflow/src/declarative_macro.rs b/hydroflow/src/declarative_macro.rs index 54d790c9c7b..12f8d3ece97 100644 --- a/hydroflow/src/declarative_macro.rs +++ b/hydroflow/src/declarative_macro.rs @@ -55,24 +55,32 @@ macro_rules! hydroflow_expect_warnings { $( , )? ) => { { - let __file = std::file!(); - let __line = std::line!() as usize; - let __hf = hydroflow::hydroflow_syntax_noemit! $hf; + fn emit(msg: impl ::std::convert::AsRef) { + if Ok("ignore") == ::std::env::var("HYDROFLOW_EXPECT_WARNINGS").as_deref() { + eprintln!("{}", msg.as_ref()); + } else { + panic!("{}", msg.as_ref()); + } + } + + let __file = ::std::file!(); + let __line = ::std::line!() as usize; + let __hf = $crate::hydroflow_syntax_noemit! $hf; let actuals = __hf.diagnostics().expect("Expected `diagnostics()` to be set."); let actuals_len = actuals.len(); - let actuals = std::collections::BTreeSet::from_iter(actuals.iter().cloned().map(|mut actual| { + let actuals = ::std::collections::BTreeSet::from_iter(actuals.iter().cloned().map(|mut actual| { actual.span.line = actual.span.line.saturating_sub(__line); - std::borrow::Cow::<'static, str>::Owned(actual.to_string().replace(__file, "$FILE")) + ::std::borrow::Cow::<'static, str>::Owned(actual.to_string().replace(__file, "$FILE")) })); let expecteds = [ $( - std::borrow::Cow::Borrowed( $msg ), + ::std::borrow::Cow::Borrowed( $msg ), )* ]; let expecteds_len = expecteds.len(); - let expecteds = std::collections::BTreeSet::from(expecteds); + let expecteds = ::std::collections::BTreeSet::from(expecteds); let missing_errs = expecteds.difference(&actuals).map(|missing| { format!("Expected diagnostic `{}` was not emitted.", missing) @@ -80,13 +88,18 @@ macro_rules! hydroflow_expect_warnings { let extra_errs = actuals.difference(&expecteds).map(|extra| { format!("Unexpected extra diagnostic `{}` was emitted", extra) }); - let all_errs: Vec<_> = missing_errs.chain(extra_errs).collect(); + let all_errs: ::std::vec::Vec<_> = missing_errs.chain(extra_errs).collect(); if !all_errs.is_empty() { - panic!("{}", all_errs.join("\n")); + emit(all_errs.join("\n")); } - // TODO(mingwei): fix duplicates generated from multi-pass flow prop algorithm. - // assert_eq!(actuals_len, expecteds_len, "Wrong nubmer of diagnostics, were there duplicates?"); + if actuals_len != expecteds_len { + emit(format!( + "Number of expected warnings ({}) does not match number of actual warnings ({}), were there duplicates?", + expecteds_len, + actuals_len + )); + } __hf } diff --git a/hydroflow/src/lib.rs b/hydroflow/src/lib.rs index 2970b129f08..effc596dc57 100644 --- a/hydroflow/src/lib.rs +++ b/hydroflow/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "nightly", feature(never_type))] #![warn(missing_docs)] //! Hydroflow is a low-level dataflow-based runtime system for the [Hydro Project](https://hydro.run/). @@ -39,12 +38,9 @@ pub use hydroflow_macro::{ hydroflow_test as test, monotonic_fn, morphism, DemuxEnum, }; +// TODO(mingwei): Use the [nightly "never" type `!`](https://doc.rust-lang.org/std/primitive.never.html) /// Stand-in for the [nightly "never" type `!`](https://doc.rust-lang.org/std/primitive.never.html) -#[cfg(not(feature = "nightly"))] pub type Never = std::convert::Infallible; -/// The [nightly "never" type `!`](https://doc.rust-lang.org/std/primitive.never.html) -#[cfg(feature = "nightly")] -pub type Never = !; #[cfg(doctest)] mod booktest { diff --git a/hydroflow/tests/surface_warnings.rs b/hydroflow/tests/surface_warnings.rs index fa9826083cd..0b73113b271 100644 --- a/hydroflow/tests/surface_warnings.rs +++ b/hydroflow/tests/surface_warnings.rs @@ -1,7 +1,3 @@ -// TODO(mingwei): fix line numbers in tests -// https://github.com/hydro-project/hydroflow/issues/729 -// https://github.com/rust-lang/rust/pull/111571 - use std::cell::RefCell; use std::rc::Rc; diff --git a/hydroflow_datalog/Cargo.toml b/hydroflow_datalog/Cargo.toml index 58a62cab9d3..c4771f901c7 100644 --- a/hydroflow_datalog/Cargo.toml +++ b/hydroflow_datalog/Cargo.toml @@ -14,9 +14,6 @@ workspace = true proc-macro = true path = "src/lib.rs" -[features] -diagnostics = [ "hydroflow_datalog_core/diagnostics" ] - [dependencies] quote = "1.0.35" syn = { version = "2.0.46", features = [ "parsing", "extra-traits" ] } diff --git a/hydroflow_datalog/src/lib.rs b/hydroflow_datalog/src/lib.rs index f1bd816360f..919115bd986 100644 --- a/hydroflow_datalog/src/lib.rs +++ b/hydroflow_datalog/src/lib.rs @@ -1,3 +1,4 @@ +use hydroflow_datalog_core::diagnostic::Diagnostic; use hydroflow_datalog_core::{gen_hydroflow_graph, hydroflow_graph_to_program}; use proc_macro2::Span; use quote::{quote, ToTokens}; @@ -31,10 +32,15 @@ pub fn datalog(item: proc_macro::TokenStream) -> proc_macro::TokenStream { program.to_token_stream().into() } Err(diagnostics) => { - for diagnostic in diagnostics { - diagnostic.emit(); - } - proc_macro::TokenStream::from(quote!(hydroflow::scheduled::graph::Hydroflow::new())) + let diagnostic_tokens = Diagnostic::try_emit_all(diagnostics.iter()) + .err() + .unwrap_or_default(); + proc_macro::TokenStream::from(quote! { + { + #diagnostic_tokens + hydroflow::scheduled::graph::Hydroflow::new() + } + }) } } } diff --git a/hydroflow_datalog_core/Cargo.toml b/hydroflow_datalog_core/Cargo.toml index 79b541b1bf3..ab7a2b1a768 100644 --- a/hydroflow_datalog_core/Cargo.toml +++ b/hydroflow_datalog_core/Cargo.toml @@ -13,10 +13,6 @@ workspace = true [lib] path = "src/lib.rs" -[features] -default = [] -diagnostics = [ "hydroflow_lang/diagnostics" ] - [dependencies] quote = "1.0.35" slotmap = "1.0.0" diff --git a/hydroflow_datalog_core/src/lib.rs b/hydroflow_datalog_core/src/lib.rs index 27203bdd6a3..64835e74ab6 100644 --- a/hydroflow_datalog_core/src/lib.rs +++ b/hydroflow_datalog_core/src/lib.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use std::ops::Deref; +pub use hydroflow_lang::diagnostic; use hydroflow_lang::diagnostic::{Diagnostic, Level}; use hydroflow_lang::graph::{ eliminate_extra_unions_tees, partition_graph, FlatGraphBuilder, HydroflowGraph, diff --git a/hydroflow_lang/Cargo.toml b/hydroflow_lang/Cargo.toml index b1321b5ebf7..256d2b9269b 100644 --- a/hydroflow_lang/Cargo.toml +++ b/hydroflow_lang/Cargo.toml @@ -12,7 +12,6 @@ workspace = true [features] default = [] -diagnostics = [] debugging = [ "dep:data-encoding", "dep:webbrowser", "clap-derive" ] clap-derive = [ "dep:clap" ] @@ -34,3 +33,4 @@ webbrowser = { version = "1.0.0", optional = true } [build-dependencies] syn = { version = "2.0.46", features = [ "extra-traits", "full", "parsing" ] } +rustc_version = "0.4.0" diff --git a/hydroflow_lang/build.rs b/hydroflow_lang/build.rs index d2e0af6f848..fe39425917a 100644 --- a/hydroflow_lang/build.rs +++ b/hydroflow_lang/build.rs @@ -4,6 +4,7 @@ use std::fs::File; use std::io::{BufWriter, Error, ErrorKind, Result, Write}; use std::path::PathBuf; +use rustc_version::{version_meta, Channel}; use syn::{ parse_quote, AttrStyle, Expr, ExprLit, Ident, Item, Lit, Member, Meta, MetaNameValue, Path, }; @@ -12,6 +13,15 @@ const OPS_PATH: &str = "src/graph/ops"; fn main() { println!("cargo::rerun-if-changed={}", OPS_PATH); + + println!("cargo::rustc-check-cfg=cfg(nightly)"); + if matches!( + version_meta().map(|meta| meta.channel), + Ok(Channel::Nightly) + ) { + println!("cargo:rustc-cfg=nightly"); + } + if Err(VarError::NotPresent) != var("CARGO_CFG_HYDROFLOW_GENERATE_DOCS") { if let Err(err) = generate_op_docs() { eprintln!("hydroflow_lang/build.rs error: {:?}", err); diff --git a/hydroflow_lang/src/diagnostic.rs b/hydroflow_lang/src/diagnostic.rs index 44ec8c588a8..1ac175d9e50 100644 --- a/hydroflow_lang/src/diagnostic.rs +++ b/hydroflow_lang/src/diagnostic.rs @@ -1,5 +1,7 @@ //! Compatibility for `proc_macro` diagnostics, which are missing from [`proc_macro2`]. +extern crate proc_macro; + use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -40,8 +42,8 @@ impl Level { /// Diagnostic. A warning or error (or lower [`Level`]) with a message and span. Shown by IDEs /// usually as a squiggly red or yellow underline. /// -/// Must call [`Diagnostic::emit`] or manually emit the output of [`Diagnostic::to_tokens`] for the -/// diagnostic to show up. +/// Diagnostics must be emitted via [`Diagnostic::try_emit`], [`Diagnostic::to_tokens`], or +/// [`Diagnostic::try_emit_all`] for diagnostics to show up. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Diagnostic { /// Span (source code location). @@ -68,11 +70,11 @@ impl Diagnostic { } } - /// Emit the diagnostic. Only works from the `proc_macro` context. Does not work outside of - /// that e.g. in normal runtime execution or in tests. - pub fn emit(&self) { - #[cfg(feature = "diagnostics")] - { + /// Emit if possible, otherwise return `Err` containing a [`TokenStream`] of a + /// `compile_error!(...)` call. + pub fn try_emit(&self) -> Result<(), TokenStream> { + #[cfg(nightly)] + if proc_macro::is_available() { let pm_diag = match self.level { Level::Error => self.span.unwrap().error(&*self.message), Level::Warning => self.span.unwrap().warning(&*self.message), @@ -80,11 +82,32 @@ impl Diagnostic { Level::Help => self.span.unwrap().help(&*self.message), }; pm_diag.emit(); + return Ok(()); } + Err(self.to_tokens()) } - /// Used to emulate [`Diagnostic::emit`] by turning this diagnostic into a properly spanned [`TokenStream`] - /// that emits an error with this diagnostic's message. + /// Emits all if possible, otherwise returns `Err` containing a [`TokenStream`] of + /// `compile_error!(...)` calls. + pub fn try_emit_all<'a>( + diagnostics: impl IntoIterator, + ) -> Result<(), TokenStream> { + if let Some(tokens) = diagnostics + .into_iter() + .filter_map(|diag| diag.try_emit().err()) + .reduce(|mut tokens, next| { + tokens.extend(next); + tokens + }) + { + Err(tokens) + } else { + Ok(()) + } + } + + /// Used to emulate `proc_macro::Diagnostic::emit` by turning this diagnostic into a properly spanned [`TokenStream`] + /// that emits an error via `compile_error!(...)` with this diagnostic's message. pub fn to_tokens(&self) -> TokenStream { let msg_lit: Literal = Literal::string(&self.message); let unique_ident = { @@ -98,7 +121,7 @@ impl Diagnostic { if Level::Error == self.level { quote_spanned! {self.span=> { - ::std::compile_error!(#msg_lit); + ::core::compile_error!(#msg_lit); } } } else { @@ -169,17 +192,24 @@ pub struct SerdeSpan { } impl From for SerdeSpan { fn from(span: Span) -> Self { - #[cfg(feature = "diagnostics")] - let path = span - .unwrap() - .source_file() - .path() - .display() - .to_string() - .into(); + #[cfg_attr( + not(nightly), + expect(unused_labels, reason = "conditional compilation") + )] + let path = 'a: { + #[cfg(nightly)] + if proc_macro::is_available() { + break 'a span + .unwrap() + .source_file() + .path() + .display() + .to_string() + .into(); + } - #[cfg(not(feature = "diagnostics"))] - let path = "unknown".into(); + "unknown".into() + }; Self { path, diff --git a/hydroflow_lang/src/graph/flat_graph_builder.rs b/hydroflow_lang/src/graph/flat_graph_builder.rs index 10c90a46c1a..8c01818c314 100644 --- a/hydroflow_lang/src/graph/flat_graph_builder.rs +++ b/hydroflow_lang/src/graph/flat_graph_builder.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; -use std::path::PathBuf; use itertools::Itertools; use proc_macro2::Span; @@ -71,9 +70,6 @@ pub struct FlatGraphBuilder { /// Use statements. uses: Vec, - /// In order to make import!() statements relative to the current file, we need to know where the file is that is building the flat graph. - invocating_file_path: PathBuf, - /// If the flat graph is being loaded as a module, then two initial ModuleBoundary nodes are inserted into the graph. One /// for the input into the module and one for the output out of the module. module_boundary_nodes: Option<(GraphNodeId, GraphNodeId)>, @@ -86,37 +82,8 @@ impl FlatGraphBuilder { } /// Convert the Hydroflow code AST into a graph builder. - pub fn from_hfcode(input: HfCode, macro_invocation_path: PathBuf) -> Self { - let mut builder = Self { - invocating_file_path: macro_invocation_path, - ..Default::default() - }; - builder.process_statements(input.statements); - builder - } - - /// Convert the Hydroflow code AST into a graph builder. - pub fn from_hfmodule(input: HfCode, root_path: PathBuf) -> Self { + pub fn from_hfcode(input: HfCode) -> Self { let mut builder = Self::default(); - builder.invocating_file_path = root_path; // imports inside of modules should be relative to the importing file. - builder.module_boundary_nodes = Some(( - builder.flat_graph.insert_node( - GraphNode::ModuleBoundary { - input: true, - import_expr: Span::call_site(), - }, - Some(Ident::new("input", Span::call_site())), - None, - ), - builder.flat_graph.insert_node( - GraphNode::ModuleBoundary { - input: false, - import_expr: Span::call_site(), - }, - Some(Ident::new("output", Span::call_site())), - None, - ), - )); builder.process_statements(input.statements); builder } @@ -276,118 +243,7 @@ impl FlatGraphBuilder { out: Some((PortIndexValue::Elided(op_span), GraphDet::Determined(nid))), } } - Pipeline::Import(import) => { - // TODO: https://github.com/rust-lang/rfcs/pull/3200 - // this would be way better... - let file_path = { - let mut dir = self.invocating_file_path.clone(); - dir.pop(); - dir.join(import.filename.value()) - }; - - let file_contents = match std::fs::read_to_string(&file_path) { - Ok(contents) => contents, - Err(err) => { - self.diagnostics.push(Diagnostic::spanned( - import.filename.span(), - Level::Error, - format!("filename: {}, err: {err}", import.filename.value()), - )); - - return Ends { - inn: None, - out: None, - }; - } - }; - - let statements = match syn::parse_str::(&file_contents) { - Ok(code) => code, - Err(err) => { - self.diagnostics.push(Diagnostic::spanned( - import.span(), - Level::Error, - format!("Error in module: {}", err), - )); - - return Ends { - inn: None, - out: None, - }; - } - }; - - let flat_graph_builder = FlatGraphBuilder::from_hfmodule(statements, file_path); - let (flat_graph, _uses, diagnostics) = flat_graph_builder.build(); - diagnostics.iter().for_each(Diagnostic::emit); - - self.merge_in(flat_graph, import.span()) - } - } - } - - /// Merge one flatgraph into the current flatgraph - /// other must be a flatgraph and not be partitioned yet. - fn merge_in(&mut self, other: HydroflowGraph, parent_span: Span) -> Ends { - assert_eq!(other.subgraphs().count(), 0); - - let mut ends = Ends { - inn: None, - out: None, - }; - - let mut node_mapping = BTreeMap::new(); - - for (other_node_id, node) in other.nodes() { - match node { - GraphNode::Operator(_) => { - let varname = other.node_varname(other_node_id); - let new_id = self.flat_graph.insert_node(node.clone(), varname, None); - node_mapping.insert(other_node_id, new_id); - } - GraphNode::ModuleBoundary { input, .. } => { - let new_id = self.flat_graph.insert_node( - GraphNode::ModuleBoundary { - input: *input, - import_expr: parent_span, - }, - Some(Ident::new(&format!("module_{}", input), parent_span)), - None, - ); - node_mapping.insert(other_node_id, new_id); - - // in the case of nested imports, this module boundary might not be the module boundary into or out of the top-most module - // So we have to be careful to only target those two boundaries. - // There should be no inputs to it, if it is an input boundary, if it is the top-most one. - // and there should be no outputs from it, if it is an output boundary, if it is the top-most one. - if *input && other.node_predecessor_nodes(other_node_id).count() == 0 { - if other.node_predecessor_nodes(other_node_id).count() == 0 { - ends.inn = - Some((PortIndexValue::Elided(None), GraphDet::Determined(new_id))); - } - } else if !(*input) && other.node_successor_nodes(other_node_id).count() == 0 { - ends.out = - Some((PortIndexValue::Elided(None), GraphDet::Determined(new_id))); - } - } - GraphNode::Handoff { .. } => { - panic!("Handoff in graph that is being merged into self") - } - } - } - - for (other_edge_id, (other_src, other_dst)) in other.edges() { - let (src_port, dst_port) = other.edge_ports(other_edge_id); - - let _new_edge_id = self.flat_graph.insert_edge( - *node_mapping.get(&other_src).unwrap(), - src_port.clone(), - *node_mapping.get(&other_dst).unwrap(), - dst_port.clone(), - ); } - - ends } /// Connects operator links as a final building step. Processes all the links stored in diff --git a/hydroflow_lang/src/graph/hydroflow_graph.rs b/hydroflow_lang/src/graph/hydroflow_graph.rs index ac857f72cc5..13af7adb8e6 100644 --- a/hydroflow_lang/src/graph/hydroflow_graph.rs +++ b/hydroflow_lang/src/graph/hydroflow_graph.rs @@ -1,5 +1,7 @@ #![warn(missing_docs)] +extern crate proc_macro; + use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use std::iter::FusedIterator; @@ -1006,13 +1008,15 @@ impl HydroflowGraph { subgraph_op_iter_code.push(write_iterator); if include_type_guards { - #[cfg(not(feature = "diagnostics"))] - let source_info = Option::::None; - - #[cfg(feature = "diagnostics")] - let source_info = std::panic::catch_unwind(|| op_span.unwrap()) - .map(|op_span| { - format!( + #[cfg_attr( + not(nightly), + expect(unused_labels, reason = "conditional compilation") + )] + let source_info = 'a: { + #[cfg(nightly)] + if proc_macro::is_available() { + let op_span = op_span.unwrap(); + break 'a format!( "loc_{}_{}_{}_{}_{}", op_span .source_file() @@ -1024,18 +1028,9 @@ impl HydroflowGraph { op_span.start().column(), op_span.end().line(), op_span.end().column(), - ) - }) - .ok(); + ); + } - #[cfg_attr( - not(feature = "diagnostics"), - expect( - clippy::unnecessary_literal_unwrap, - reason = "conditional compilation" - ) - )] - let source_info = source_info.unwrap_or_else(|| { format!( "loc_nopath_{}_{}_{}_{}", op_span.start().line, @@ -1043,7 +1038,7 @@ impl HydroflowGraph { op_span.end().line, op_span.end().column ) - }); + }; let fn_ident = format_ident!( "{}__{}__{}", diff --git a/hydroflow_lang/src/graph/mod.rs b/hydroflow_lang/src/graph/mod.rs index 7cb82e22c8d..d06ba21e8b3 100644 --- a/hydroflow_lang/src/graph/mod.rs +++ b/hydroflow_lang/src/graph/mod.rs @@ -25,7 +25,6 @@ mod hydroflow_graph; mod hydroflow_graph_debugging; use std::fmt::Display; -use std::path::PathBuf; pub use di_mul_graph::DiMulGraph; pub use eliminate_extra_unions_tees::eliminate_extra_unions_tees; @@ -376,9 +375,8 @@ impl Display for PortIndexValue { pub fn build_hfcode( hf_code: HfCode, root: &TokenStream, - macro_invocation_path: PathBuf, ) -> (Option<(HydroflowGraph, TokenStream)>, Vec) { - let flat_graph_builder = FlatGraphBuilder::from_hfcode(hf_code, macro_invocation_path); + let flat_graph_builder = FlatGraphBuilder::from_hfcode(hf_code); let (mut flat_graph, uses, mut diagnostics) = flat_graph_builder.build(); if !diagnostics.iter().any(Diagnostic::is_error) { if let Err(diagnostic) = flat_graph.merge_modules() { diff --git a/hydroflow_lang/src/lib.rs b/hydroflow_lang/src/lib.rs index 91d06c49be0..2b824532e81 100644 --- a/hydroflow_lang/src/lib.rs +++ b/hydroflow_lang/src/lib.rs @@ -1,10 +1,7 @@ //! Hydroflow surface syntax #![warn(missing_docs)] -#![cfg_attr( - feature = "diagnostics", - feature(proc_macro_diagnostic, proc_macro_span) -)] +#![cfg_attr(nightly, feature(proc_macro_diagnostic, proc_macro_span))] pub mod diagnostic; pub mod graph; pub mod parse; diff --git a/hydroflow_lang/src/parse.rs b/hydroflow_lang/src/parse.rs index e08fdda44b9..b353078fa44 100644 --- a/hydroflow_lang/src/parse.rs +++ b/hydroflow_lang/src/parse.rs @@ -11,7 +11,7 @@ use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; use syn::{ braced, bracketed, parenthesized, AngleBracketedGenericArguments, Expr, ExprPath, - GenericArgument, Ident, ItemUse, LitInt, LitStr, Path, PathArguments, PathSegment, Token, + GenericArgument, Ident, ItemUse, LitInt, Path, PathArguments, PathSegment, Token, }; use crate::process_singletons::preprocess_singletons; @@ -61,7 +61,6 @@ impl Parse for HfStatement { } else if lookahead2.peek(Token![->]) || lookahead2.peek(Paren) || lookahead2.peek(Bracket) - || lookahead2.peek(Token![!]) { Ok(Self::Pipeline(PipelineStatement::parse(input)?)) } else { @@ -140,7 +139,6 @@ pub enum Pipeline { Link(PipelineLink), Operator(Operator), ModuleBoundary(Ported), - Import(Import), } impl Pipeline { fn parse_one(input: ParseStream) -> syn::Result { @@ -175,20 +173,17 @@ impl Pipeline { // Ident or macro-style expression } else if lookahead1.peek(Ident) { let speculative = input.fork(); - let ident: Ident = speculative.parse()?; - let lookahead2 = speculative.lookahead1(); + let _ident: Ident = speculative.parse()?; // If has paren or generic next, it's an operator - if lookahead2.peek(Paren) || lookahead2.peek(Token![<]) || lookahead2.peek(Token![::]) { + if speculative.peek(Paren) + || speculative.peek(Token![<]) + || speculative.peek(Token![::]) + { Ok(Self::Operator(input.parse()?)) - // macro-style expression "x!.." - } else if lookahead2.peek(Token![!]) { - match ident.to_string().as_str() { - "import" => Ok(Self::Import(input.parse()?)), - _ => Err(syn::Error::new(ident.span(), r#"Expected "import""#)), - } - // Otherwise it's a name - } else { + } + // Otherwise it's a variable name + else { Ok(Self::Name(input.parse()?)) } } @@ -223,7 +218,6 @@ impl ToTokens for Pipeline { Self::Name(x) => x.to_tokens(tokens), Self::Operator(x) => x.to_tokens(tokens), Self::ModuleBoundary(x) => x.to_tokens(tokens), - Self::Import(x) => x.to_tokens(tokens), } } } @@ -264,38 +258,6 @@ impl ToTokens for LoopStatement { } } -#[derive(Clone, Debug)] -pub struct Import { - pub import: Ident, - pub bang: Token![!], - pub paren_token: Paren, - pub filename: LitStr, -} -impl Parse for Import { - fn parse(input: ParseStream) -> syn::Result { - let import = input.parse()?; - let bang = input.parse()?; - let content; - let paren_token = parenthesized!(content in input); - let filename: LitStr = content.parse()?; - - Ok(Self { - import, - bang, - paren_token, - filename, - }) - } -} -impl ToTokens for Import { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.import.to_tokens(tokens); - self.bang.to_tokens(tokens); - self.paren_token - .surround(tokens, |tokens| self.filename.to_tokens(tokens)); - } -} - #[derive(Clone, Debug)] pub struct Ported { pub inn: Option, diff --git a/hydroflow_lang/src/pretty_span.rs b/hydroflow_lang/src/pretty_span.rs index 8ad6ae6ee3d..ed4d92ba20d 100644 --- a/hydroflow_lang/src/pretty_span.rs +++ b/hydroflow_lang/src/pretty_span.rs @@ -1,27 +1,28 @@ //! Pretty, human-readable printing of [`proc_macro2::Span`]s. +extern crate proc_macro; + /// Helper struct which displays the span as `path:row:col` for human reading/IDE linking. /// Example: `hydroflow\tests\surface_syntax.rs:42:18`. pub struct PrettySpan(pub proc_macro2::Span); impl std::fmt::Display for PrettySpan { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[cfg(feature = "diagnostics")] - { - if let Ok(span) = std::panic::catch_unwind(|| self.0.unwrap()) { - write!( - f, - "{}:{}:{}", - span.source_file().path().display(), - span.start().line(), - span.start().column(), - )?; - return Ok(()); - } + #[cfg(nightly)] + if proc_macro::is_available() { + let span = self.0.unwrap(); + write!( + f, + "{}:{}:{}", + span.source_file().path().display(), + span.start().line(), + span.start().column(), + )?; + return Ok(()); } write!( f, - "nopath:{}:{}", + "unknown:{}:{}", self.0.start().line, self.0.start().column ) diff --git a/hydroflow_macro/Cargo.toml b/hydroflow_macro/Cargo.toml index 947cd24a997..4581adb9960 100644 --- a/hydroflow_macro/Cargo.toml +++ b/hydroflow_macro/Cargo.toml @@ -13,9 +13,6 @@ workspace = true [lib] proc-macro = true -[features] -diagnostics = [ "hydroflow_lang/diagnostics" ] - [dependencies] # Note: If we ever compile this proc macro crate to WASM (e.g., if we are # building on a WASM host), we may need to turn diagnostics off for WASM if @@ -30,3 +27,4 @@ syn = { version = "2.0.46", features = [ "parsing", "extra-traits" ] } hydroflow_lang = { path = "../hydroflow_lang", version = "^0.10.0" } itertools = "0.10.0" quote = "1.0.35" +rustc_version = "0.4.0" diff --git a/hydroflow_macro/build.rs b/hydroflow_macro/build.rs index adf5e9459b2..3fa4cb3c6f8 100644 --- a/hydroflow_macro/build.rs +++ b/hydroflow_macro/build.rs @@ -10,6 +10,7 @@ use hydroflow_lang::graph::ops::{PortListSpec, OPERATORS}; use hydroflow_lang::graph::PortIndexValue; use itertools::Itertools; use quote::ToTokens; +use rustc_version::{version_meta, Channel}; const FILENAME: &str = "surface_ops_gen.md"; @@ -207,6 +208,14 @@ fn update_book() -> Result<()> { } fn main() { + println!("cargo::rustc-check-cfg=cfg(nightly)"); + if matches!( + version_meta().map(|meta| meta.channel), + Ok(Channel::Nightly) + ) { + println!("cargo:rustc-cfg=nightly"); + } + if Err(VarError::NotPresent) != std::env::var("CARGO_CFG_HYDROFLOW_GENERATE_DOCS") { if let Err(err) = update_book() { eprintln!("hydroflow_macro/build.rs error: {:?}", err); diff --git a/hydroflow_macro/src/lib.rs b/hydroflow_macro/src/lib.rs index eaf15571a7c..f03ae0dc87e 100644 --- a/hydroflow_macro/src/lib.rs +++ b/hydroflow_macro/src/lib.rs @@ -1,10 +1,8 @@ #![cfg_attr( - feature = "diagnostics", + nightly, feature(proc_macro_diagnostic, proc_macro_span, proc_macro_def_site) )] -use std::path::PathBuf; - use hydroflow_lang::diagnostic::{Diagnostic, Level}; use hydroflow_lang::graph::{build_hfcode, partition_graph, FlatGraphBuilder}; use hydroflow_lang::parse::HfCode; @@ -59,32 +57,13 @@ fn root() -> proc_macro2::TokenStream { } } -// May panic -fn macro_invocation_path() -> PathBuf { - #[cfg(feature = "diagnostics")] - { - proc_macro::Span::call_site().source_file().path() - } - #[cfg(not(feature = "diagnostics"))] - { - std::env::current_dir().unwrap_or_else(|_| { - PathBuf::from( - std::env::var("CARGO_MANIFEST_DIR") - .expect("Failed to determine fallback env var CARGO_MANIFEST_DIR."), - ) - }) - } -} - fn hydroflow_syntax_internal( input: proc_macro::TokenStream, min_diagnostic_level: Option, ) -> proc_macro::TokenStream { - let macro_invocation_path = macro_invocation_path(); - let input = parse_macro_input!(input as HfCode); let root = root(); - let (graph_code_opt, diagnostics) = build_hfcode(input, &root, macro_invocation_path); + let (graph_code_opt, diagnostics) = build_hfcode(input, &root); let tokens = graph_code_opt .map(|(_graph, code)| code) .unwrap_or_else(|| quote! { #root::scheduled::graph::Hydroflow::new() }); @@ -93,25 +72,16 @@ fn hydroflow_syntax_internal( .iter() .filter(|diag: &&Diagnostic| Some(diag.level) <= min_diagnostic_level); - #[cfg(feature = "diagnostics")] - { - diagnostics.for_each(Diagnostic::emit); - tokens.into() - } - - #[cfg(not(feature = "diagnostics"))] - { - let diagnostics = diagnostics.map(Diagnostic::to_tokens); - quote! { - { - #( - #diagnostics - )* - #tokens - } + let diagnostic_tokens = Diagnostic::try_emit_all(diagnostics) + .err() + .unwrap_or_default(); + quote! { + { + #diagnostic_tokens + #tokens } - .into() } + .into() } /// Parse Hydroflow "surface syntax" without emitting code. @@ -119,11 +89,9 @@ fn hydroflow_syntax_internal( /// Used for testing, users will want to use [`hydroflow_syntax!`] instead. #[proc_macro] pub fn hydroflow_parser(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let macro_invocation_path = macro_invocation_path(); - let input = parse_macro_input!(input as HfCode); - let flat_graph_builder = FlatGraphBuilder::from_hfcode(input, macro_invocation_path); + let flat_graph_builder = FlatGraphBuilder::from_hfcode(input); let (mut flat_graph, _uses, mut diagnostics) = flat_graph_builder.build(); if !diagnostics.iter().any(Diagnostic::is_error) { if let Err(diagnostic) = flat_graph.merge_modules() { @@ -146,8 +114,10 @@ pub fn hydroflow_parser(input: proc_macro::TokenStream) -> proc_macro::TokenStre } } - diagnostics.iter().for_each(Diagnostic::emit); - quote! {}.into() + Diagnostic::try_emit_all(diagnostics.iter()) + .err() + .unwrap_or_default() + .into() } #[doc(hidden)] diff --git a/hydroflow_plus/Cargo.toml b/hydroflow_plus/Cargo.toml index 23ad3d33433..d209f43f81a 100644 --- a/hydroflow_plus/Cargo.toml +++ b/hydroflow_plus/Cargo.toml @@ -15,7 +15,6 @@ path = "src/lib.rs" [features] default = ["deploy_runtime"] -diagnostics = [ "hydroflow_lang/diagnostics" ] stageleft_devel = [] deploy_runtime = [ "hydroflow/deploy_integration" ] deploy = [ "deploy_runtime", "dep:hydro_deploy", "dep:trybuild-internals-api", "dep:toml", "dep:prettyplease" ] diff --git a/website_playground/src/lib.rs b/website_playground/src/lib.rs index f996c62b618..b670c74126c 100644 --- a/website_playground/src/lib.rs +++ b/website_playground/src/lib.rs @@ -1,7 +1,6 @@ mod utils; use std::cell::RefCell; use std::collections::HashMap; -use std::path::PathBuf; use std::task::{Context, Poll}; use std::thread_local; @@ -120,8 +119,7 @@ pub fn compile_hydroflow( let out = match syn::parse_str(&program) { Ok(input) => { - let (graph_code_opt, diagnostics) = - build_hfcode(input, "e!(hydroflow), PathBuf::default()); + let (graph_code_opt, diagnostics) = build_hfcode(input, "e!(hydroflow)); let output = graph_code_opt.map(|(graph, code)| { let mermaid = graph.to_mermaid(&write_config); let file = syn::parse_quote! {