macroonz_compiler/host/place.rs
1//! Placing one refusal as a `compile_error!` at the position it is a fact about.
2//!
3//! Both roads end in one composition, so a refusal the compiler established and one this host raised point at their token the same way and neither can drift into answering with the declaration's first span.
4
5use super::types::{CaptureError, EmissionError, Spans};
6use crate::diagnostic::Diagnostic;
7use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
8
9/// The `compile_error!` one diagnostic expands to, at the token its site names.
10///
11/// The line is the one the compiler composed and is only read here; this host builds no sentence.
12/// A diagnostic that names no token was established before any capture issued one, and is reported at the invocation — which is the only thing about the expansion such an observation is a fact about.
13#[must_use]
14pub fn place(diagnostic: &Diagnostic, spans: &Spans) -> TokenStream {
15 refused(diagnostic.summary(), sited(diagnostic, spans))
16}
17
18/// Place one host emission contradiction at the invocation boundary.
19pub(super) fn emission_refused(refusal: &EmissionError) -> TokenStream {
20 refused(&refusal.to_string(), Span::call_site())
21}
22
23impl CaptureError {
24 /// The `compile_error!` this refusal expands to, at the position it is a fact about.
25 ///
26 /// A magnitude is a fact about the whole declaration and no one token overran it, so it is reported at the invocation.
27 /// A literal this crate could not read is a fact about exactly one token, and that token's handle was issued before its payload was read — so the span is held and the report goes there.
28 ///
29 /// # Nonclaims
30 ///
31 /// It is not a [`Diagnostic`]: composing one needs the door whose prefix, grammar, and callable entry the line carries, and a capture runs before any door is named.
32 #[must_use]
33 pub fn placed(self, spans: &Spans) -> TokenStream {
34 let at = match &self {
35 Self::Unbounded { .. } => Span::call_site(),
36 Self::Unread { at, .. } => spans.at(*at),
37 };
38 refused(&self.to_string(), at)
39 }
40}
41
42/// The compiler span one diagnostic points at.
43fn sited(diagnostic: &Diagnostic, spans: &Spans) -> Span {
44 diagnostic
45 .site()
46 .token()
47 .map_or_else(Span::call_site, |handle| spans.at(handle))
48}
49
50/// One `compile_error!` at one span, carrying a line composed elsewhere.
51fn refused(message: &str, span: Span) -> TokenStream {
52 let mut bang = Punct::new('!', Spacing::Alone);
53 bang.set_span(span);
54 let mut terminator = Punct::new(';', Spacing::Alone);
55 terminator.set_span(span);
56 let mut line = Literal::string(message);
57 line.set_span(span);
58 let mut argument = Group::new(
59 Delimiter::Parenthesis,
60 TokenStream::from(TokenTree::Literal(line)),
61 );
62 argument.set_span(span);
63 [
64 TokenTree::Ident(Ident::new("compile_error", span)),
65 TokenTree::Punct(bang),
66 TokenTree::Group(argument),
67 TokenTree::Punct(terminator),
68 ]
69 .into_iter()
70 .collect()
71}