-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion/brainstorming: Non-fatal errors #175
Comments
Would adding a struct like the one below solve this? pub struct WithDiagnostics<T> {
pub value: T,
pub diagnostics: Vec<Error>,
}
impl<T: FromMeta> FromMeta for WithDiagnostics<T> {
fn from_meta(meta: &Meta) -> Result<Self> {
Ok(Self {
value: FromMeta::from_meta(meta)?,
diagnostics: vec![],
})
}
}
impl<T: ToTokens> ToTokens for WithDiagnostics<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.value.to_tokens(tokens);
for diagnostic in &self.diagnostics {
// TODO add errors to token stream
}
}
} Pros
Cons
|
@AlisCode I'm curious to get your input on this one; does your company have situations where you want to add warnings/notes/etc. without adding an error? Would a new util type like |
In the case of the macro I've been refactoring, we don't exactly separate the concepts of "receiver structs" and "meaningful/stronger-typed structs". We have our "meaningful" structs implement Any validation problem on any of the variants of the enum is fatal, so I assume that no, optional diagnostics arent really a thing we need. Not for that specific macro, anyway 👀 . |
darling
has a simple pass-fail approach for turning the AST into receiver structs. All of thedarling
traits returnResult<Self, darling::Error>
. This is great for approachability, but it does mean there's not a good place to put non-fatal diagnostics.Some concrete examples:
#[darling(deprecated = "...")]
, enabling someone to deprecate a field in their macro as easily as they would a regular method.derive_builder
the conflicting visibility declarations don't need to block code generation. Ideally there would be one error for "A field cannot be both public and private" without a second compiler error "LoremBuilder does not exist"It's possible that these scenarios are best handled outside the existing system - since they don't block parsing, they could be implemented as functions that take the
Receiver
and return aVec<Diagnostic>
. That "feels" wrong though:darling
for derivation shouldn't need to remember to also call some other method to avoid losing non-fatal diagnostics.FromMeta
should "just work", including emitting non-fatal diagnostics when appropriate. If the non-fatal diagnostics are gathered in a separate pass, the author will need to remember to visit every field when gathering diagnostics or they'll miss things.darling
suggests this rarely happens, withString
andbool
being very common receiver field types. As a result, non-fatal diagnostics would not have access to good spans.There seem to be two ways this "could" work:
darling
passes around a context of some sort, andFromMeta
impls can add diagnostics into that context.darling
traits is changed to be something like:DarlingResult<T> { value: Option<T>, diagnostics: Vec<Diagnostic> }
. A fatal error would causevalue
to beNone
.Both of these have problems.
Approach 1 would likely require introduction of parallel traits:
FromMetaContext
,FromDeriveInputContext
, etc. Those wouldn't compose, since existing derivations ofFromMeta
wouldn't pass the context to their children when they callFromMeta
.Approach 2 would mean an enormous breaking change to the entire
darling
ecosystem, and despite being a 0.x crate, I'm extremely reluctant - bordering on outright unwilling - to do that.That leaves the after-the-fact traversal. Maybe there's a way with drop bombs to make that workable: Make
#[darling::post_validation]
be an attribute macro that implements a trait and injects a drop-bomb field to ensure the trait implementation is called?The text was updated successfully, but these errors were encountered: