rustqual 1.2.5

Comprehensive Rust code quality analyzer — seven dimensions: IOSP, Complexity, DRY, SRP, Coupling, Test Quality, Architecture
Documentation
//! Trait-method anchor index for the call-parity call graph.
//!
//! Builds and queries `AnchorInfo` per `<Trait>::<method>` synthetic
//! node emitted by `dyn Trait.method()` dispatch. The unified target-
//! capability rule (`is_anchor_target_capability`) is the single
//! source of truth shared by the boundary walker and the Check B/D
//! capability enumeration — without that sharing, the two sides drift
//! (parallel-path inconsistency).

use super::type_infer::{MethodLocation, WorkspaceTypeIndex};
use crate::adapters::analyzers::architecture::layer_rule::LayerDefinitions;
use std::collections::HashSet;

/// One trait-method anchor's resolved metadata. Built at graph-build
/// time; consumed by the unified `is_anchor_target_capability` rule
/// (walker + `target_anchor_capabilities`) and by anchor-finding
/// rendering.
#[derive(Debug, Clone)]
pub(crate) struct AnchorInfo {
    /// Set of layer names where overriding impls of this trait method
    /// live. Empty for default-only traits.
    pub(crate) impl_layers: HashSet<String>,
    /// Canonical names of the overriding impl methods (`<Impl>::<method>`).
    /// Used by Check B/D to skip those concrete pub-fn entries — the
    /// anchor takes capability responsibility.
    pub(crate) impl_method_canonicals: HashSet<String>,
    /// Layer where the trait declaration itself lives. Denormalised
    /// (could be re-resolved via `LayerDefinitions::layer_of_crate_path`
    /// from the anchor key) so the unified target-capability rule
    /// stays a single predicate-only call on the hot path.
    pub(crate) decl_layer: Option<String>,
    /// True iff the trait method declares a default body. Methods
    /// without a default body and without an overriding impl are
    /// uncallable signatures and do NOT count as a target capability
    /// just because the trait happens to live in the target layer.
    pub(crate) has_default_body: bool,
    /// Whether the trait is workspace-visible per the shared
    /// `visible_canonicals` set (same set `pub_fns` consumes).
    pub(crate) trait_visible: bool,
    /// Source location of the trait method declaration. `None` when
    /// the trait was registered without a captured span (synthetic
    /// test fixtures).
    pub(crate) location: Option<MethodLocation>,
}

/// Single source of truth for "is this anchor a target capability?"
/// — consulted by the boundary walker, Check B/D capability
/// enumeration, AND the reachable-targets BFS, so the three never
/// drift apart.
///
/// Passes iff trait is visible, declaring layer is not a peer
/// adapter, AND (decl_layer == target with a callable body) OR an
/// overriding impl lives in target_layer.
pub(crate) fn is_anchor_target_capability(
    info: &AnchorInfo,
    target_layer: &str,
    adapter_layers: &[String],
) -> bool {
    if !info.trait_visible {
        return false;
    }
    if let Some(decl) = info.decl_layer.as_deref() {
        if decl != target_layer && adapter_layers.iter().any(|a| a == decl) {
            return false;
        }
        if decl == target_layer
            && (info.has_default_body || info.impl_layers.contains(target_layer))
        {
            return true;
        }
    }
    info.impl_layers.contains(target_layer)
}

/// Per-trait inputs that don't change across the trait's methods.
/// Bundles the canonical, declaring layer (resolved once via
/// `LayerDefinitions::layer_of_crate_path`), and the workspace-
/// visibility flag (sourced from the same `visible_canonicals` set
/// pub-fns uses) so `build_anchor_info` stays under the SRP param
/// budget while the per-method anchor build remains a pure projection
/// over the type index.
pub(crate) struct TraitAnchorMeta<'a> {
    pub canonical: &'a str,
    pub decl_layer: &'a Option<String>,
    pub visible: bool,
}

/// Construct one anchor's `AnchorInfo` from the workspace type index
/// plus per-trait metadata. `trait_visible` is sourced from the
/// workspace-wide `visible_canonicals` set (the same set
/// `pub_fns::collect_pub_fns_by_layer` uses) so anchor visibility
/// agrees with the rest of call-parity. Operation: per-method
/// assembly.
pub(crate) fn build_anchor_info(
    type_index: &WorkspaceTypeIndex,
    layers: &LayerDefinitions,
    trait_meta: &TraitAnchorMeta<'_>,
    method: &str,
) -> AnchorInfo {
    // Strict: only overriding impls contribute layers and method
    // canonicals. Inherited defaults aren't included — the default
    // body lives on the trait (not the impl's layer), and fabricating
    // `<Impl>::<method>` would collide with unrelated inherent methods
    // of the same name.
    let overriding = type_index.overriding_impls_for(trait_meta.canonical, method);
    let impl_layers: HashSet<String> = overriding
        .iter()
        .filter_map(|impl_canon| layers.layer_of_crate_path(impl_canon).map(String::from))
        .collect();
    let impl_method_canonicals: HashSet<String> = overriding
        .iter()
        .map(|impl_canon| format!("{impl_canon}::{method}"))
        .collect();
    let location = type_index
        .trait_method_location(trait_meta.canonical, method)
        .cloned();
    let has_default_body = type_index.trait_method_has_default_body(trait_meta.canonical, method);
    AnchorInfo {
        impl_layers,
        impl_method_canonicals,
        decl_layer: trait_meta.decl_layer.clone(),
        has_default_body,
        trait_visible: trait_meta.visible,
        location,
    }
}