diff options
| author | Mica White <botahamec@outlook.com> | 2026-09-27 15:48:03 -0400 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-09-27 15:48:03 -0400 |
| commit | 35f6fc17d0c98f424e01904b8856cf42c80bcfd3 (patch) | |
| tree | ba99ada4ac6e149c00e5d820b9c5f65e82fed980 /src/diagnostics.rs | |
| parent | 8399983fd45bc0f11eac9def280c0833ffb93c04 (diff) | |
Add buggy diagnostics
Diffstat (limited to 'src/diagnostics.rs')
| -rw-r--r-- | src/diagnostics.rs | 104 |
1 files changed, 72 insertions, 32 deletions
diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 514818a..d2db96b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -13,7 +13,7 @@ mod forbidden_top_level_tag; mod html_tag; mod image_tag; mod non_alphabetic_attribute_name; -mod non_alphabetic_tag_name; +mod non_alphanumeric_tag_name; mod non_self_closing_void_tag; mod non_void_self_closing_tag; mod self_closing_end_tag; @@ -27,7 +27,7 @@ mod unterminated_tag; const VOID_TAGS: &[&str] = &[ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", - "track", "wbr", + "track", "wbr", "import", ]; const ALLOWED_TOP_LEVEL_TAGS: &[&str] = &["import", "const", "block", "head", "body"]; @@ -35,6 +35,12 @@ pub struct DiagnosticsContext { source_code: &'static str, } +impl DiagnosticsContext { + pub fn new(source_code: &'static str) -> Self { + Self { source_code } + } +} + trait TokenDiagnostic: Sized + Send + Sync + 'static { fn handle_token(_: &Token<'_>) -> Option<Self> { None @@ -66,13 +72,16 @@ trait AstDiagnostic: Sized + Send + Sync + 'static { fn maybe_print_diagnostic<D: Diagnostic + Send + Sync + 'static>( ctx: &DiagnosticsContext, diagnostic: Option<D>, -) { +) -> Option<()> { if let Some(diagnostic) = diagnostic { eprintln!( - "{}", + "{:?}", Error::new(diagnostic).with_source_code(ctx.source_code) ); + return None; } + + Some(()) } #[derive(Debug, Error, Diagnostic)] @@ -119,9 +128,10 @@ pub fn run_character_diagnostics(source: &'static [u8]) -> Option<&'static str> fn run_token_diagnostic<D: Diagnostic + TokenDiagnostic>( ctx: &DiagnosticsContext, tokens: &[Token<'_>], -) { +) -> Option<()> { + let mut has_failed = false; for token in tokens { - maybe_print_diagnostic(ctx, D::handle_token(token)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_token(token)).is_none(); let TokenType::Tag { attribute_tokens, .. @@ -131,53 +141,82 @@ fn run_token_diagnostic<D: Diagnostic + TokenDiagnostic>( }; for token in attribute_tokens { - maybe_print_diagnostic(ctx, D::handle_attribute_token(token)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_attribute_token(token)).is_none(); } } + + (!has_failed).then_some(()) } fn run_ast_diagnostic<D: AstDiagnostic + Diagnostic>( ctx: &DiagnosticsContext, document: &Document<'_>, -) { - maybe_print_diagnostic(ctx, D::handle_document(document)); +) -> Option<()> { + let mut has_failed = maybe_print_diagnostic(ctx, D::handle_document(document)).is_none(); for node in document.children { - maybe_print_diagnostic(ctx, D::handle_node(node)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_node(node)).is_none(); let Node::Element(element) = node else { continue; }; - maybe_print_diagnostic(ctx, D::handle_element(element)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_element(element)).is_none(); for attribute in element.attributes { - maybe_print_diagnostic(ctx, D::handle_attribute(attribute)); + has_failed |= maybe_print_diagnostic(ctx, D::handle_attribute(attribute)).is_none(); } } + + (!has_failed).then_some(()) } -pub fn run_token_diagnostics(ctx: &DiagnosticsContext, tokens: &[Token<'_>]) { - run_token_diagnostic::<non_void_self_closing_tag::NonVoidSelfClosingTag>(ctx, tokens); - run_token_diagnostic::<non_self_closing_void_tag::NonSelfClosingVoidTag>(ctx, tokens); - run_token_diagnostic::<end_tag_with_attributes::EndTagWithAttributes>(ctx, tokens); - run_token_diagnostic::<self_closing_end_tag::SelfClosingEndTag>(ctx, tokens); - run_token_diagnostic::<non_alphabetic_tag_name::NonAlphabeticTagName>(ctx, tokens); - run_token_diagnostic::<empty_tag_name::EmptyTagName>(ctx, tokens); - run_token_diagnostic::<unterminated_tag::UnterminatedTag>(ctx, tokens); - run_token_diagnostic::<unterminated_comment::UnterminatedComment>(ctx, tokens); - run_token_diagnostic::<non_alphabetic_attribute_name::NonAlphabeticAttributeName>(ctx, tokens); +pub fn run_token_diagnostics(ctx: &DiagnosticsContext, tokens: &[Token<'_>]) -> Option<()> { + let mut has_failed = false; + has_failed |= + run_token_diagnostic::<non_void_self_closing_tag::NonVoidSelfClosingTag>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<non_self_closing_void_tag::NonSelfClosingVoidTag>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<end_tag_with_attributes::EndTagWithAttributes>(ctx, tokens) + .is_none(); + has_failed |= + run_token_diagnostic::<self_closing_end_tag::SelfClosingEndTag>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<non_alphanumeric_tag_name::NonAlphaNumericTagName>(ctx, tokens) + .is_none(); + has_failed |= run_token_diagnostic::<empty_tag_name::EmptyTagName>(ctx, tokens).is_none(); + has_failed |= run_token_diagnostic::<unterminated_tag::UnterminatedTag>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<unterminated_comment::UnterminatedComment>(ctx, tokens).is_none(); + has_failed |= + run_token_diagnostic::<non_alphabetic_attribute_name::NonAlphabeticAttributeName>( + ctx, tokens, + ) + .is_none(); // TODO invalid character reference + (!has_failed).then_some(()) } -pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) { - run_ast_diagnostic::<stray_equal_sign::StrayEqualSign>(ctx, document); - run_ast_diagnostic::<too_many_equal_signs::TooManyEqualSigns>(ctx, document); - run_ast_diagnostic::<duplicate_attribute_names::DuplicateAttributeNames>(ctx, document); - run_ast_diagnostic::<top_level_character_data::TopLevelCharacterData>(ctx, document); - run_ast_diagnostic::<forbidden_top_level_tag::ForbiddenTopLevelTag>(ctx, document); - run_ast_diagnostic::<stray_end_tag::StrayEndTag>(ctx, document); - run_ast_diagnostic::<unclosed_start_tag::UnclosedStartTag>(ctx, document); - run_ast_diagnostic::<html_tag::HtmlTag>(ctx, document); - run_ast_diagnostic::<image_tag::ImageTag>(ctx, document); +pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) -> Option<()> { + let mut has_failed = false; + has_failed |= run_ast_diagnostic::<stray_equal_sign::StrayEqualSign>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<too_many_equal_signs::TooManyEqualSigns>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<duplicate_attribute_names::DuplicateAttributeNames>(ctx, document) + .is_none(); + has_failed |= + run_ast_diagnostic::<top_level_character_data::TopLevelCharacterData>(ctx, document) + .is_none(); + has_failed |= + run_ast_diagnostic::<forbidden_top_level_tag::ForbiddenTopLevelTag>(ctx, document) + .is_none(); + has_failed |= run_ast_diagnostic::<stray_end_tag::StrayEndTag>(ctx, document).is_none(); + has_failed |= + run_ast_diagnostic::<unclosed_start_tag::UnclosedStartTag>(ctx, document).is_none(); + has_failed |= run_ast_diagnostic::<html_tag::HtmlTag>(ctx, document).is_none(); + has_failed |= run_ast_diagnostic::<image_tag::ImageTag>(ctx, document).is_none(); // TODO invalid <kuht-var /> // TODO invalid <import /> // TODO invalid <const /> @@ -214,4 +253,5 @@ pub fn run_ast_diagnostics(ctx: &DiagnosticsContext, document: &Document<'_>) { // TODO aria tags don't match tag semantics // TODO empty or missing image src // TODO missing alt text for image (unless inside figure with caption, or has title, or meta name = "generator") + (!has_failed).then_some(()) } |
