summaryrefslogtreecommitdiff
path: root/src/diagnostics.rs
diff options
context:
space:
mode:
authorMica White <botahamec@outlook.com>2026-09-27 15:48:03 -0400
committerMica White <botahamec@outlook.com>2026-09-27 15:48:03 -0400
commit35f6fc17d0c98f424e01904b8856cf42c80bcfd3 (patch)
treeba99ada4ac6e149c00e5d820b9c5f65e82fed980 /src/diagnostics.rs
parent8399983fd45bc0f11eac9def280c0833ffb93c04 (diff)
Add buggy diagnostics
Diffstat (limited to 'src/diagnostics.rs')
-rw-r--r--src/diagnostics.rs104
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(())
}