// Relationship between objects represented on ASG //
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
//
// This file is part of TAME.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! Relationship betwen objects on the ASG.
//!
//! See (parent module)[super] for more information.
use super::{
Expr, Ident, Object, ObjectIndex, ObjectKind, OiPairObjectInner, Pkg, Root,
};
use crate::{asg::Asg, f::Functor, span::Span};
use std::fmt::Display;
/// Object types corresponding to variants in [`Object`].
///
/// These are used as small tags for [`ObjectRelatable`].
/// Rust unfortunately makes working with its internal tags difficult,
/// despite their efforts with [`std::mem::Discriminant`],
/// which requires a _value_ to produce.
///
/// TODO: Encapsulate within `crate::asg` when the graph can be better
/// encapsulated.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ObjectRelTy {
Root,
Pkg,
Ident,
Expr,
}
impl Display for ObjectRelTy {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// At the time of writing,
// this happens to be sufficient.
std::fmt::Debug::fmt(self, f)
}
}
/// A dynamic relationship (edge) from one object to another before it has
/// been narrowed.
///
/// The target of this edge is usually an [`ObjectIndex`],
/// but it is made generic (`T`) to support mapping while retaining useful
/// metadata,
/// e.g. to resolve an object while retaining the edge information.
#[derive(Debug, PartialEq)]
pub struct DynObjectRel>(
(ObjectRelTy, ObjectRelTy),
T,
Option,
);
impl DynObjectRel {
pub(in super::super) fn new(
from_ty: ObjectRelTy,
to_ty: ObjectRelTy,
x: T,
ctx_span: Option,
) -> Self {
Self((from_ty, to_ty), x, ctx_span)
}
/// The type of the source edge.
pub fn source_ty(&self) -> ObjectRelTy {
match self {
Self((ty, _), ..) => *ty,
}
}
/// The type of the target edge.
pub fn target_ty(&self) -> ObjectRelTy {
match self {
Self((_, ty), ..) => *ty,
}
}
/// The target of this relationship.
///
/// This type generally originates as [`ObjectIndex`] but can be mapped
/// over to retain the structured edge data.
pub fn target(&self) -> &T {
match self {
Self(_, oi, _) => oi,
}
}
/// A [`Span`] associated with the _relationship_ between the source and
/// target objects,
/// if any.
pub fn ctx_span(&self) -> Option {
match self {
Self(_, _, ctx_span) => *ctx_span,
}
}
}
impl DynObjectRel> {
/// See [`ObjectIndex::must_narrow_into`].
pub fn must_narrow_into(&self) -> ObjectIndex {
match self {
Self(_, oi, _) => oi.must_narrow_into(),
}
}
/// Attempt to narrow into the [`ObjectRel`] of `O`.
///
/// See [`ObjectRelatable::new_rel_dyn`] for more information.
pub fn narrow(&self) -> Option {
O::new_rel_dyn(self.target_ty(), *self.target())
}
/// Pair the inner [`ObjectIndex`] with its resolved [`Object`].
///
/// This allows the [`ObjectIndex`] to be refined alongside the inner
/// [`ObjectKind`] so that callers can make use of the refined
/// [`ObjectIndex`] without having to explicitly narrow themselves.
/// While isn't any more or less safe than the manual alternative,
/// it _does_ defend against logic bugs.
pub fn resolve_oi_pair(
self,
asg: &Asg,
) -> DynObjectRel