///|
fn resource_members(model : Model, id : ShapeId) -> Array[Shape] {
  let result : Array[Shape] = []
  if model.shapes.get(id) is Some(shape) {
    let members = match shape.body {
      Structure(members) | Union(members) => members
      _ => []
    }
    for member_ in members {
      if model.shapes.get(member_) is Some(member_) {
        result.push(member_)
      }
    }
  }
  result
}

///|
fn resource_member_name(member_ : Shape) -> String {
  member_.id.member.unwrap_or("")
}

///|
fn resource_bindings(
  model : Model,
  identifiers : Map[String, ShapeId],
  structure : ShapeId,
) -> Map[String, String] {
  let bindings : Map[String, String] = Map([])
  let explicit : Map[String, String] = Map([])
  for member_ in resource_members(model, structure) {
    let name = resource_member_name(member_)
    if member_.trait_value("smithy.api#resourceIdentifier")
      is Some(String(binding)) {
      explicit[binding] = name
    } else if member_.body is Member(target~) &&
      member_.trait_value("smithy.api#required") is Some(_) &&
      identifiers.get(name) == Some(target) {
      bindings[name] = name
    }
  }
  for name, member_ in explicit {
    bindings[name] = member_
  }
  bindings
}

///|
fn resource_binding_message(bindings : Map[String, String]) -> String {
  let descriptions : Array[String] = []
  for name, target in bindings {
    descriptions.push(
      "required member named `" + name + "` that targets `" + target + "`",
    )
  }
  descriptions.sort()
  descriptions.join(", ")
}

///|
fn resource_ticked_list(values : Array[String]) -> String {
  values.sort()
  values.map(value => "`" + value + "`").join(", ")
}

///|
fn resource_operation_bindings(shape : Shape) -> Array[(String, ShapeId, Bool)] {
  let result : Array[(String, ShapeId, Bool)] = []
  if shape.body
    is Resource(
      create~,
      put~,
      read~,
      update~,
      delete~,
      list~,
      operations~,
      collectionOperations~,
      ..
    ) {
    for
      (name, operation, collection) in [
        ("create", create, true),
        ("put", put, false),
        ("read", read, false),
        ("update", update, false),
        ("delete", delete, false),
        ("list", list, true),
      ] {
      if operation is Some(operation) {
        result.push((name, operation, collection))
      }
    }
    for operation in operations {
      result.push((operation.name, operation, false))
    }
    for operation in collectionOperations {
      result.push((operation.name, operation, true))
    }
  }
  result.map(entry => {
    let (name, operation, collection) = entry
    let is_collection = collection ||
      (match shape.body {
        Resource(create~, list~, collectionOperations~, ..) =>
          create == Some(operation) ||
          list == Some(operation) ||
          collectionOperations.contains(operation)
        _ => false
      })
    (name, operation, is_collection)
  })
}

///|
fn resource_is_lifecycle(
  resource : Shape,
  name : String,
  operation : ShapeId,
) -> Bool {
  match resource.body {
    Resource(create~, put~, read~, update~, delete~, list~, ..) =>
      (match name {
        "create" => create
        "put" => put
        "read" => read
        "update" => update
        "delete" => delete
        "list" => list
        _ => None
      }) ==
      Some(operation)
    _ => false
  }
}

///|
fn resource_descendants(model : Model, roots : Array[ShapeId]) -> Array[Shape] {
  let pending = roots.copy()
  let seen : Map[ShapeId, Bool] = Map([])
  let result : Array[Shape] = []
  while pending.pop() is Some(id) {
    if seen.contains(id) {
      continue
    }
    seen[id] = true
    if model.shapes.get(id) is Some(shape) &&
      shape.body is Resource(resources~, ..) {
      result.push(shape)
      pending.append(resources)
    }
  }
  result
}

///|
fn Model::resource_identifier_trait_events(
  self : Model,
) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  for _, structure in self.shapes {
    let members = resource_members(self, structure.id)
    if !members
      .iter()
      .any(member_ => {
        member_.trait_value("smithy.api#resourceIdentifier") is Some(_)
      }) {
      continue
    }
    let potential : Map[String, Array[String]] = Map([])
    let explicit : Map[String, Array[String]] = Map([])
    for member_ in members {
      let name = resource_member_name(member_)
      let binding = match member_.trait_value("smithy.api#resourceIdentifier") {
        Some(String(binding)) => {
          explicit.get_or_init(binding, () => []).push(name)
          binding
        }
        _ => name
      }
      potential.get_or_init(binding, () => []).push(name)
    }
    for binding, names in potential {
      if names.length() > 1 && names.contains(binding) {
        let overrides = names.filter(name => name != binding)
        overrides.sort()
        events.push(
          validation_event(
            "ResourceIdentifierBinding",
            "WARNING",
            Some(structure),
            "Implicit resource identifier for '" +
            binding +
            "' is overridden by `resourceIdentifier` trait on members: '" +
            overrides.join("', '") +
            "'",
          ),
        )
      }
    }
    for binding, names in explicit {
      if names.length() > 1 {
        names.sort()
        events.push(
          validation_event(
            "ResourceIdentifierBinding",
            "ERROR",
            Some(structure),
            "Conflicting resource identifier member bindings found for identifier '" +
            binding +
            "' between members: '" +
            names.join("', '") +
            "'",
          ),
        )
      }
    }
  }
  events
}

