tamer: asg::air: Merge Pkg closing span

The `Pkg` span will now properly reflect the entire definition of the
package including the opening and closing tags.

This was found while I was working on a graph traversal.

DEV-13597
main
Mike Gerwitz 2023-02-07 12:19:27 -05:00
parent 39e98210be
commit 4afc8c22e6
3 changed files with 40 additions and 14 deletions

View File

@ -501,7 +501,10 @@ impl ParseState for AirAggregate {
.err(AsgError::NestedPkgOpen(span, oi_pkg.span()))
}
(PkgDfn(_, es), PkgClose(_)) => Transition(Empty(es)).incomplete(),
(PkgDfn(oi_pkg, es), PkgClose(span)) => {
oi_pkg.close(asg, span);
Transition(Empty(es)).incomplete()
}
(st @ (Empty(..) | BuildingExpr(..)), PkgClose(span)) => {
Transition(st).err(AsgError::InvalidPkgCloseContext(span))

View File

@ -263,7 +263,7 @@ fn pkg_is_rooted() {
.expect("missing rooted package")
.resolve(&asg);
assert_eq!(pkg.span(), S1);
assert_eq!(pkg.span(), S1.merge(S2).unwrap());
}
#[test]
@ -1075,38 +1075,48 @@ fn expr_ref_outside_of_expr_context() {
#[test]
fn idents_share_defining_pkg() {
let id_foo = SPair("foo".into(), S2);
let id_bar = SPair("bar".into(), S4);
let id_baz = SPair("baz".into(), S5);
let id_foo = SPair("foo".into(), S3);
let id_bar = SPair("bar".into(), S5);
let id_baz = SPair("baz".into(), S6);
// An expression nested within another.
let toks = vec![
Air::ExprOpen(ExprOp::Sum, S1),
Air::PkgOpen(S1),
Air::ExprOpen(ExprOp::Sum, S2),
Air::ExprIdent(id_foo),
Air::ExprOpen(ExprOp::Sum, S3),
Air::ExprOpen(ExprOp::Sum, S4),
Air::ExprIdent(id_bar),
Air::ExprRef(id_baz),
Air::ExprClose(S6),
Air::ExprClose(S7),
Air::ExprClose(S8),
Air::PkgClose(S9),
];
let asg = asg_from_toks(toks);
let mut sut = Sut::parse(toks.into_iter());
assert!(sut.all(|x| x.is_ok()));
let asg = sut.finalize().unwrap().into_context();
let oi_foo = asg.lookup(id_foo).unwrap();
let oi_bar = asg.lookup(id_bar).unwrap();
assert_eq!(oi_foo.src_pkg(&asg).unwrap(), oi_bar.src_pkg(&asg).unwrap());
// Missing identifiers should not have a source package,
// since we don't know what defined it yet.
let oi_baz = asg.lookup(id_baz).unwrap();
assert_eq!(None, oi_baz.src_pkg(&asg));
// Ontological sanity check:
// edges from the package to identifiers defined by it should not be
// considered cross edges.
let oi_pkg = oi_foo.src_pkg(&asg).unwrap();
assert!(oi_pkg.edges(&asg).all(|rel| !rel.is_cross_edge()));
// Missing identifiers should not have a source package,
// since we don't know what defined it yet.
let oi_baz = asg.lookup(id_baz).unwrap();
assert_eq!(None, oi_baz.src_pkg(&asg));
// The package span should encompass the entire definition.
assert_eq!(
S1.merge(S9),
oi_foo.src_pkg(&asg).map(|pkg| pkg.resolve(&asg).span())
)
}
fn asg_from_toks<I: IntoIterator<Item = Air>>(toks: I) -> Asg

View File

@ -23,7 +23,7 @@ use super::{
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
ObjectRelatable,
};
use crate::{asg::Asg, span::Span};
use crate::{asg::Asg, f::Functor, span::Span};
use std::fmt::Display;
#[cfg(doc)]
@ -50,6 +50,14 @@ impl Display for Pkg {
}
}
impl Functor<Span> for Pkg {
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
match self {
Self(span) => Self(f(span)),
}
}
}
/// Subset of [`ObjectKind`]s that are valid targets for edges from
/// [`Ident`].
///
@ -107,4 +115,9 @@ impl ObjectIndex<Pkg> {
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self {
self.add_edge_to(asg, oi)
}
/// Complete the definition of a package.
pub fn close(self, asg: &mut Asg, span: Span) -> Self {
self.map_obj(asg, Pkg::fmap(|open| open.merge(span).unwrap_or(open)))
}
}