diff --git a/tools/pdl/src/ast.rs b/tools/pdl/src/ast.rs
index 01ce05c6453b0c345b69973b1c5c7a7dd01662b0..8d4e87592a4a5f851ad9521a95bdb336b291b141 100644
--- a/tools/pdl/src/ast.rs
+++ b/tools/pdl/src/ast.rs
@@ -168,16 +168,6 @@ pub struct Grammar {
     pub declarations: Vec<Decl>,
 }
 
-/// Implemented for all AST elements.
-pub trait Located<'d> {
-    fn loc(&'d self) -> &'d SourceRange;
-}
-
-/// Implemented for named AST elements.
-pub trait Named<'d> {
-    fn id(&'d self) -> Option<&'d String>;
-}
-
 impl SourceLocation {
     /// Construct a new source location.
     ///
@@ -244,8 +234,34 @@ impl Grammar {
     }
 }
 
-impl<'d> Located<'d> for Field {
-    fn loc(&'d self) -> &'d SourceRange {
+impl Decl {
+    pub fn loc(&self) -> &SourceRange {
+        match self {
+            Decl::Checksum { loc, .. }
+            | Decl::CustomField { loc, .. }
+            | Decl::Enum { loc, .. }
+            | Decl::Packet { loc, .. }
+            | Decl::Struct { loc, .. }
+            | Decl::Group { loc, .. }
+            | Decl::Test { loc, .. } => loc,
+        }
+    }
+
+    pub fn id(&self) -> Option<&String> {
+        match self {
+            Decl::Test { .. } => None,
+            Decl::Checksum { id, .. }
+            | Decl::CustomField { id, .. }
+            | Decl::Enum { id, .. }
+            | Decl::Packet { id, .. }
+            | Decl::Struct { id, .. }
+            | Decl::Group { id, .. } => Some(id),
+        }
+    }
+}
+
+impl Field {
+    pub fn loc(&self) -> &SourceRange {
         match self {
             Field::Checksum { loc, .. }
             | Field::Padding { loc, .. }
@@ -261,24 +277,8 @@ impl<'d> Located<'d> for Field {
             | Field::Group { loc, .. } => loc,
         }
     }
-}
-
-impl<'d> Located<'d> for Decl {
-    fn loc(&'d self) -> &'d SourceRange {
-        match self {
-            Decl::Checksum { loc, .. }
-            | Decl::CustomField { loc, .. }
-            | Decl::Enum { loc, .. }
-            | Decl::Packet { loc, .. }
-            | Decl::Struct { loc, .. }
-            | Decl::Group { loc, .. }
-            | Decl::Test { loc, .. } => loc,
-        }
-    }
-}
 
-impl<'d> Named<'d> for Field {
-    fn id(&'d self) -> Option<&'d String> {
+    pub fn id(&self) -> Option<&String> {
         match self {
             Field::Checksum { .. }
             | Field::Padding { .. }
@@ -296,20 +296,6 @@ impl<'d> Named<'d> for Field {
     }
 }
 
-impl<'d> Named<'d> for Decl {
-    fn id(&'d self) -> Option<&'d String> {
-        match self {
-            Decl::Test { .. } => None,
-            Decl::Checksum { id, .. }
-            | Decl::CustomField { id, .. }
-            | Decl::Enum { id, .. }
-            | Decl::Packet { id, .. }
-            | Decl::Struct { id, .. }
-            | Decl::Group { id, .. } => Some(id),
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/tools/pdl/src/lint.rs b/tools/pdl/src/lint.rs
index 0b2d3bc62c0452380d4c257c9e7c144e757b8968..ebd9a0aa366ec44ee8ac7511fb8725d46f2f8264 100644
--- a/tools/pdl/src/lint.rs
+++ b/tools/pdl/src/lint.rs
@@ -22,7 +22,8 @@ pub trait Lintable {
 /// Each field but the last in the chain is a typedef field of a group.
 /// The last field can also be a typedef field of a group if the chain is
 /// not fully expanded.
-type FieldPath<'d> = Vec<&'d Field>;
+#[derive(Clone)]
+struct FieldPath<'d>(Vec<&'d Field>);
 
 /// Gather information about the full grammar declaration.
 struct Scope<'d> {
@@ -90,9 +91,9 @@ impl<'d> std::hash::Hash for &'d Decl {
     }
 }
 
-impl<'d> Located<'d> for FieldPath<'d> {
-    fn loc(&'d self) -> &'d SourceRange {
-        self.last().unwrap().loc()
+impl FieldPath<'_> {
+    fn loc(&self) -> &SourceRange {
+        self.0.last().unwrap().loc()
     }
 }
 
@@ -147,7 +148,7 @@ impl<'d> PacketScope<'d> {
     fn insert(&mut self, field: &'d Field, result: &mut LintDiagnostics) {
         match field {
             Field::Checksum { loc, field_id, .. } => {
-                self.checksums.insert(field_id.clone(), vec![field]).map(|prev| {
+                self.checksums.insert(field_id.clone(), FieldPath(vec![field])).map(|prev| {
                     result.push(
                         Diagnostic::error()
                             .with_message(format!(
@@ -167,7 +168,7 @@ impl<'d> PacketScope<'d> {
             Field::Padding { .. } | Field::Reserved { .. } | Field::Fixed { .. } => None,
 
             Field::Size { loc, field_id, .. } | Field::Count { loc, field_id, .. } => {
-                self.sizes.insert(field_id.clone(), vec![field]).map(|prev| {
+                self.sizes.insert(field_id.clone(), FieldPath(vec![field])).map(|prev| {
                     result.push(
                         Diagnostic::error()
                             .with_message(format!(
@@ -195,7 +196,7 @@ impl<'d> PacketScope<'d> {
                             ]),
                     )
                 }
-                self.payload = Some(vec![field]);
+                self.payload = Some(FieldPath(vec![field]));
                 None
             }
 
@@ -203,7 +204,7 @@ impl<'d> PacketScope<'d> {
             | Field::Scalar { loc, id, .. }
             | Field::Typedef { loc, id, .. } => self
                 .named
-                .insert(id.clone(), vec![field])
+                .insert(id.clone(), FieldPath(vec![field]))
                 .map(|prev| result.err_redeclared(id, "field", loc, prev.loc())),
 
             Field::Group { loc, group_id, .. } => {
@@ -314,8 +315,8 @@ impl<'d> PacketScope<'d> {
         }
         for (id, field) in packet_scope.named.iter() {
             let mut path = vec![group];
-            path.extend(field.clone());
-            if let Some(prev) = self.named.insert(id.clone(), path) {
+            path.extend(field.0.clone());
+            if let Some(prev) = self.named.insert(id.clone(), FieldPath(path)) {
                 err_redeclared_by_group(
                     result,
                     format!("inserted group redeclares field `{}`", id),
@@ -328,8 +329,8 @@ impl<'d> PacketScope<'d> {
         // Append group fields to the finalizeed fields.
         for field in packet_scope.fields.iter() {
             let mut path = vec![group];
-            path.extend(field.clone());
-            self.fields.push(path);
+            path.extend(field.0.clone());
+            self.fields.push(FieldPath(path));
         }
 
         // Append group constraints to the caller packet_scope.
@@ -357,7 +358,7 @@ impl<'d> PacketScope<'d> {
     /// Cleanup scope after processing all fields.
     fn finalize(&mut self, result: &mut LintDiagnostics) {
         // Check field shadowing.
-        for f in self.fields.iter().map(|f| f.last().unwrap()) {
+        for f in self.fields.iter().map(|f| f.0.last().unwrap()) {
             if let Some(id) = f.id() {
                 if let Some(prev) = self.all_fields.insert(id.clone(), f) {
                     result.push(
@@ -533,7 +534,7 @@ impl<'d> Scope<'d> {
                         }
                     }
                     Field::Typedef { type_id, .. } => {
-                        lscope.fields.push(vec![f]);
+                        lscope.fields.push(FieldPath(vec![f]));
                         match scope.typedef.get(type_id) {
                             None => result.push(
                                 Diagnostic::error()
@@ -549,7 +550,7 @@ impl<'d> Scope<'d> {
                             Some(_) => (),
                         }
                     }
-                    _ => lscope.fields.push(vec![f]),
+                    _ => lscope.fields.push(FieldPath(vec![f])),
                 }
             }
 
@@ -652,7 +653,7 @@ fn lint_checksum(
     let checksum_loc = path.loc();
     let field_decl = packet_scope.named.get(field_id);
 
-    match field_decl.and_then(|f| f.last()) {
+    match field_decl.and_then(|f| f.0.last()) {
         Some(Field::Typedef { loc: field_loc, type_id, .. }) => {
             // Check declaration type of checksum field.
             match scope.typedef.get(type_id) {
@@ -675,7 +676,7 @@ fn lint_checksum(
                 None => (),
             };
             // Check declaration order of checksum field.
-            match field_decl.and_then(|f| f.first()) {
+            match field_decl.and_then(|f| f.0.first()) {
                 Some(decl) if decl.loc().start > checksum_loc.start => result.push(
                     Diagnostic::error()
                         .with_message("invalid checksum start declaration")
@@ -722,14 +723,14 @@ fn lint_size(
     let size_loc = path.loc();
 
     if field_id == "_payload_" {
-        return match packet_scope.payload.as_ref().and_then(|f| f.last()) {
+        return match packet_scope.payload.as_ref().and_then(|f| f.0.last()) {
             Some(Field::Body { .. }) => result.push(
                 Diagnostic::error()
                     .with_message("size field uses undeclared payload field, did you mean _body_ ?")
                     .with_labels(vec![size_loc.primary()]),
             ),
             Some(Field::Payload { .. }) => {
-                match packet_scope.payload.as_ref().and_then(|f| f.first()) {
+                match packet_scope.payload.as_ref().and_then(|f| f.0.first()) {
                     Some(field) if field.loc().start < size_loc.start => result.push(
                         Diagnostic::error().with_message("invalid size field").with_labels(vec![
                             size_loc
@@ -750,14 +751,14 @@ fn lint_size(
         };
     }
     if field_id == "_body_" {
-        return match packet_scope.payload.as_ref().and_then(|f| f.last()) {
+        return match packet_scope.payload.as_ref().and_then(|f| f.0.last()) {
             Some(Field::Payload { .. }) => result.push(
                 Diagnostic::error()
                     .with_message("size field uses undeclared body field, did you mean _payload_ ?")
                     .with_labels(vec![size_loc.primary()]),
             ),
             Some(Field::Body { .. }) => {
-                match packet_scope.payload.as_ref().and_then(|f| f.first()) {
+                match packet_scope.payload.as_ref().and_then(|f| f.0.first()) {
                     Some(field) if field.loc().start < size_loc.start => result.push(
                         Diagnostic::error().with_message("invalid size field").with_labels(vec![
                             size_loc
@@ -780,7 +781,7 @@ fn lint_size(
 
     let field = packet_scope.named.get(field_id);
 
-    match field.and_then(|f| f.last()) {
+    match field.and_then(|f| f.0.last()) {
         Some(Field::Array { size: Some(_), loc: array_loc, .. }) => result.push(
             Diagnostic::warning()
                 .with_message(format!("size field uses array `{}` with static size", field_id))
@@ -807,7 +808,7 @@ fn lint_size(
 
         None => result.err_undeclared(field_id, size_loc),
     };
-    match field.and_then(|f| f.first()) {
+    match field.and_then(|f| f.0.first()) {
         Some(field) if field.loc().start < size_loc.start => {
             result.push(Diagnostic::error().with_message("invalid size field").with_labels(vec![
                     size_loc
@@ -839,7 +840,7 @@ fn lint_count(
     let count_loc = path.loc();
     let field = packet_scope.named.get(field_id);
 
-    match field.and_then(|f| f.last()) {
+    match field.and_then(|f| f.0.last()) {
         Some(Field::Array { size: Some(_), loc: array_loc, .. }) => result.push(
             Diagnostic::warning()
                 .with_message(format!("count field uses array `{}` with static size", field_id))
@@ -867,7 +868,7 @@ fn lint_count(
 
         None => result.err_undeclared(field_id, count_loc),
     };
-    match field.and_then(|f| f.first()) {
+    match field.and_then(|f| f.0.first()) {
         Some(field) if field.loc().start < count_loc.start => {
             result.push(Diagnostic::error().with_message("invalid count field").with_labels(vec![
                     count_loc.primary().with_message(format!(
@@ -1043,7 +1044,7 @@ fn lint_field(
     field: &FieldPath,
     result: &mut LintDiagnostics,
 ) {
-    match field.last().unwrap() {
+    match field.0.last().unwrap() {
         Field::Checksum { field_id, .. } => {
             lint_checksum(scope, packet_scope, field, field_id, result)
         }