mirror of
https://github.com/typst/typst
synced 2025-05-20 20:15:29 +08:00
move around some sink functions
This commit is contained in:
parent
fd410f230a
commit
629375a981
@ -1,5 +1,6 @@
|
|||||||
//! Diagnostics.
|
//! Diagnostics.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -10,7 +11,7 @@ use comemo::Tracked;
|
|||||||
use ecow::{eco_vec, EcoVec};
|
use ecow::{eco_vec, EcoVec};
|
||||||
|
|
||||||
use crate::syntax::package::{PackageSpec, PackageVersion};
|
use crate::syntax::package::{PackageSpec, PackageVersion};
|
||||||
use crate::syntax::{Span, Spanned, SyntaxError};
|
use crate::syntax::{ast, Span, Spanned, SyntaxError};
|
||||||
use crate::{World, WorldExt};
|
use crate::{World, WorldExt};
|
||||||
|
|
||||||
/// Early-return with a [`StrResult`] or [`SourceResult`].
|
/// Early-return with a [`StrResult`] or [`SourceResult`].
|
||||||
@ -476,6 +477,118 @@ impl<T> Hint<T> for HintedStrResult<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deduplicate errors based on their spans and messages.
|
||||||
|
pub fn deduplicate_errors(
|
||||||
|
mut errors: EcoVec<SourceDiagnostic>,
|
||||||
|
) -> EcoVec<SourceDiagnostic> {
|
||||||
|
let mut unique = HashSet::new();
|
||||||
|
errors.retain(|error| {
|
||||||
|
debug_assert!(error.severity == Severity::Error);
|
||||||
|
let hash = crate::utils::hash128(&(&error.span, &error.message));
|
||||||
|
unique.insert(hash)
|
||||||
|
});
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply warning suppression, deduplication, and return the remaining
|
||||||
|
/// warnings.
|
||||||
|
///
|
||||||
|
/// We deduplicate warnings which are identical modulo tracepoints.
|
||||||
|
/// This is so we can attempt to suppress each tracepoint separately,
|
||||||
|
/// without having one suppression discard all other attempts of raising
|
||||||
|
/// this same warning through a different set of tracepoints.
|
||||||
|
///
|
||||||
|
/// For example, calling the same function twice but suppressing a warning
|
||||||
|
/// on the first call shouldn't suppress on the second, so each set of
|
||||||
|
/// tracepoints for a particular warning matters. If at least one instance
|
||||||
|
/// of a warning isn't suppressed, the warning will be returned, and the
|
||||||
|
/// remaining duplicates are discarded.
|
||||||
|
pub fn deduplicate_and_suppress_warnings(
|
||||||
|
warnings: &mut EcoVec<SourceDiagnostic>,
|
||||||
|
world: &dyn World,
|
||||||
|
) {
|
||||||
|
let mut unsuppressed = HashSet::<u128>::default();
|
||||||
|
|
||||||
|
// Only retain warnings which weren't locally suppressed where they
|
||||||
|
// were emitted or at any of their tracepoints.
|
||||||
|
warnings.retain(|diag| {
|
||||||
|
debug_assert!(diag.severity == Severity::Warning);
|
||||||
|
let hash = crate::utils::hash128(&(&diag.span, &diag.identifier, &diag.message));
|
||||||
|
if unsuppressed.contains(&hash) {
|
||||||
|
// This warning - with the same span, identifier and message -
|
||||||
|
// was already raised and not suppressed before, with a
|
||||||
|
// different set of tracepoints. Therefore, we should not raise
|
||||||
|
// it again, and checking for suppression is unnecessary.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(identifier) = &diag.identifier else {
|
||||||
|
// Can't suppress without an identifier. Therefore, retain the
|
||||||
|
// warning. It is not a duplicate due to the check above.
|
||||||
|
unsuppressed.insert(hash);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_raise = !is_warning_suppressed(diag.span, world, identifier)
|
||||||
|
&& !diag.trace.iter().any(|tracepoint| {
|
||||||
|
is_warning_suppressed(tracepoint.span, world, identifier)
|
||||||
|
});
|
||||||
|
|
||||||
|
// If this warning wasn't suppressed, any further duplicates (with
|
||||||
|
// different tracepoints) should be removed.
|
||||||
|
should_raise && unsuppressed.insert(hash)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given warning is suppressed given one span it has a tracepoint
|
||||||
|
/// in. If one of the ancestors of the node where the warning occurred has a
|
||||||
|
/// warning suppression decorator sibling right before it suppressing this
|
||||||
|
/// particular warning, the warning is considered suppressed.
|
||||||
|
fn is_warning_suppressed(span: Span, world: &dyn World, identifier: &Identifier) -> bool {
|
||||||
|
// Don't suppress detached warnings.
|
||||||
|
let Some(source) = span.id().and_then(|file| world.source(file).ok()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let search_root = source.find(span);
|
||||||
|
let mut searched_node = search_root.as_ref();
|
||||||
|
|
||||||
|
// Walk the parent nodes to check for a warning suppression in the
|
||||||
|
// previous line.
|
||||||
|
while let Some(node) = searched_node {
|
||||||
|
let mut searched_decorator = node.prev_attached_decorator();
|
||||||
|
while let Some(sibling) = searched_decorator {
|
||||||
|
let decorator = sibling.cast::<ast::Decorator>().unwrap();
|
||||||
|
if check_decorator_suppresses_warning(decorator, identifier) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
searched_decorator = sibling.prev_attached_decorator();
|
||||||
|
}
|
||||||
|
searched_node = node.parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if an 'allow' decorator would cause a warning with a particular
|
||||||
|
/// identifier to be suppressed.
|
||||||
|
fn check_decorator_suppresses_warning(
|
||||||
|
decorator: ast::Decorator,
|
||||||
|
warning: &Identifier,
|
||||||
|
) -> bool {
|
||||||
|
if decorator.name().as_str() != "allow" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for argument in decorator.arguments() {
|
||||||
|
if warning.name() == argument.get() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// A result type with a file-related error.
|
/// A result type with a file-related error.
|
||||||
pub type FileResult<T> = Result<T, FileError>;
|
pub type FileResult<T> = Result<T, FileError>;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterato
|
|||||||
use crate::diag::{self, SourceDiagnostic, SourceResult, Trace, Tracepoint};
|
use crate::diag::{self, SourceDiagnostic, SourceResult, Trace, Tracepoint};
|
||||||
use crate::foundations::{Styles, Value};
|
use crate::foundations::{Styles, Value};
|
||||||
use crate::introspection::Introspector;
|
use crate::introspection::Introspector;
|
||||||
use crate::syntax::{ast, FileId, Span};
|
use crate::syntax::{FileId, Span};
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Holds all data needed during compilation.
|
/// Holds all data needed during compilation.
|
||||||
@ -115,9 +115,9 @@ impl Engine<'_> {
|
|||||||
route: route.clone(),
|
route: route.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trace errors immediately, followed by warnings on the sink.
|
// Trace errors and warnings on the sink immediately.
|
||||||
let call_result = f(&mut engine).trace(world, make_point, span);
|
let call_result = f(&mut engine).trace(world, make_point, span);
|
||||||
sink.trace_warnings(world, make_point, span);
|
sink.warnings = std::mem::take(&mut sink.warnings).trace(world, make_point, span);
|
||||||
|
|
||||||
// Push the accumulated warnings and other fields back to the
|
// Push the accumulated warnings and other fields back to the
|
||||||
// original sink after we have modified them. This is needed so the
|
// original sink after we have modified them. This is needed so the
|
||||||
@ -208,69 +208,11 @@ impl Sink {
|
|||||||
self.values
|
self.values
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply warning suppression, deduplication, and return the remaining
|
/// Deduplicates and suppresses the stored warnings before returning them.
|
||||||
/// warnings.
|
pub fn finish_warnings(mut self, world: &dyn World) -> EcoVec<SourceDiagnostic> {
|
||||||
///
|
diag::deduplicate_and_suppress_warnings(&mut self.warnings, world);
|
||||||
/// We deduplicate warnings which are identical modulo tracepoints.
|
|
||||||
/// This is so we can attempt to suppress each tracepoint separately,
|
|
||||||
/// without having one suppression discard all other attempts of raising
|
|
||||||
/// this same warning through a different set of tracepoints.
|
|
||||||
///
|
|
||||||
/// For example, calling the same function twice but suppressing a warning
|
|
||||||
/// on the first call shouldn't suppress on the second, so each set of
|
|
||||||
/// tracepoints for a particular warning matters. If at least one instance
|
|
||||||
/// of a warning isn't suppressed, the warning will be returned, and the
|
|
||||||
/// remaining duplicates are discarded.
|
|
||||||
pub fn suppress_and_deduplicate_warnings(
|
|
||||||
mut self,
|
|
||||||
world: &dyn World,
|
|
||||||
) -> EcoVec<SourceDiagnostic> {
|
|
||||||
let mut unsuppressed_warning_set = HashSet::<u128>::default();
|
|
||||||
|
|
||||||
// Only retain warnings which weren't locally suppressed where they
|
|
||||||
// were emitted or at any of their tracepoints.
|
|
||||||
self.warnings.retain(|diag| {
|
|
||||||
let hash =
|
|
||||||
crate::utils::hash128(&(&diag.span, &diag.identifier, &diag.message));
|
|
||||||
if unsuppressed_warning_set.contains(&hash) {
|
|
||||||
// This warning - with the same span, identifier and message -
|
|
||||||
// was already raised and not suppressed before, with a
|
|
||||||
// different set of tracepoints. Therefore, we should not raise
|
|
||||||
// it again, and checking for suppression is unnecessary.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(identifier) = &diag.identifier else {
|
|
||||||
// Can't suppress without an identifier. Therefore, retain the
|
|
||||||
// warning. It is not a duplicate due to the check above.
|
|
||||||
unsuppressed_warning_set.insert(hash);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
let should_raise = !is_warning_suppressed(diag.span, world, identifier)
|
|
||||||
&& !diag.trace.iter().any(|tracepoint| {
|
|
||||||
is_warning_suppressed(tracepoint.span, world, identifier)
|
|
||||||
});
|
|
||||||
|
|
||||||
// If this warning wasn't suppressed, any further duplicates (with
|
|
||||||
// different tracepoints) should be removed.
|
|
||||||
should_raise && unsuppressed_warning_set.insert(hash)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.warnings
|
self.warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a tracepoint to all warnings outside the given span.
|
|
||||||
pub fn trace_warnings<F>(
|
|
||||||
&mut self,
|
|
||||||
world: Tracked<dyn World + '_>,
|
|
||||||
make_point: F,
|
|
||||||
span: Span,
|
|
||||||
) where
|
|
||||||
F: Fn() -> Tracepoint,
|
|
||||||
{
|
|
||||||
self.warnings = std::mem::take(&mut self.warnings).trace(world, make_point, span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[comemo::track]
|
#[comemo::track]
|
||||||
@ -329,57 +271,6 @@ impl Sink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a given warning is suppressed given one span it has a tracepoint
|
|
||||||
/// in. If one of the ancestors of the node where the warning occurred has a
|
|
||||||
/// warning suppression decorator sibling right before it suppressing this
|
|
||||||
/// particular warning, the warning is considered suppressed.
|
|
||||||
fn is_warning_suppressed(
|
|
||||||
span: Span,
|
|
||||||
world: &dyn World,
|
|
||||||
identifier: &diag::Identifier,
|
|
||||||
) -> bool {
|
|
||||||
// Don't suppress detached warnings.
|
|
||||||
let Some(source) = span.id().and_then(|file| world.source(file).ok()) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
let search_root = source.find(span);
|
|
||||||
let mut searched_node = search_root.as_ref();
|
|
||||||
|
|
||||||
// Walk the parent nodes to check for a warning suppression in the
|
|
||||||
// previous line.
|
|
||||||
while let Some(node) = searched_node {
|
|
||||||
let mut searched_decorator = node.prev_attached_decorator();
|
|
||||||
while let Some(sibling) = searched_decorator {
|
|
||||||
let decorator = sibling.cast::<ast::Decorator>().unwrap();
|
|
||||||
if check_decorator_suppresses_warning(decorator, identifier) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
searched_decorator = sibling.prev_attached_decorator();
|
|
||||||
}
|
|
||||||
searched_node = node.parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_decorator_suppresses_warning(
|
|
||||||
decorator: ast::Decorator,
|
|
||||||
warning: &diag::Identifier,
|
|
||||||
) -> bool {
|
|
||||||
if decorator.name().as_str() != "allow" {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for argument in decorator.arguments() {
|
|
||||||
if warning.name() == argument.get() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The route the engine took during compilation. This is used to detect
|
/// The route the engine took during compilation. This is used to detect
|
||||||
/// cyclic imports and excessive nesting.
|
/// cyclic imports and excessive nesting.
|
||||||
pub struct Route<'a> {
|
pub struct Route<'a> {
|
||||||
|
@ -56,7 +56,6 @@ pub use typst_syntax as syntax;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use typst_utils as utils;
|
pub use typst_utils as utils;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::{Deref, Range};
|
||||||
|
|
||||||
use comemo::{Track, Tracked, Validate};
|
use comemo::{Track, Tracked, Validate};
|
||||||
@ -87,9 +86,9 @@ use crate::visualize::Color;
|
|||||||
pub fn compile(world: &dyn World) -> Warned<SourceResult<Document>> {
|
pub fn compile(world: &dyn World) -> Warned<SourceResult<Document>> {
|
||||||
let mut sink = Sink::new();
|
let mut sink = Sink::new();
|
||||||
let output = compile_inner(world.track(), Traced::default().track(), &mut sink)
|
let output = compile_inner(world.track(), Traced::default().track(), &mut sink)
|
||||||
.map_err(deduplicate);
|
.map_err(diag::deduplicate_errors);
|
||||||
|
|
||||||
let warnings = sink.suppress_and_deduplicate_warnings(world);
|
let warnings = sink.finish_warnings(world);
|
||||||
Warned { output, warnings }
|
Warned { output, warnings }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,16 +177,6 @@ fn compile_inner(
|
|||||||
Ok(document)
|
Ok(document)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deduplicate diagnostics.
|
|
||||||
fn deduplicate(mut diags: EcoVec<SourceDiagnostic>) -> EcoVec<SourceDiagnostic> {
|
|
||||||
let mut unique = HashSet::new();
|
|
||||||
diags.retain(|diag| {
|
|
||||||
let hash = crate::utils::hash128(&(&diag.span, &diag.message));
|
|
||||||
unique.insert(hash)
|
|
||||||
});
|
|
||||||
diags
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The environment in which typesetting occurs.
|
/// The environment in which typesetting occurs.
|
||||||
///
|
///
|
||||||
/// All loading functions (`main`, `source`, `file`, `font`) should perform
|
/// All loading functions (`main`, `source`, `file`, `font`) should perform
|
||||||
|
Loading…
x
Reference in New Issue
Block a user