///|
fn Model::resource_events(self : Model) -> Array[ValidationEvent] {
  let events = self.resource_identifier_trait_events()
  for _, resource in self.shapes {
    guard resource.body is Resource(identifiers~, properties~, resources~, ..) else {
      continue
    }
    for identifier, _ in identifiers {
      if properties
        .keys()
        .any(property => property.to_lower() == identifier.to_lower()) {
        events.push(
          validation_event(
            "ResourceIdentifier",
            "ERROR",
            Some(resource),
            "Resource identifier `" +
            identifier +
            "` cannot also be a resource property",
          ),
        )
      }
    }
    for child_id in resources {
      guard self.shapes.get(child_id) is Some(child) &&
        child.body is Resource(identifiers=child_identifiers, ..) else {
        continue
      }
      let missing : Array[String] = []
      let mismatched : Array[String] = []
      for name, target in identifiers {
        match child_identifiers.get(name) {
          None => missing.push(name)
          Some(actual) =>
            if actual != target {
              mismatched.push(
                "expected the `" +
                name +
                "` member to target `" +
                target.to_string() +
                "`, but found a target of `" +
                actual.to_string() +
                "`",
              )
            }
        }
      }
      missing.sort()
      mismatched.sort()
      if !missing.is_empty() {
        events.push(
          validation_event(
            "ResourceIdentifier",
            "ERROR",
            Some(child),
            "This resource is bound as a child of `" +
            resource.id.to_string() +
            "`, but it is invalid because its `identifiers` property is missing the following identifiers that are defined in `" +
            resource.id.to_string() +
            "`: [" +
            missing.join(", ") +
            "]",
          ),
        )
      }
      if !mismatched.is_empty() {
        events.push(
          validation_event(
            "ResourceIdentifier",
            "ERROR",
            Some(child),
            "The `identifiers` property of this resource is incompatible with its binding to `" +
            resource.id.to_string() +
            "`: " +
            mismatched.join("; "),
          ),
        )
      }
      let checked : Map[ShapeId, Bool] = Map([])
      for (_, operation_id, _) in resource_operation_bindings(child) {
        if checked.contains(operation_id) {
          continue
        }
        checked[operation_id] = true
        guard self.shapes.get(operation_id) is Some(operation) &&
          operation.body is Operation(input~, ..) else {
          continue
        }
        let bindings = resource_bindings(self, child_identifiers, input)
        let missing = identifiers
          .keys()
          .filter(name => !bindings.contains(name))
          .collect()
        if !missing.is_empty() {
          events.push(
            validation_event(
              "ResourceIdentifierBinding",
              "ERROR",
              Some(operation),
              "This operation is bound to the `" +
              child.id.to_string() +
              "` resource, which is a child of the `" +
              resource.id.to_string() +
              "` resource, and it is missing the following resource identifier bindings of `" +
              resource.id.to_string() +
              "`: [" +
              resource_ticked_list(missing) +
              "]",
            ),
          )
        }
      }
    }
    let checked : Map[ShapeId, Bool] = Map([])
    for
      (name, operation_id, collection) in resource_operation_bindings(resource) {
      guard self.shapes.get(operation_id) is Some(operation) &&
        operation.body is Operation(input~, ..) else {
        continue
      }
      if resource_is_lifecycle(resource, name, operation_id) {
        let is_readonly = operation.trait_value("smithy.api#readonly")
          is Some(_)
        let require_readonly = name == "read" || name == "list"
        if is_readonly != require_readonly {
          events.push(
            validation_event(
              "ResourceLifecycle",
              "ERROR",
              Some(resource),
              "The `" +
              name +
              "` lifecycle operation of this resource targets an invalid operation, `" +
              operation_id.to_string() +
              "`. The targeted operation " +
              (if require_readonly { "must" } else { "must not" }) +
              " be marked with the readonly trait.",
            ),
          )
        }
        if (name == "put" || name == "delete") &&
          operation.trait_value("smithy.api#idempotent") is None {
          events.push(
            validation_event(
              "ResourceLifecycle",
              "ERROR",
              Some(resource),
              "The `" +
              name +
              "` lifecycle operation of this resource targets an invalid operation, `" +
              operation_id.to_string() +
              "`. The targeted operation must be marked as idempotent.",
            ),
          )
        }
      }
      if checked.contains(operation_id) {
        continue
      }
      checked[operation_id] = true
      let bindings = resource_bindings(self, identifiers, input)
      let members = resource_members(self, input)
      let all_bound = identifiers
        .iter()
        .all(entry => {
          let (name, target) = entry
          match bindings.get(name) {
            Some(member_name) =>
              members
              .iter()
              .any(member_ => {
                resource_member_name(member_) == member_name &&
                member_.body is Member(target=actual) &&
                actual == target
              })
            None => false
          }
        })
      if collection && all_bound {
        events.push(
          validation_event(
            "ResourceIdentifierBinding",
            "ERROR",
            Some(operation),
            "This operation is bound as a collection operation on the `" +
            resource.id.to_string() +
            "` resource, but it improperly binds all of the identifiers of the resource to members of the operation input.",
          ),
        )
      } else if !collection && !all_bound {
        let expected : Map[String, String] = Map([])
        for name, target in identifiers {
          expected[name] = target.to_string()
        }
        events.push(
          validation_event(
            "ResourceIdentifierBinding",
            "ERROR",
            Some(operation),
            "This operation does not form a valid instance operation when bound to resource `" +
            resource.id.to_string() +
            "`. All of the identifiers of the resource were not implicitly or explicitly bound to the input of the operation. Expected the following identifier bindings: [" +
            resource_binding_message(expected) +
            "]. Found the following identifier bindings: [" +
            resource_binding_message(bindings) +
            "]",
          ),
        )
      }
    }
    events.append(self.resource_property_events(resource))
  }
  events.append(self.resource_service_events())
  events.append(self.resource_reference_events())
  events.append(self.resource_lifecycle_trait_events())
  events
}

