Expand description
Emit messages to the user running a policy.
The diagnostics of a Paralegal policy are similar to those used in rustc. In particular we use a strategy of “keep going”, meaning that a policy should not fail at the first error. Instead it should attempt to keep going if possible and emit additional errors, as those messages are useful for the user.
This manifests for instance in Diagnostics::error
. This function records
a severe error that should fail the policy but it does not exit the program.
Instead the message is recorded and emitted later, for instance by
RootContext::emit_diagnostics
.
§Emitting Messages
The main interface for diagnostics is the Diagnostics
trait which
defines functions for emitting messages like error
for policy failures and warning
for indicators to
the user that something may be off, but not causing a policy failure.
We also offer two convenience macros assert_error!
and
assert_warning!
that correspond to either function. Much like
assert!
they only evaluate and emit their messages if the provided
condition is false
. They should be used like assert_warning!(ctx, condition, message)
. ctx
here is anything that implements
Diagnostics
.
Diagnostics
is implemented directly by RootContext
so you can use
ctx.error()
or ctx.warning()
. You can also call it on scoped contexts
(see below).
§Scoping messages
You may however add additional contextual information about which policy or
combinator is currently executing. RootContext::named_policy
returns a
wrapper that can be used the same way that you use Context
, but when
error
or warning
is called
it also appends the name of the policy to you specified.
Similarly you can use RootContext::named_combinator
or
PolicyContext::named_combinator
to add context about a named combinator.
§Intended Workflow
use paralegal_policy::{
Context, RootContext, assert_error, assert_warning,
paralegal_spdg::Identifier
};
use std::sync::Arc;
fn my_check(ctx: Arc<RootContext>) {
ctx.named_policy(Identifier::new_intern("cannot escape"), |ctx| {
let result_1 = ctx.clone().named_combinator(
Identifier::new_intern("collect something"),
|ctx| {
/* actual computation */
assert_error!(ctx, 1 + 2 == 4, "Oh oh, fail!");
true
}
);
let result_2 = ctx.clone().named_combinator(
Identifier::new_intern("reach something"),
|ctx| {
assert_warning!(ctx, 1 - 3 == 0, "maybe wrong?");
false
}
);
assert_error!(ctx, result_1 || result_2, "combination failure");
})
}
The messages emitted from here (if all conditions fail true) would be
[policy: cannot escape] [collect something] Oh oh, fail!
[policy: cannot escape] [reach something] maybe wrong?
[policy: cannot escape] combination failure
It is recommended to use this “shadowing” approach, where all contexts are
named ctx
, to avoid using the outer context by accident. The methods off
outer contexts are always available on the inner ones.
Note that some methods, like RootContext::always_happens_before
add a named
combinator context by themselves when you use their
report
functions.
Structs§
- A context for combinators.
- A context for controllers
- Representation of a diagnostic message. You should not interact with this type directly but use the methods on
Diagnostics
orDiagnosticBuilder
to create these. - Facility to create structured diagnostics including spans and multi-part messages. New builders are created with methods on
Diagnostics
.struct_<severity>
creates simple main diagnostics with only a message,struct_node_<severity>
creates a main diagnostic with a message and the span of a graph node andstruct_span_<severity>
creates a main diagnostic with a message and a custom source code span. - Base database of emitted diagnostics.
- A span with only a portion highlighted.
- A context for a named policy.
- SubSpan 🔒
Enums§
- Severity of a recorded diagnostic message
Constants§
- TAB_
SIZE 🔒
Traits§
- User-facing methods to emit diagnostics.
- Low level machinery for diagnostics.
Functions§
Type Aliases§
- Context provided to
HasDiagnosticsBase::record
.