diff --git a/Cargo.lock b/Cargo.lock index ca8aab2608b0..e762579e1353 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", @@ -2633,7 +2634,7 @@ dependencies = [ "byteorder", "libc", "nom 2.2.1", - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -3067,6 +3068,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/hydroflow/Cargo.toml b/hydroflow/Cargo.toml index 7c0473f41ef8..e55322b13168 100644 --- a/hydroflow/Cargo.toml +++ b/hydroflow/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [features] default = [ "macros", "nightly", "debugging" ] -nightly = [ "hydroflow_macro", "hydroflow_macro/diagnostics" ] +nightly = [] macros = [ "hydroflow_macro", "hydroflow_datalog" ] hydroflow_macro = [ "dep:hydroflow_macro" ] hydroflow_datalog = [ "dep:hydroflow_datalog" ] diff --git a/hydroflow_datalog/Cargo.toml b/hydroflow_datalog/Cargo.toml index 58a62cab9d3b..c4771f901c7a 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_core/Cargo.toml b/hydroflow_datalog_core/Cargo.toml index 79b541b1bf30..ab7a2b1a7688 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_lang/Cargo.toml b/hydroflow_lang/Cargo.toml index b1321b5ebf72..256d2b9269bb 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 d2e0af6f8487..fe39425917ac 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 44ec8c588a8d..0941bfc319c1 100644 --- a/hydroflow_lang/src/diagnostic.rs +++ b/hydroflow_lang/src/diagnostic.rs @@ -40,8 +40,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,23 +68,47 @@ 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)] { - let pm_diag = match self.level { - Level::Error => self.span.unwrap().error(&*self.message), - Level::Warning => self.span.unwrap().warning(&*self.message), - Level::Note => self.span.unwrap().note(&*self.message), - Level::Help => self.span.unwrap().help(&*self.message), - }; - pm_diag.emit(); + if let Ok(()) = std::panic::catch_unwind(|| { + let pm_diag = match self.level { + Level::Error => self.span.unwrap().error(&*self.message), + Level::Warning => self.span.unwrap().warning(&*self.message), + Level::Note => self.span.unwrap().note(&*self.message), + Level::Help => self.span.unwrap().help(&*self.message), + }; + pm_diag.emit() + }) { + return Ok(()); + } + } + Err(self.to_tokens()) + } + + /// 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 [`Diagnostic::emit`] by turning this diagnostic into a properly spanned [`TokenStream`] - /// that emits an error with this diagnostic's message. + /// 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 +122,7 @@ impl Diagnostic { if Level::Error == self.level { quote_spanned! {self.span=> { - ::std::compile_error!(#msg_lit); + ::core::compile_error!(#msg_lit); } } } else { @@ -169,7 +193,7 @@ pub struct SerdeSpan { } impl From for SerdeSpan { fn from(span: Span) -> Self { - #[cfg(feature = "diagnostics")] + #[cfg(nightly)] let path = span .unwrap() .source_file() @@ -178,7 +202,7 @@ impl From for SerdeSpan { .to_string() .into(); - #[cfg(not(feature = "diagnostics"))] + #[cfg(not(nightly))] let path = "unknown".into(); Self { diff --git a/hydroflow_lang/src/graph/hydroflow_graph.rs b/hydroflow_lang/src/graph/hydroflow_graph.rs index ac857f72cc5f..b174393631b2 100644 --- a/hydroflow_lang/src/graph/hydroflow_graph.rs +++ b/hydroflow_lang/src/graph/hydroflow_graph.rs @@ -1006,10 +1006,10 @@ impl HydroflowGraph { subgraph_op_iter_code.push(write_iterator); if include_type_guards { - #[cfg(not(feature = "diagnostics"))] + #[cfg(not(nightly))] let source_info = Option::::None; - #[cfg(feature = "diagnostics")] + #[cfg(nightly)] let source_info = std::panic::catch_unwind(|| op_span.unwrap()) .map(|op_span| { format!( @@ -1029,7 +1029,7 @@ impl HydroflowGraph { .ok(); #[cfg_attr( - not(feature = "diagnostics"), + not(nightly), expect( clippy::unnecessary_literal_unwrap, reason = "conditional compilation" diff --git a/hydroflow_lang/src/lib.rs b/hydroflow_lang/src/lib.rs index 91d06c49be0b..2b824532e81c 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/pretty_span.rs b/hydroflow_lang/src/pretty_span.rs index 8ad6ae6ee3dc..0af791ed0d94 100644 --- a/hydroflow_lang/src/pretty_span.rs +++ b/hydroflow_lang/src/pretty_span.rs @@ -5,7 +5,7 @@ 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")] + #[cfg(nightly)] { if let Ok(span) = std::panic::catch_unwind(|| self.0.unwrap()) { write!( @@ -21,7 +21,7 @@ impl std::fmt::Display for PrettySpan { 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 947cd24a997e..16c9450b7fdd 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 diff --git a/hydroflow_plus/Cargo.toml b/hydroflow_plus/Cargo.toml index 23ad3d334337..d209f43f81ac 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" ]