///|
fn Model::resource_service_events(self : Model) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  for _, service in self.shapes {
    guard service.body is Service(operations~, resources~, ..) else { continue }
    let descendants = resource_descendants(self, resources)
    let operation_containers : Map[ShapeId, Array[String]] = Map([])
    for operation in operations {
      operation_containers
      .get_or_init(operation, () => [])
      .push("(service: `" + service.id.to_string() + "`)")
      guard self.shapes.get(operation) is Some(operation) &&
        operation.body is Operation(input~, output~, ..) else {
        continue
      }
      let members = resource_members(self, input)
      members.append(resource_members(self, output))
      let candidates : Array[String] = []
      for resource in descendants {
        guard resource.body is Resource(identifiers~, ..) else { continue }
        if members
          .iter()
          .any(member_ => {
            member_.body is Member(target~) &&
            member_.trait_value("smithy.api#required") is Some(_) &&
            identifiers.get(resource_member_name(member_)) == Some(target)
          }) {
          candidates.push(resource.id.to_string())
        }
      }
      if !candidates.is_empty() {
        events.push(
          validation_event(
            "ServiceBoundResourceOperation." +
            service.id.to_string() +
            "." +
            operation.id.name,
            "WARNING",
            Some(operation),
            "The `" +
            operation.id.to_string() +
            "` operation is bound to the `" +
            service.id.to_string() +
            "` service but has members that match identifiers of the following resource shapes: [" +
            resource_ticked_list(candidates) +
            "]. It may be more accurately bound to one of them than directly to the service.",
          ),
        )
      }
    }
    for resource in descendants {
      let containers : Array[String] = []
      if resources.contains(resource.id) {
        containers.push(service.id.to_string())
      }
      for parent in descendants {
        if parent.body is Resource(resources=children, ..) &&
          children.contains(resource.id) {
          containers.push(parent.id.to_string())
        }
      }
      if containers.length() > 1 {
        events.push(
          validation_event(
            "SingleResourceBinding",
            "ERROR",
            Some(resource),
            "A resource can appear only once in an entire service closure. This resource is illegally bound into the `" +
            service.id.to_string() +
            "` service closure from multiple shapes: [" +
            resource_ticked_list(containers) +
            "]",
          ),
        )
      }
      let checked : Map[ShapeId, Bool] = Map([])
      for (_, operation, _) in resource_operation_bindings(resource) {
        if checked.contains(operation) {
          continue
        }
        checked[operation] = true
        operation_containers
        .get_or_init(operation, () => [])
        .push("(resource: `" + resource.id.to_string() + "`)")
      }
    }
    for operation, containers in operation_containers {
      if containers.length() > 1 &&
        self.shapes.get(operation) is Some(operation) {
        containers.sort()
        events.push(
          validation_event(
            "SingleOperationBinding",
            "ERROR",
            Some(operation),
            "An operation can appear only once in an entire service closure. This operation is illegally bound into the `" +
            service.id.to_string() +
            "` service closure from multiple shapes: " +
            containers.join(", "),
          ),
        )
      }
    }
  }
  events
}

