[DEV-7084] TAMER: Finalize AsgBuilder
parent
32abc7dce2
commit
9893d56775
|
@ -32,24 +32,94 @@ use std::hash::BuildHasher;
|
|||
pub type Result<'i, S, Ix> =
|
||||
std::result::Result<AsgBuilderState<'i, S, Ix>, AsgBuilderError<Ix>>;
|
||||
|
||||
/// Builder state between imports.
|
||||
///
|
||||
/// [`AsgBuilder::import_xmlo`] is designed to return its state after having
|
||||
/// been invoked,
|
||||
/// which contains in part a set of relative package import paths that
|
||||
/// were discovered during processing (`found`).
|
||||
/// This state can then be fed back to `import_xmlo` when recursively
|
||||
/// importing those packages.
|
||||
///
|
||||
/// The values [`name`](AsgBuilderState::name) and
|
||||
/// [`relroot`](AsgBuilderState::relroot) are set only once when the first
|
||||
/// package is processed.
|
||||
/// A package is considered to be the first if `name` is [`None`].
|
||||
///
|
||||
/// [`roots`](AsgBuilderState::roots) is added to as certain identifiers are
|
||||
/// discovered that should be used as starting points for a topological
|
||||
/// sort.
|
||||
/// This is used by the linker to only include dependencies that are
|
||||
/// actually used by a particular program.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AsgBuilderState<'i, S, Ix>
|
||||
where
|
||||
S: BuildHasher,
|
||||
Ix: IndexType,
|
||||
{
|
||||
/// Discovered roots.
|
||||
///
|
||||
/// Roots represent starting points for a topological sort of the
|
||||
/// graph.
|
||||
/// They are meaningful to the linker.
|
||||
pub roots: Vec<ObjectRef<Ix>>,
|
||||
|
||||
/// Relative paths to imported packages that have been discovered.
|
||||
///
|
||||
/// The caller will use these to perform recursive loads.
|
||||
/// This is contained within an [`Option`] so that the caller can `take`
|
||||
/// ownership over its contents.
|
||||
///
|
||||
/// See [`AsgBuilder::import_xmlo`] for behavior when this value is
|
||||
/// [`None`].
|
||||
pub found: Option<HashSet<&'i str, S>>,
|
||||
|
||||
/// Program name once discovered.
|
||||
///
|
||||
/// This will be set by the first package encountered.
|
||||
pub name: Option<&'i Symbol<'i>>,
|
||||
|
||||
/// Relative path to project root once discovered.
|
||||
///
|
||||
/// This will be set by the first package encountered.
|
||||
pub relroot: Option<String>,
|
||||
}
|
||||
|
||||
impl<'i, S, Ix> AsgBuilderState<'i, S, Ix>
|
||||
where
|
||||
S: BuildHasher,
|
||||
Ix: IndexType,
|
||||
{
|
||||
/// Whether this is the first discovered package.
|
||||
///
|
||||
/// This is true if [`name`](AsgBuilderState::name) is [`None`].
|
||||
fn is_first(&self) -> bool {
|
||||
self.name.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Populate [`Asg`] with data derived from `xmlo` files.
|
||||
///
|
||||
/// This accepts anything capable of iterating over [`XmloResult`].
|
||||
///
|
||||
/// For more information on what data are processed,
|
||||
/// see [`AsgBuilderState`].
|
||||
pub trait AsgBuilder<'i, O, S, Ix>
|
||||
where
|
||||
O: IdentObjectState<'i, O>,
|
||||
S: BuildHasher,
|
||||
Ix: IndexType,
|
||||
{
|
||||
/// Import [`XmloResult`]s into an [`Asg`].
|
||||
///
|
||||
/// This is an IR lowering operation.
|
||||
/// The [`XmloResult`] produces data gleaned from
|
||||
/// [`legacyir`](crate::ir::legacyir),
|
||||
/// and this process lowers it into the IR [`asg`](crate::ir::asg).
|
||||
///
|
||||
/// Each call to this method augments the provided [`AsgBuilderState`];
|
||||
/// see its documentation for more information.
|
||||
/// Its initial value can be provided as [`Default::default`].
|
||||
fn import_xmlo(
|
||||
&mut self,
|
||||
xmlo: impl Iterator<Item = XmloResult<XmloEvent<'i>>>,
|
||||
|
@ -70,11 +140,11 @@ where
|
|||
mut state: AsgBuilderState<'i, S, Ix>,
|
||||
) -> Result<'i, S, Ix> {
|
||||
let mut elig = None;
|
||||
let first = state.name.is_none();
|
||||
let first = state.is_first();
|
||||
let found = state.found.get_or_insert(Default::default());
|
||||
|
||||
loop {
|
||||
match xmlo.next().unwrap()? {
|
||||
while let Some(ev) = xmlo.next() {
|
||||
match ev? {
|
||||
XmloEvent::Package(attrs) => {
|
||||
if first {
|
||||
state.name = attrs.name;
|
||||
|
@ -86,7 +156,10 @@ where
|
|||
|
||||
XmloEvent::SymDeps(sym, deps) => {
|
||||
// Maps should not pull in symbols since we may end up
|
||||
// mapping to params that are never actually used
|
||||
// mapping to params that are never actually used.
|
||||
// TODO: Can these just be removed from the xmlo files
|
||||
// rather than adding exceptions?
|
||||
// TODO: No string comparison.
|
||||
if !sym.starts_with(":map:") {
|
||||
for dep_sym in deps {
|
||||
self.add_dep_lookup(sym, dep_sym);
|
||||
|
@ -98,7 +171,6 @@ where
|
|||
if let Some(sym_src) = attrs.src {
|
||||
found.insert(sym_src);
|
||||
} else {
|
||||
let owned = attrs.src.is_none();
|
||||
let extern_ = attrs.extern_;
|
||||
let kindval = (&attrs).try_into()?;
|
||||
|
||||
|
@ -110,13 +182,12 @@ where
|
|||
src.pkg_name = None;
|
||||
}
|
||||
|
||||
let link_root = owned
|
||||
&& matches!(
|
||||
kindval,
|
||||
IdentKind::Meta
|
||||
| IdentKind::Map
|
||||
| IdentKind::RetMap
|
||||
);
|
||||
let link_root = matches!(
|
||||
kindval,
|
||||
IdentKind::Meta
|
||||
| IdentKind::Map
|
||||
| IdentKind::RetMap
|
||||
);
|
||||
|
||||
if extern_ {
|
||||
self.declare_extern(sym, kindval, src)?;
|
||||
|
@ -139,7 +210,10 @@ where
|
|||
}
|
||||
|
||||
// We don't need to read any further than the end of the
|
||||
// header (symtable, sym-deps, fragments)
|
||||
// header (symtable, sym-deps, fragments). Note that this
|
||||
// may change in the future, in which case this
|
||||
// responsibility can be delegated to the linker (to produce
|
||||
// an `Iterator` that stops at EOH).
|
||||
XmloEvent::Eoh => break,
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +231,7 @@ where
|
|||
}
|
||||
|
||||
/// Error populating graph with [`XmloResult`]-derived data.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AsgBuilderError<Ix: IndexType> {
|
||||
/// Error with the source `xmlo` file.
|
||||
XmloError(XmloError),
|
||||
|
@ -228,3 +302,515 @@ impl<Ix: IndexType> Error for AsgBuilderError<Ix> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These tests are coupled with BaseAsg, which is not ideal.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::ir::asg::{DefaultAsg, FragmentText, IdentObject};
|
||||
use crate::ir::legacyir::{PackageAttrs, SymAttrs, SymType};
|
||||
use crate::sym::SymbolIndex;
|
||||
use std::collections::hash_map::RandomState;
|
||||
|
||||
type SutIx = u8;
|
||||
type Sut<'i> = DefaultAsg<'i, IdentObject<'i>, SutIx>;
|
||||
type SutState<'i> = AsgBuilderState<'i, RandomState, SutIx>;
|
||||
|
||||
#[test]
|
||||
fn gets_data_from_package_event() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let name = symbol_dummy!(1, "name");
|
||||
let relroot = "some/path".to_string();
|
||||
|
||||
let evs = vec![Ok(XmloEvent::Package(PackageAttrs {
|
||||
name: Some(&name),
|
||||
relroot: Some(relroot.clone()),
|
||||
..Default::default()
|
||||
}))];
|
||||
|
||||
let state = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect("parsing of proper PackageAttrs must succeed");
|
||||
|
||||
assert_eq!(Some(&name), state.name);
|
||||
assert_eq!(Some(relroot), state.relroot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xmlo_error_returned() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let evs = vec![Err(XmloError::UnexpectedRoot)];
|
||||
let result = sut.import_xmlo(evs.into_iter(), SutState::default());
|
||||
|
||||
assert_eq!(
|
||||
AsgBuilderError::XmloError(XmloError::UnexpectedRoot),
|
||||
result.expect_err("expected error to be proxied"),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_elig_as_root() {
|
||||
let mut sut = Sut::new();
|
||||
let elig_sym = symbol_dummy!(1, "elig");
|
||||
|
||||
// The symbol must be on the graph, or it'll fail.
|
||||
let elig_node = sut
|
||||
.declare(&elig_sym, IdentKind::Meta, Default::default())
|
||||
.unwrap();
|
||||
|
||||
let evs = vec![Ok(XmloEvent::Package(PackageAttrs {
|
||||
elig: Some(&elig_sym),
|
||||
..Default::default()
|
||||
}))];
|
||||
|
||||
let state = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.unwrap();
|
||||
|
||||
assert!(state.roots.contains(&elig_node));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_sym_deps() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym_from = symbol_dummy!(1, "from");
|
||||
let sym_to1 = symbol_dummy!(2, "to1");
|
||||
let sym_to2 = symbol_dummy!(3, "to2");
|
||||
|
||||
let evs =
|
||||
vec![Ok(XmloEvent::SymDeps(&sym_from, vec![&sym_to1, &sym_to2]))];
|
||||
|
||||
let _ = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect("unexpected failure");
|
||||
|
||||
let node_from = sut.lookup(&sym_from).expect("from node not added");
|
||||
let node_to1 = sut.lookup(&sym_to1).expect("to1 node not added");
|
||||
let node_to2 = sut.lookup(&sym_to2).expect("to2 node not added");
|
||||
|
||||
assert!(sut.has_dep(node_from, node_to1));
|
||||
assert!(sut.has_dep(node_from, node_to2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_map_sym_deps() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym_from = symbol_dummy!(1, ":map:sym");
|
||||
let sym_to = symbol_dummy!(2, "to");
|
||||
|
||||
let evs = vec![Ok(XmloEvent::SymDeps(&sym_from, vec![&sym_to]))];
|
||||
|
||||
let _ = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect("unexpected failure");
|
||||
|
||||
assert!(sut.lookup(&sym_from).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sym_decl_with_src_not_added_and_populates_found() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
let src_a = symbol_dummy!(2, "src_a");
|
||||
let src_b = symbol_dummy!(3, "src_b");
|
||||
|
||||
let evs = vec![
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
src: Some(&src_a),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
src: Some(&src_b),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
];
|
||||
|
||||
let mut founds = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect("unexpected failure")
|
||||
.found
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|s| *s)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Just to remove nondeterminism in case the iteration order happens
|
||||
// to change (we're using RandomState).
|
||||
founds.sort();
|
||||
|
||||
assert_eq!(vec![&src_a as &str, &src_b as &str], founds);
|
||||
|
||||
// Symbols with `src` set are external and should not be added to
|
||||
// the graph.
|
||||
assert!(sut.lookup(&sym).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sym_decl_added_to_graph() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym_extern = symbol_dummy!(1, "sym_extern");
|
||||
let sym_non_extern = symbol_dummy!(2, "sym_non_extern");
|
||||
let sym_map = symbol_dummy!(3, "sym_map");
|
||||
let sym_retmap = symbol_dummy!(4, "sym_retmap");
|
||||
let pkg_name = symbol_dummy!(5, "pkg name");
|
||||
|
||||
let evs = vec![
|
||||
// Note that externs should not be recognized as roots even if
|
||||
// their type would be.
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym_extern,
|
||||
SymAttrs {
|
||||
pkg_name: Some(&pkg_name),
|
||||
extern_: true,
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
// These three will be roots
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym_non_extern,
|
||||
SymAttrs {
|
||||
pkg_name: Some(&pkg_name),
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym_map,
|
||||
SymAttrs {
|
||||
pkg_name: Some(&pkg_name),
|
||||
ty: Some(SymType::Map),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym_retmap,
|
||||
SymAttrs {
|
||||
pkg_name: Some(&pkg_name),
|
||||
ty: Some(SymType::RetMap),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
];
|
||||
|
||||
let state = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.unwrap();
|
||||
|
||||
// Both above symbols were local (no `src`).
|
||||
assert!(state.found.unwrap().len() == 0);
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
sut.lookup(&sym_non_extern).unwrap(),
|
||||
sut.lookup(&sym_map).unwrap(),
|
||||
sut.lookup(&sym_retmap).unwrap(),
|
||||
],
|
||||
state.roots
|
||||
);
|
||||
|
||||
// Note that each of these will have their package names cleared
|
||||
// since this is considered to be the first package encountered.
|
||||
|
||||
assert_eq!(
|
||||
&IdentObject::Extern(
|
||||
&sym_extern,
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
sut.get(sut.lookup(&sym_extern).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&IdentObject::Ident(
|
||||
&sym_non_extern,
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
sut.get(sut.lookup(&sym_non_extern).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&IdentObject::Ident(
|
||||
&sym_map,
|
||||
IdentKind::Map,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
sut.get(sut.lookup(&sym_map).unwrap()).unwrap(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
&IdentObject::Ident(
|
||||
&sym_retmap,
|
||||
IdentKind::RetMap,
|
||||
Source {
|
||||
pkg_name: None,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
sut.get(sut.lookup(&sym_retmap).unwrap()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
// See above test, where pkg_name was cleared.
|
||||
#[test]
|
||||
fn sym_decl_pkg_name_retained_if_not_first() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
let pkg_name = symbol_dummy!(2, "pkg name");
|
||||
|
||||
// This is all that's needed to not consider this to be the first
|
||||
// package, so that pkg_name is retained below.
|
||||
let state = AsgBuilderState::<'_, RandomState, SutIx> {
|
||||
name: Some(&pkg_name),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let evs = vec![Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
pkg_name: Some(&pkg_name),
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
))];
|
||||
|
||||
let _ = sut.import_xmlo(evs.into_iter(), state).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
// `pkg_name` retained
|
||||
&IdentObject::Ident(
|
||||
&sym,
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
pkg_name: Some(&pkg_name),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
sut.get(sut.lookup(&sym).unwrap()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ident_kind_conversion_error_propagates() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
let bad_attrs = SymAttrs::default();
|
||||
|
||||
let evs = vec![Ok(XmloEvent::SymDecl(&sym, bad_attrs))];
|
||||
|
||||
let result = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect_err("expected IdentKind conversion error");
|
||||
|
||||
assert!(matches!(result, AsgBuilderError::IdentKindError(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_extern_error_propagates() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
|
||||
let evs = vec![
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
extern_: true,
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
// Incompatible
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
extern_: true,
|
||||
ty: Some(SymType::Map),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
];
|
||||
|
||||
let result = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect_err("expected extern error");
|
||||
|
||||
assert!(matches!(result, AsgBuilderError::AsgError(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_error_propagates() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
|
||||
let evs = vec![
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
// Redeclare
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
];
|
||||
|
||||
let result = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect_err("expected declare error");
|
||||
|
||||
assert!(matches!(result, AsgBuilderError::AsgError(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sets_fragment() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
let frag = FragmentText::from("foo");
|
||||
|
||||
let evs = vec![
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Ok(XmloEvent::Fragment(&sym, frag.clone())),
|
||||
];
|
||||
|
||||
let _ = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.unwrap();
|
||||
|
||||
let node = sut
|
||||
.lookup(&sym)
|
||||
.expect("ident/fragment was not added to graph");
|
||||
|
||||
assert_eq!(
|
||||
Some(&IdentObject::IdentFragment(
|
||||
&sym,
|
||||
IdentKind::Meta,
|
||||
Default::default(),
|
||||
frag
|
||||
)),
|
||||
sut.get(node),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_missing_ident_for_fragment() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
|
||||
// Note: missing `SymDecl`.
|
||||
let evs = vec![Ok(XmloEvent::Fragment(&sym, "foo".into()))];
|
||||
|
||||
let result = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect_err("expected error for fragment without ident");
|
||||
|
||||
assert_eq!(
|
||||
AsgBuilderError::MissingFragmentIdent(sym.to_string()),
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragment_error_propagates() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = symbol_dummy!(1, "sym");
|
||||
let frag = FragmentText::from("foo");
|
||||
|
||||
let evs = vec![
|
||||
Ok(XmloEvent::SymDecl(
|
||||
&sym,
|
||||
SymAttrs {
|
||||
// Invalid fragment destination
|
||||
extern_: true,
|
||||
ty: Some(SymType::Meta),
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
Ok(XmloEvent::Fragment(&sym, frag.clone())),
|
||||
];
|
||||
|
||||
let result = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.expect_err("expected fragment set failure");
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
AsgBuilderError::AsgError(AsgError::ObjectTransition(_))
|
||||
));
|
||||
|
||||
let node = sut
|
||||
.lookup(&sym)
|
||||
.expect("ident/fragment was not added to graph");
|
||||
|
||||
// The identifier should not have been modified on failure.
|
||||
assert!(matches!(
|
||||
sut.get(node).unwrap(),
|
||||
IdentObject::Extern(_, _, _)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stops_at_eoh() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let pkg_name = symbol_dummy!(1, "pkg name");
|
||||
|
||||
let evs = vec![
|
||||
// Stop here.
|
||||
Ok(XmloEvent::Eoh),
|
||||
// Shouldn't make it to this one.
|
||||
Ok(XmloEvent::Package(PackageAttrs {
|
||||
name: Some(&pkg_name),
|
||||
..Default::default()
|
||||
})),
|
||||
];
|
||||
|
||||
let state = sut
|
||||
.import_xmlo(evs.into_iter(), SutState::default())
|
||||
.unwrap();
|
||||
|
||||
// Should still be true because we didn't get to the `PackageAttrs`
|
||||
// event.
|
||||
assert!(state.is_first());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue