summaryrefslogtreecommitdiff
path: root/src/diagnostics/duplicate_attribute_names.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/diagnostics/duplicate_attribute_names.rs')
-rw-r--r--src/diagnostics/duplicate_attribute_names.rs41
1 files changed, 41 insertions, 0 deletions
diff --git a/src/diagnostics/duplicate_attribute_names.rs b/src/diagnostics/duplicate_attribute_names.rs
new file mode 100644
index 0000000..542d8ec
--- /dev/null
+++ b/src/diagnostics/duplicate_attribute_names.rs
@@ -0,0 +1,41 @@
+use std::collections::HashMap;
+
+use miette::Diagnostic;
+use thiserror::Error;
+
+use crate::{
+ diagnostics::AstDiagnostic,
+ parse::{Attribute, Element},
+ tokenize::Span,
+};
+
+#[derive(Error, Debug, Diagnostic)]
+#[error("Multiple attributes have the same name")]
+#[diagnostic(code(DuplicateAttributeNames), severity(Error))]
+pub struct DuplicateAttributeNames {
+ #[label(collection)]
+ duplicates: Vec<Span>,
+}
+
+impl AstDiagnostic for DuplicateAttributeNames {
+ fn handle_element(element: &Element<'_>) -> Option<Self> {
+ let mut seen_attributes = HashMap::<&str, Attribute<'_>>::new();
+ for attribute in element.attributes {
+ let Some(name) = attribute.name_lowercase() else {
+ continue;
+ };
+ if let Some(duplicate) = seen_attributes.get(name) {
+ return Some(Self {
+ duplicates: vec![
+ duplicate.name_token().expect("a named attribute").span,
+ attribute.name_token().expect("a named attribute").span,
+ ],
+ });
+ }
+
+ seen_attributes.insert(name, *attribute);
+ }
+
+ None
+ }
+}