///|
fn Model::resource_reference_events(self : Model) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  let candidates : Map[String, Array[ShapeId]] = Map([])
  for _, resource in self.shapes {
    if resource.body is Resource(identifiers~, ..) {
      for name, target in identifiers {
        candidates
        .get_or_init(name + "|" + target.to_string(), () => [])
        .push(resource.id)
      }
    }
  }
  if candidates.is_empty() {
    return events
  }
  let lifecycle_members = self.resource_lifecycle_members()
  let edges = selector_edges(self).filter(edge => edge.label != "trait")
  let predecessors : Map[ShapeId, Array[ShapeId]] = Map([])
  for edge in edges {
    predecessors.get_or_init(edge.to, () => []).push(edge.from)
  }
  for _, member_ in self.shapes {
    guard member_.body is Member(target~) &&
      self.shapes.get(target) is Some(target_shape) &&
      target_shape.body is Simple(String) else {
      continue
    }
    guard candidates.get(
        resource_member_name(member_) + "|" + target.to_string(),
      )
      is Some(resources) else {
      continue
    }
    let ignored : Map[ShapeId, Bool] = Map([])
    for resource in lifecycle_members.get(member_.id).unwrap_or([]) {
      ignored[resource] = true
    }
    for id in [member_.id, member_.id.root()] {
      if self.shapes.get(id) is Some(shape) {
        for
          reference in validation_array(
            shape.trait_value("smithy.api#references"),
          ) {
          if validation_string(reference, "resource") is Some(resource) &&
            ShapeId::parse(resource) is Some(resource) {
            ignored[resource] = true
          }
        }
      }
    }
    let pending = [member_.id]
    let visited : Map[ShapeId, Bool] = Map([])
    while pending.pop() is Some(current) {
      if visited.contains(current) {
        continue
      }
      visited[current] = true
      if self.shapes.get(current) is Some(shape) &&
        shape.body is Resource(resources~, ..) {
        ignored[current] = true
        for child in resources {
          ignored[child] = true
        }
      }
      pending.append(predecessors.get(current).unwrap_or([]))
    }
    let missing = resources
      .filter(resource => !ignored.contains(resource))
      .map(id => id.to_string())
    if !missing.is_empty() {
      events.push(
        validation_event(
          "MemberShouldReferenceResource",
          "WARNING",
          Some(member_),
          "This member appears to reference the following resources without being included in a `@references` trait: [" +
          resource_ticked_list(missing) +
          "]",
        ),
      )
    }
  }
  events
}

///|
fn resource_field_chain(node : @jmespath.Node) -> Array[String]? {
  match node.kind {
    Field(name) => Some([name])
    Subexpression(left, right) =>
      if resource_field_chain(left) is Some(left) &&
        resource_field_chain(right) is Some(right) {
        left.append(right)
        Some(left)
      } else {
        None
      }
    _ => None
  }
}

///|
fn Model::resource_lifecycle_members(
  self : Model,
) -> Map[ShapeId, Array[ShapeId]] {
  let result : Map[ShapeId, Array[ShapeId]] = Map([])
  for _, operation in self.shapes {
    guard operation.body is Operation(input~, output~, ..) else { continue }
    for
      trait_name in [
        "createsResources", "putsResources", "readsResources", "updatesResources",
        "deletesResources",
      ] {
      for
        binding in validation_array(
          operation.trait_value("smithy.api#" + trait_name),
        ) {
        guard validation_string(binding, "resource") is Some(resource_id) &&
          self.get(resource_id) is Some(resource) &&
          resource.body is Resource(identifiers~, ..) else {
          continue
        }
        for structure in [input, output] {
          for member_ in resource_members(self, structure) {
            if identifiers.contains(resource_member_name(member_)) {
              result.get_or_init(member_.id, () => []).push(resource.id)
            }
          }
          if validation_field(binding, "identifiers") is Some(Object(locators)) {
            for _, locator in locators {
              guard validation_string(locator, "path") is Some(path) else {
                continue
              }
              let parsed = @jmespath.parse(path) catch { _ => continue }
              guard resource_field_chain(parsed.root) is Some(fields) else {
                continue
              }
              let mut current = structure
              let mut terminal : ShapeId? = None
              for field in fields {
                terminal = None
                for member_ in resource_members(self, current) {
                  if resource_member_name(member_) == field &&
                    member_.body is Member(target~) {
                    terminal = Some(member_.id)
                    current = target
                    break
                  }
                }
                if terminal is None {
                  break
                }
              }
              if terminal is Some(member_id) {
                result.get_or_init(member_id, () => []).push(resource.id)
              }
            }
          }
        }
      }
    }
  }
  result
}

///|
fn resource_property_shape(model : Model, structure : ShapeId) -> ShapeId {
  for member_ in resource_members(model, structure) {
    if member_.trait_value("smithy.api#nestedProperties") is Some(_) &&
      member_.body is Member(target~) &&
      model.shapes.get(target) is Some(shape) &&
      shape.body is Structure(_) {
      return target
    }
  }
  structure
}

///|
fn resource_list_element(model : Model, member_ : Shape) -> ShapeId? {
  if member_.body is Member(target~) &&
    model.shapes.get(target) is Some(list) &&
    list.body is List(element) &&
    model.shapes.get(element) is Some(element) &&
    element.body is Member(target~) &&
    model.shapes.get(target) is Some(structure) &&
    structure.body is Structure(_) {
    Some(target)
  } else {
    None
  }
}

///|
fn resource_element_shape(
  model : Model,
  structure : ShapeId,
  auto_detect : Bool,
) -> (ShapeId, Bool)? {
  let members = resource_members(model, structure)
  for member_ in members {
    if member_.trait_value("smithy.api#nestedProperties") is Some(_) {
      return resource_list_element(model, member_).map(id => (id, true))
    }
  }
  if auto_detect {
    let elements : Array[ShapeId] = []
    for member_ in members {
      if resource_list_element(model, member_) is Some(id) {
        elements.push(id)
      }
    }
    if elements.length() == 1 {
      return Some((elements[0], false))
    }
  }
  None
}

///|
fn resource_not_property(model : Model, member_ : Shape) -> Bool {
  member_.trait_value("smithy.api#notProperty") is Some(_) ||
  member_.traits
  .keys()
  .any(id => {
    model.shapes
    .get(id)
    .map(trait_ => {
      trait_.trait_value("smithy.api#trait") is Some(_) &&
      trait_.trait_value("smithy.api#notProperty") is Some(_)
    })
    .unwrap_or(false)
  })
}

///|
fn resource_property_name(member_ : Shape) -> String {
  match member_.trait_value("smithy.api#property") {
    Some(value) =>
      validation_string(value, "name").unwrap_or(resource_member_name(member_))
    None => resource_member_name(member_)
  }
}

///|
fn Model::resource_property_member_events(
  self : Model,
  resource : Shape,
  structure : ShapeId,
  identifiers : Map[String, String],
  binding : String,
  element : Bool,
  strict : Bool,
  used : Map[String, Bool],
) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  guard resource.body is Resource(properties~, ..) else { return events }
  let mappings : Map[String, Array[String]] = Map([])
  let severity = if strict { "ERROR" } else { "WARNING" }
  for member_ in resource_members(self, structure) {
    guard member_.body is Member(target~) else { continue }
    let name = resource_member_name(member_)
    let identifier = identifiers.values().any(value => value == name)
    let excluded = resource_not_property(self, member_)
    let explicitly_named = match member_.trait_value("smithy.api#property") {
      Some(value) => validation_string(value, "name") is Some(_)
      None => false
    }
    let is_property = if element {
      !identifier &&
      (explicitly_named || (!excluded && properties.contains(name)))
    } else {
      (!identifier && !excluded) || properties.contains(name)
    }
    if !is_property {
      if element && strict && !identifier && !excluded {
        events.push(
          validation_event(
            "ResourceOperationInputOutput",
            "ERROR",
            Some(member_),
            "Member `" +
            name +
            "` does not target a property or identifier for resource `" +
            resource.id.to_string() +
            "`. If it is an identifier, apply the `smithy.api#resourceIdentifier` trait. If it is a property, apply the `smithy.api#property` trait. If it is neither, apply the `smithy.api#notProperty` trait.",
          ),
        )
      }
      continue
    }
    let property = resource_property_name(member_)
    used[property] = true
    mappings.get_or_init(property, () => []).push(name)
    match properties.get(property) {
      Some(expected) =>
        if target != expected {
          events.push(
            validation_event(
              "ResourceOperationInputOutput",
              severity,
              Some(member_),
              "This member must target `" +
              expected.to_string() +
              "`. This member is used as part of the `" +
              binding +
              "` operation of the `" +
              resource.id.to_string() +
              "` resource and conflicts with its `" +
              property +
              "` resource property.",
            ),
          )
        }
      None =>
        if element {
          events.push(
            validation_event(
              "ResourceOperationInputOutput",
              severity,
              Some(member_),
              "This member is marked with the `smithy.api#property` trait but resolves to `" +
              property +
              "`, which is not a resource property of the `" +
              resource.id.to_string() +
              "` resource.",
            ),
          )
        } else if !identifier {
          events.push(
            validation_event(
              "ResourceOperationInputOutput",
              "ERROR",
              Some(member_),
              "Member `" +
              name +
              "` does not target a property or identifier for resource `" +
              resource.id.to_string() +
              "`. If it is an identifier, apply the `smithy.api#resourceIdentifier` trait. If it is a property, apply the `smithy.api#property` trait. If it is neither, apply the `smithy.api#notProperty` trait.",
            ),
          )
        }
    }
  }
  for property, members in mappings {
    if members.length() > 1 {
      members.sort()
      events.push(
        validation_event(
          "ResourceOperationInputOutput",
          severity,
          self.shapes.get(structure),
          "This shape contains members with conflicting resource property names that resolve to '" +
          property +
          "': " +
          members.join(", "),
        ),
      )
    }
  }
  events
}

///|
fn Model::resource_property_events(
  self : Model,
  resource : Shape,
) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  if resource.body is Resource(identifiers~, properties~, ..) &&
    !properties.is_empty() {
    let used : Map[String, Bool] = Map([])
    for
      (name, operation_id, collection) in resource_operation_bindings(resource) {
      guard self.shapes.get(operation_id) is Some(operation) &&
        operation.body is Operation(input~, output~, ..) else {
        continue
      }
      // Create is a collection binding whose properties are still top-level.
      if !collection || name == "create" {
        for structure in [input, output] {
          let bindings = resource_bindings(self, identifiers, structure)
          events.append(
            self.resource_property_member_events(
              resource,
              resource_property_shape(self, structure),
              bindings,
              name,
              false,
              true,
              used,
            ),
          )
        }
      } else {
        for
          (structure, auto_detect) in [(input, false), (output, name == "list")] {
          match resource_element_shape(self, structure, auto_detect) {
            Some((element, strict)) => {
              let bindings = resource_bindings(self, identifiers, element)
              events.append(
                self.resource_property_member_events(
                  resource, element, bindings, name, true, strict, used,
                ),
              )
            }
            None =>
              for member_ in resource_members(self, structure) {
                if member_.trait_value("smithy.api#nestedProperties") is Some(_) {
                  events.push(
                    validation_event(
                      "ResourceOperationInputOutput",
                      "WARNING",
                      Some(member_),
                      "The `smithy.api#nestedProperties` trait applied to a member of a collection operation's input or output does not target a list of structures.",
                    ),
                  )
                }
              }
          }
        }
      }
    }
    for property, _ in properties {
      if !used.contains(property) {
        events.push(
          validation_event(
            "ResourceOperationInputOutput",
            "ERROR",
            Some(resource),
            "Resource property `" +
            property +
            "` is not used in the input or output of create, an instance operation, or the list element of a collection operation.",
          ),
        )
      }
    }
  }
  events
}

///|
priv struct ResourcePathValue {
  leaf : ShapeId
  arrays : Array[ShapeId]
}

///|
fn resource_path_normalize(model : Model, id : ShapeId) -> ResourcePathValue {
  let mut leaf = id
  let arrays : Array[ShapeId] = []
  let seen : Map[ShapeId, Bool] = Map([])
  while model.shapes.get(leaf) is Some(shape) && shape.body is List(element) {
    if seen.contains(leaf) {
      break
    }
    seen[leaf] = true
    guard model.shapes.get(element) is Some(element) &&
      element.body is Member(target~) else {
      break
    }
    arrays.push(leaf)
    leaf = target
  }
  { leaf, arrays, }
}

///|
fn resource_path_unsupported(node : @jmespath.Node) -> Array[String] {
  match node.kind {
    Identity | Field(_) => []
    Subexpression(left, right)
    | Projection(left, right)
    | ObjectProjection(left, right) => {
      let unsupported = resource_path_unsupported(left)
      unsupported.append(resource_path_unsupported(right))
      unsupported
    }
    Flatten(inner) | ExpressionRef(inner) => resource_path_unsupported(inner)
    Function(name, _) => ["`" + name + "` function"]
    Index(_, _) => ["index"]
    Slice(_, _, _, _) => ["slice"]
    Literal(_) => ["literal"]
    MultiList(_) => ["multiselect list"]
    MultiHash(_) => ["multiselect hash"]
    Binary(operator, _, _) =>
      [
        match operator {
          And => "and"
          Or => "or"
          Pipe => "pipe"
          _ => "comparator"
        },
      ]
    Not(_) => ["not"]
    Filter(_, _, _) => ["filter projection"]
  }
}

///|
fn resource_path_walk(
  model : Model,
  node : @jmespath.Node,
  start : ResourcePathValue,
) -> Result[ResourcePathValue, String] {
  match node.kind {
    Identity => Ok(start)
    Field(name) => {
      if !start.arrays.is_empty() {
        return Err("cannot access field `" + name + "` on an array")
      }
      guard model.shapes.get(start.leaf) is Some(shape) else {
        return Err("unknown shape `" + start.leaf.to_string() + "`")
      }
      match shape.body {
        Structure(_) | Union(_) => {
          for member_ in resource_members(model, shape.id) {
            if resource_member_name(member_) == name &&
              member_.body is Member(target~) {
              return Ok(resource_path_normalize(model, target))
            }
          }
          Err("no member `" + name + "` on `" + start.leaf.to_string() + "`")
        }
        Map(value~, ..) =>
          if model.shapes.get(value) is Some(member_) &&
            member_.body is Member(target~) {
            Ok(resource_path_normalize(model, target))
          } else {
            Err("unknown map value")
          }
        _ =>
          Err(
            "cannot access field `" +
            name +
            "` on `" +
            start.leaf.to_string() +
            "`",
          )
      }
    }
    Subexpression(left, right) =>
      match resource_path_walk(model, left, start) {
        Err(error) => Err(error)
        Ok(left) => resource_path_walk(model, right, left)
      }
    Projection(left, right) =>
      match resource_path_walk(model, left, start) {
        Err(error) => Err(error)
        Ok(left) => {
          if left.arrays.is_empty() {
            return Err("projection `[*]` applied to a non-array")
          }
          let inner = { leaf: left.leaf, arrays: left.arrays[1:].to_owned(), }
          match resource_path_walk(model, right, inner) {
            Err(error) => Err(error)
            Ok(right) => {
              let arrays = [left.arrays[0]]
              arrays.append(right.arrays)
              Ok({ leaf: right.leaf, arrays, })
            }
          }
        }
      }
    Flatten(inner) =>
      match resource_path_walk(model, inner, start) {
        Err(error) => Err(error)
        Ok(inner) => {
          if inner.arrays.is_empty() {
            return Err("flatten `[]` applied to a non-array")
          }
          let arrays = [inner.arrays[0]]
          if inner.arrays.length() > 2 {
            arrays.append(inner.arrays[2:].to_owned())
          }
          Ok({ leaf: inner.leaf, arrays, })
        }
      }
    _ => Err("unsupported expression")
  }
}

///|
fn resource_path_parse(
  path : String,
  kind : String,
  name : String,
  trait_name : String,
  operation : Shape,
  events : Array[ValidationEvent],
) -> @jmespath.Node? {
  let parsed = @jmespath.parse(path) catch {
    @jmespath.JmespathError(diagnostic) => {
      events.push(
        validation_event(
          "ResourceLifecycleTrait",
          "ERROR",
          Some(operation),
          "Invalid JMESPath expression `" +
          path +
          "` for " +
          kind +
          " `" +
          name +
          "` in `@" +
          trait_name +
          "`: " +
          diagnostic.message,
        ),
      )
      return None
    }
  }
  let unsupported = resource_path_unsupported(parsed.root)
  if !unsupported.is_empty() {
    events.push(
      validation_event(
        "ResourceLifecycleTrait",
        "ERROR",
        Some(operation),
        "JMESPath expression `" +
        path +
        "` for " +
        kind +
        " `" +
        name +
        "` in `@" +
        trait_name +
        "` contains unsupported expressions: " +
        unsupported.map(value => "'" + value + "'").join(", "),
      ),
    )
    return None
  }
  Some(parsed.root)
}

///|
fn Model::resource_lifecycle_kind_events(
  self : Model,
  operation : Shape,
  resource : Shape,
  trait_name : String,
  binding : NodeValue,
  kind : String,
  side : ShapeId,
) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  guard resource.body is Resource(identifiers~, properties~, ..) else {
    return events
  }
  let definitions = if kind == "identifier" { identifiers } else { properties }
  let key = if kind == "identifier" { "identifiers" } else { "properties" }
  let explicit = match validation_field(binding, key) {
    Some(Object(fields)) => fields
    _ => Map([])
  }
  let signatures : Map[String, Array[ShapeId]] = Map([])
  for name, locator in explicit {
    guard validation_string(locator, "path") is Some(path) else { continue }
    guard definitions.get(name) is Some(target) else {
      let capitalized = if kind == "identifier" {
        "Identifier"
      } else {
        "Property"
      }
      events.push(
        validation_event(
          "ResourceLifecycleTrait",
          "ERROR",
          Some(operation),
          capitalized +
          " `" +
          name +
          "` in `@" +
          trait_name +
          "` does not match any " +
          kind +
          " of resource `" +
          resource.id.to_string() +
          "`. Valid " +
          kind +
          "s: [" +
          resource_ticked_list(definitions.keys().to_array()) +
          "]",
        ),
      )
      continue
    }
    guard resource_path_parse(path, kind, name, trait_name, operation, events)
      is Some(parsed) else {
      continue
    }
    match resource_path_walk(self, parsed, { leaf: side, arrays: [], }) {
      Err(error) =>
        events.push(
          validation_event(
            "ResourceLifecycleTrait",
            "DANGER",
            Some(operation),
            "JMESPath expression `" +
            path +
            "` for " +
            kind +
            " `" +
            name +
            "` in `@" +
            trait_name +
            "` has problems when resolved against `" +
            side.to_string() +
            "`: " +
            error,
          ),
        )
      Ok(result) => {
        if !result.arrays.is_empty() {
          signatures[name] = result.arrays
        }
        let declared = resource_path_normalize(self, target).leaf
        if result.leaf != declared {
          events.push(
            validation_event(
              "ResourceLifecycleTrait",
              "ERROR",
              Some(operation),
              "JMESPath expression `" +
              path +
              "` for " +
              kind +
              " `" +
              name +
              "` in `@" +
              trait_name +
              "` resolves to `" +
              result.leaf.to_string() +
              "`, but the resource " +
              kind +
              " targets `" +
              declared.to_string() +
              "`.",
            ),
          )
        }
      }
    }
  }
  if kind == "identifier" && signatures.length() > 1 {
    let values = signatures.values().to_array()
    if values.iter().any(value => value != values[0]) {
      let details : Array[String] = []
      for name, arrays in signatures {
        details.push(
          "`" +
          name +
          "` -> [" +
          arrays.map(id => "`" + id.to_string() + "`").join(", ") +
          "]",
        )
      }
      events.push(
        validation_event(
          "ResourceLifecycleTrait",
          "ERROR",
          Some(operation),
          "Identifiers in `@" +
          trait_name +
          "` for resource `" +
          resource.id.to_string() +
          "` project through different lists, making cardinality ambiguous. Identifiers and the lists they iterate: " +
          details.join(", "),
        ),
      )
    }
  }
  if validation_string(binding, key + "From") is Some(path) &&
    resource_path_parse(
      path,
      key + "From",
      key + "From",
      trait_name,
      operation,
      events,
    )
    is Some(parsed) {
    let prefix = "`" + key + "From` `" + path + "` in `@" + trait_name + "`"
    match resource_path_walk(self, parsed, { leaf: side, arrays: [], }) {
      Err(error) =>
        events.push(
          validation_event(
            "ResourceLifecycleTrait",
            "DANGER",
            Some(operation),
            prefix +
            " has problems when resolved against `" +
            side.to_string() +
            "`: " +
            error,
          ),
        )
      Ok(result) =>
        if parsed.kind is Identity {
          let side_name = if operation.body is Operation(output~, ..) &&
            output == side {
            "output"
          } else {
            "input"
          }
          events.push(
            validation_event(
              "ResourceLifecycleTrait",
              "ERROR",
              Some(operation),
              prefix +
              " points at the whole " +
              side_name +
              " root; it must point at a nested structure or a projection.",
            ),
          )
        } else if self.shapes.get(result.leaf) is Some(shape) &&
          shape.body is Structure(_) {
          let matched : Map[String, Shape] = Map([])
          let unmatched : Array[String] = []
          for member_ in resource_members(self, result.leaf) {
            if member_.trait_value("smithy.api#notProperty") is Some(_) {
              continue
            }
            let name = if kind == "identifier" {
              match member_.trait_value("smithy.api#resourceIdentifier") {
                Some(String(name)) => name
                _ => resource_member_name(member_)
              }
            } else {
              resource_property_name(member_)
            }
            if definitions.contains(name) {
              matched[name] = member_
            } else {
              unmatched.push(resource_member_name(member_))
            }
          }
          if matched.is_empty() {
            events.push(
              validation_event(
                "ResourceLifecycleTrait",
                "ERROR",
                Some(operation),
                prefix +
                " resolves to `" +
                result.leaf.to_string() +
                "`, whose members match no " +
                kind +
                " of resource `" +
                resource.id.to_string() +
                "`.",
              ),
            )
          } else {
            if !unmatched.is_empty() {
              events.push(
                validation_event(
                  "ResourceLifecycleTrait",
                  "ERROR",
                  Some(operation),
                  prefix +
                  " resolves to `" +
                  result.leaf.to_string() +
                  "`, which has members that are neither a " +
                  kind +
                  " nor an identifier of resource `" +
                  resource.id.to_string() +
                  "` and are not marked `@notProperty`: [" +
                  unmatched.map(name => "`" + name + "`").join(", ") +
                  "]",
                ),
              )
            }
            for name, member_ in matched {
              if member_.body is Member(target~) &&
                definitions.get(name) is Some(declared) {
                let leaf = resource_path_normalize(self, target).leaf
                let declared = resource_path_normalize(self, declared).leaf
                if leaf != declared {
                  events.push(
                    validation_event(
                      "ResourceLifecycleTrait",
                      "ERROR",
                      Some(operation),
                      "Inferred " +
                      kind +
                      " `" +
                      name +
                      "` in `@" +
                      trait_name +
                      "` (member `" +
                      resource_member_name(member_) +
                      "`) resolves to `" +
                      leaf.to_string() +
                      "`, but the resource " +
                      kind +
                      " targets `" +
                      declared.to_string() +
                      "`.",
                    ),
                  )
                }
              }
            }
          }
        } else {
          events.push(
            validation_event(
              "ResourceLifecycleTrait",
              "ERROR",
              Some(operation),
              prefix +
              " must point at a nested structure or a projection of structures, but resolved to `" +
              result.leaf.to_string() +
              "`.",
            ),
          )
        }
    }
  }
  events
}

///|
fn Model::resource_lifecycle_trait_events(
  self : Model,
) -> Array[ValidationEvent] {
  let events : Array[ValidationEvent] = []
  for _, operation in self.shapes {
    guard operation.body is Operation(input~, output~, ..) else { continue }
    for
      (trait_name, lifecycle) in [
        ("createsResources", "create"),
        ("putsResources", "put"),
        ("readsResources", "read"),
        ("updatesResources", "update"),
        ("deletesResources", "delete"),
      ] {
      for
        binding in validation_array(
          operation.trait_value("smithy.api#" + trait_name),
        ) {
        guard validation_string(binding, "resource") is Some(resource_id) &&
          self.get(resource_id) is Some(resource) &&
          resource.body is Resource(..) else {
          continue
        }
        if resource_is_lifecycle(resource, lifecycle, operation.id) {
          events.push(
            validation_event(
              "ResourceLifecycleTrait",
              "WARNING",
              Some(operation),
              "Resource `" +
              resource_id +
              "` in `@" +
              trait_name +
              "` is already bound to this operation via the resource's lifecycle. The trait is redundant.",
            ),
          )
        }
        let identifier_side = if lifecycle == "create" { output } else { input }
        events.append(
          self.resource_lifecycle_kind_events(
            operation, resource, trait_name, binding, "identifier", identifier_side,
          ),
        )
        if lifecycle != "delete" {
          let property_side = if lifecycle == "read" { output } else { input }
          events.append(
            self.resource_lifecycle_kind_events(
              operation, resource, trait_name, binding, "property", property_side,
            ),
          )
        }
      }
    }
  }
  events
}