summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-09-17 11:23:35 +0200
committerLaurenz <laurmaedje@gmail.com>2024-09-17 11:25:22 +0200
commitd4242ff8c1086842a6d7c601d4ffe32feb6f9bc2 (patch)
tree0d2d09151f65cee09ccea4fbe0a25eabca057b71
parent4c0f1173abbdba917d2d25921e575e3981ddc15b (diff)
Turn unresolved references and citations into warnings
-rw-r--r--crates/typst/src/model/cite.rs26
-rw-r--r--crates/typst/src/model/reference.rs52
-rw-r--r--tests/ref/cite-missing.pngbin0 -> 1881 bytes
-rw-r--r--tests/ref/label-multiple-ignored-warn.pngbin0 -> 894 bytes
-rw-r--r--tests/ref/ref-label-complex-missing.pngbin0 -> 1291 bytes
-rw-r--r--tests/ref/ref-label-missing.pngbin0 -> 423 bytes
-rw-r--r--tests/suite/foundations/label.typ14
-rw-r--r--tests/suite/model/cite.typ11
-rw-r--r--tests/suite/model/ref.typ8
9 files changed, 89 insertions, 22 deletions
diff --git a/crates/typst/src/model/cite.rs b/crates/typst/src/model/cite.rs
index e156e0ec..fea4fcea 100644
--- a/crates/typst/src/model/cite.rs
+++ b/crates/typst/src/model/cite.rs
@@ -1,3 +1,5 @@
+use ecow::eco_format;
+
use crate::diag::{error, At, HintedString, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
@@ -5,7 +7,7 @@ use crate::foundations::{
};
use crate::introspection::Locatable;
use crate::model::bibliography::Works;
-use crate::model::CslStyle;
+use crate::model::{unresolved_reference, BibliographyElem, CslStyle};
use crate::text::{Lang, Region, TextElem};
/// Cite a work from the bibliography.
@@ -40,7 +42,7 @@ use crate::text::{Lang, Region, TextElem};
/// This function indirectly has dedicated syntax. [References]($ref) can be
/// used to cite works from the bibliography. The label then corresponds to the
/// citation key.
-#[elem(Synthesize)]
+#[elem(Synthesize, Show)]
pub struct CiteElem {
/// The citation key that identifies the entry in the bibliography that
/// shall be cited, as a label.
@@ -117,6 +119,26 @@ impl Synthesize for Packed<CiteElem> {
}
}
+impl Show for Packed<CiteElem> {
+ #[typst_macros::time(name = "cite", span = self.span())]
+ fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
+ if !BibliographyElem::has(engine, self.key) {
+ return Ok(unresolved_reference(
+ engine,
+ eco_format!(
+ "key `{}` does not exist in the bibliography",
+ self.key.as_str()
+ ),
+ "cite",
+ self.key,
+ self.span(),
+ ));
+ }
+
+ Ok(self.clone().pack())
+ }
+}
+
cast! {
CiteElem,
v: Content => v.unpack::<Self>().map_err(|_| "expected citation")?,
diff --git a/crates/typst/src/model/reference.rs b/crates/typst/src/model/reference.rs
index 8194eec5..33ae1480 100644
--- a/crates/typst/src/model/reference.rs
+++ b/crates/typst/src/model/reference.rs
@@ -1,18 +1,20 @@
use comemo::Track;
-use ecow::eco_format;
+use ecow::{eco_format, EcoString};
-use crate::diag::{bail, At, Hint, SourceResult};
+use crate::diag::{bail, At, Hint, SourceDiagnostic, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
- cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Show,
- Smart, StyleChain, Synthesize,
+ cast, elem, Content, Context, Func, IntoValue, Label, NativeElement, Packed, Repr,
+ Show, Smart, StyleChain, Synthesize,
};
-use crate::introspection::{Counter, Locatable};
+use crate::introspection::{Counter, Locatable, QueryError};
use crate::math::EquationElem;
use crate::model::{
BibliographyElem, CiteElem, Destination, Figurable, FootnoteElem, Numbering,
};
-use crate::text::TextElem;
+use crate::syntax::{is_valid_label_literal, Span};
+use crate::text::{RawContent, RawElem, TextElem};
+use crate::visualize::Color;
/// A reference to a label or bibliography.
///
@@ -163,22 +165,25 @@ impl Synthesize for Packed<RefElem> {
impl Show for Packed<RefElem> {
#[typst_macros::time(name = "ref", span = self.span())]
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- let target = *self.target();
- let elem = engine.introspector.query_label(target);
let span = self.span();
+ let result = engine.introspector.query_label(self.target);
- if BibliographyElem::has(engine, target) {
- if elem.is_ok() {
+ if BibliographyElem::has(engine, self.target) {
+ if result.is_ok() {
bail!(span, "label occurs in the document and its bibliography");
}
return Ok(to_citation(self, engine, styles)?.pack().spanned(span));
}
- let elem = elem.at(span)?;
+ if let Err(error @ QueryError::MissingLabel(_)) = result {
+ return Ok(unresolved_reference(engine, error, "ref", self.target, span));
+ }
+
+ let elem = result.at(span)?;
if let Some(footnote) = elem.to_packed::<FootnoteElem>() {
- return Ok(footnote.into_ref(target).pack().spanned(span));
+ return Ok(footnote.into_ref(self.target).pack().spanned(span));
}
let elem = elem.clone();
@@ -305,3 +310,26 @@ pub trait Refable {
/// Returns the numbering of this element.
fn numbering(&self) -> Option<&Numbering>;
}
+
+/// Generates a warning for an unresolved reference and returns placeholder
+/// content.
+pub(crate) fn unresolved_reference(
+ engine: &mut Engine,
+ message: impl Into<EcoString>,
+ func: &str,
+ target: Label,
+ span: Span,
+) -> Content {
+ engine.sink.warn(SourceDiagnostic::warning(span, message));
+
+ let text = if is_valid_label_literal(target.as_str()) {
+ eco_format!("@{}", target.as_str())
+ } else {
+ eco_format!("#{func}(label({}))", target.as_str().repr())
+ };
+
+ return RawElem::new(RawContent::Text(text))
+ .pack()
+ .spanned(span)
+ .styled(TextElem::set_fill(Color::RED.into()));
+}
diff --git a/tests/ref/cite-missing.png b/tests/ref/cite-missing.png
new file mode 100644
index 00000000..beb97f58
--- /dev/null
+++ b/tests/ref/cite-missing.png
Binary files differ
diff --git a/tests/ref/label-multiple-ignored-warn.png b/tests/ref/label-multiple-ignored-warn.png
new file mode 100644
index 00000000..f623f3a7
--- /dev/null
+++ b/tests/ref/label-multiple-ignored-warn.png
Binary files differ
diff --git a/tests/ref/ref-label-complex-missing.png b/tests/ref/ref-label-complex-missing.png
new file mode 100644
index 00000000..933721a2
--- /dev/null
+++ b/tests/ref/ref-label-complex-missing.png
Binary files differ
diff --git a/tests/ref/ref-label-missing.png b/tests/ref/ref-label-missing.png
new file mode 100644
index 00000000..22596df7
--- /dev/null
+++ b/tests/ref/ref-label-missing.png
Binary files differ
diff --git a/tests/suite/foundations/label.typ b/tests/suite/foundations/label.typ
index af6d2380..f662695a 100644
--- a/tests/suite/foundations/label.typ
+++ b/tests/suite/foundations/label.typ
@@ -72,16 +72,16 @@ _Visible_
// Hint: 1-8 only the last label is used, the rest are ignored
= Hello <a> <b>
-// Warning: 12-19 content labelled multiple times
-// Hint: 12-19 only the last label is used, the rest are ignored
-#let f = [#block()<c>]
+// Warning: 12-26 content labelled multiple times
+// Hint: 12-26 only the last label is used, the rest are ignored
+#let f = [#metadata(none)<c>]
#f<d>
-// Warning: 6-13 content labelled multiple times
-// Hint: 6-13 only the last label is used, the rest are ignored
-#[#[#block()]<e>]<f>
+// Warning: 6-20 content labelled multiple times
+// Hint: 6-20 only the last label is used, the rest are ignored
+#[#[#metadata(none)]<e>]<f>
-// Error: 1-3 label `<a>` does not exist in the document
+// Warning: 1-3 label `<a>` does not exist in the document
@a
--- label-unattached-warn ---
diff --git a/tests/suite/model/cite.typ b/tests/suite/model/cite.typ
index ffbd3b52..34f91f4c 100644
--- a/tests/suite/model/cite.typ
+++ b/tests/suite/model/cite.typ
@@ -49,6 +49,17 @@ A @netwok @arrgh @quark, B.
#show bibliography: none
#bibliography("/assets/bib/works.bib")
+--- cite-missing ---
+// Warning: 2-15 key `peter` does not exist in the bibliography
+// Warning: 31-37 label `<extra>` does not exist in the document
+#cite(<peter>) @netwok @arrgh @extra
+
+// Warning: 2-20 key `>?&` does not exist in the bibliography
+#cite(label(">?&"))
+
+#show bibliography: none
+#bibliography("/assets/bib/works.bib")
+
--- issue-785-cite-locate ---
// Test citation in other introspection.
#set page(width: 180pt)
diff --git a/tests/suite/model/ref.typ b/tests/suite/model/ref.typ
index cc6d6bf1..c8ff8067 100644
--- a/tests/suite/model/ref.typ
+++ b/tests/suite/model/ref.typ
@@ -10,9 +10,15 @@ See @setup.
As seen in @intro, we proceed.
--- ref-label-missing ---
-// Error: 1-5 label `<foo>` does not exist in the document
+// Warning: 1-5 label `<foo>` does not exist in the document
@foo
+--- ref-label-complex-missing ---
+#set page(width: auto)
+
+// Warning: 2-28 label `label("is;/"bad%//#")` does not exist in the document
+#ref(label("is;\"bad%//#"))
+
--- ref-label-duplicate ---
= First <foo>
= Second <foo>