tame/tamer/src/asg/graph/object/ident/test.rs

794 lines
21 KiB
Rust

// Tests for ASG identifiers
//
// 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 <http://www.gnu.org/licenses/>.
use super::*;
use crate::{
num::Dim,
span::dummy::*,
sym::{GlobalSymbolIntern, SymbolId},
};
// Note that Ident has no variants capable of None
#[test]
fn ident_name() {
let name = "name".into();
let spair = SPair(name, S1);
assert_eq!(Some(spair), Ident::Missing(spair).name());
assert_eq!(
Some(spair),
Ident::Opaque(spair, IdentKind::Meta, Source::default()).name()
);
assert_eq!(
Some(spair),
Ident::Extern(spair, IdentKind::Meta, Source::default()).name()
);
assert_eq!(
Some(spair),
Ident::IdentFragment(
spair,
IdentKind::Meta,
Source::default(),
"".intern()
)
.name()
);
}
#[test]
fn ident_kind() {
let name = SPair("foo".into(), S1);
let kind = IdentKind::Class(Dim::Matrix);
assert_eq!(None, Ident::Missing(name).kind());
assert_eq!(
Some(&kind),
Ident::Opaque(name, kind.clone(), Source::default()).kind()
);
assert_eq!(
Some(&kind),
Ident::Extern(name, kind.clone(), Source::default()).kind()
);
assert_eq!(
Some(&kind),
Ident::IdentFragment(
name,
kind.clone(),
Source::default(),
"".intern()
)
.kind()
);
}
#[test]
fn ident_src() {
let name = SPair("foo".into(), S1);
let src = Source {
desc: Some("test source".into()),
..Default::default()
};
assert_eq!(None, Ident::Missing(name).src());
assert_eq!(
Some(&src),
Ident::Opaque(name, IdentKind::Meta, src.clone()).src()
);
assert_eq!(
None,
Ident::Extern(name, IdentKind::Meta, src.clone()).src()
);
assert_eq!(
Some(&src),
Ident::IdentFragment(name, IdentKind::Meta, src.clone(), "".intern())
.src()
);
}
#[test]
fn ident_fragment() {
let name = SPair("foo".into(), S1);
let text = "foo".into();
assert_eq!(None, Ident::Missing(name).fragment());
assert_eq!(
None,
Ident::Opaque(name, IdentKind::Meta, Source::default()).fragment()
);
assert_eq!(
None,
Ident::Extern(name, IdentKind::Meta, Source::default()).fragment()
);
assert_eq!(
Some(text),
Ident::IdentFragment(name, IdentKind::Meta, Source::default(), text,)
.fragment()
);
}
#[test]
fn ident_missing() {
let name = SPair("foo".into(), S1);
assert_eq!(Ident::Missing(name), Ident::declare(name));
}
#[test]
fn resolved_on_missing() {
let name = SPair("foo".into(), S1);
assert_eq!(
Ident::declare(name).resolved(),
Err(UnresolvedError::Missing(name))
);
}
#[test]
fn ident_resolved() {
let sym = "foo".into();
let kind = IdentKind::Meta;
let src = Source {
desc: Some("ident ctor".into()),
..Default::default()
};
assert_eq!(
Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.unwrap(),
Ident::Opaque(SPair(sym, S2), kind.clone(), src.clone()),
);
}
#[test]
fn resolved_on_ident() {
let sym = "ident resolve".into();
let kind = IdentKind::Meta;
let src = Source {
desc: Some("ident ctor".into()),
..Default::default()
};
assert_eq!(
Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.unwrap()
.resolved()
.unwrap(),
(
&Ident::Opaque(SPair(sym, S2), kind.clone(), src.clone()),
SPair(sym, S2)
),
);
}
// Note that we don't care about similar sources. It's expected
// that the system populating the ASG will only resolve local
// symbols, and so redeclarations should represent that multiple
// packages have the same local symbol.
#[test]
fn ident_object_redeclare_same_src() {
let sym = "redecl".into();
let kind = IdentKind::Meta;
let src = Source::default();
let first = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.unwrap();
// Resolve twice, as if we encountered two local symbols.
assert_eq!(
first.clone().resolve(S3, kind.clone(), src.clone()),
Err((first, TransitionError::Redeclare(SPair(sym, S2), S3))),
);
}
mod extern_ {
use super::*;
#[test]
fn ident_object() {
let sym = "extern".into();
let kind = IdentKind::Class(Dim::Vector);
let src = Source {
desc: Some("extern".into()),
..Default::default()
};
assert_eq!(
Ident::declare(SPair(sym, S1)).extern_(
S2,
kind.clone(),
src.clone()
),
Ok(Ident::Extern(SPair(sym, S2), kind, src)),
);
}
#[test]
fn resolved_on_extern() {
let sym = "extern resolved".into();
let kind = IdentKind::Class(Dim::Vector);
let pkg_name: SymbolId = "pkg/name".intern();
let src = Source {
pkg_name: Some(pkg_name),
desc: Some("extern".into()),
..Default::default()
};
assert_eq!(
Ident::Extern(SPair(sym, S1), kind.clone(), src.clone()).resolved(),
Err(UnresolvedError::Extern(SPair(sym, S1), kind)),
);
}
// Extern first, then identifier
#[test]
fn redeclare_compatible_resolves() {
let sym = "extern_re_pre".into();
let kind = IdentKind::Class(Dim::Matrix);
let src = Source {
desc: Some("okay".into()),
..Default::default()
};
// Compatible kind, should resolve.
let result = Ident::declare(SPair(sym, S1))
.extern_(S2, kind.clone(), Source::default())
.and_then(|o| o.resolve(S3, kind.clone(), src.clone()));
assert_eq!(Ok(Ident::Opaque(SPair(sym, S3), kind, src)), result);
}
// Identifier first, then extern
#[test]
fn redeclare_compatible_resolves_post() {
let sym = "extern_re_post".into();
let kind = IdentKind::Class(Dim::Vector);
let src = Source {
desc: Some("okay".into()),
..Default::default()
};
// Compatible kind, should resolve.
let result = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.and_then(|o| o.extern_(S3, kind.clone(), Source::default()));
// Note that we keep the span of the concrete identifier,
// despite the extern being encountered later.
assert_eq!(Ok(Ident::Opaque(SPair(sym, S2), kind, src)), result,);
}
#[test]
fn redeclare_another_extern() {
let sym = "extern_extern".into();
let kind = IdentKind::Class(Dim::Scalar);
let src_first = Source {
desc: Some("first src".into()),
..Default::default()
};
let src_second = Source {
desc: Some("second src".into()),
..Default::default()
};
let result = Ident::declare(SPair(sym, S1))
.extern_(S2, kind.clone(), src_first.clone())
.and_then(|o| o.extern_(S3, kind.clone(), src_second));
// Note that, if it resolves, it should keep what is
// _existing_, meaning that it must keep the first src.
assert_eq!(Ok(Ident::Extern(SPair(sym, S2), kind, src_first)), result);
}
// Extern first, then identifier
#[test]
fn redeclare_post_incompatible_kind() {
let sym = "extern_re_bad_post".into();
let kind = IdentKind::Class(Dim::Matrix);
let src = Source {
desc: Some("bad kind".into()),
..Default::default()
};
let orig = Ident::declare(SPair(sym, S1))
.extern_(S2, kind.clone(), Source::default())
.unwrap();
// Incompatible kind
let kind_bad = IdentKind::Meta;
assert_eq!(
orig.clone().resolve(S3, kind_bad.clone(), src),
Err((
orig,
TransitionError::ExternResolution(
SPair(sym, S2),
kind,
(kind_bad, S3)
)
)),
);
}
// Identifier first, then extern
#[test]
fn redeclare_pre_incompatible_kind() {
let sym = "extern_re_bad_pre".into();
let kind_orig = IdentKind::Class(Dim::Vector);
let src = Source {
desc: Some("bad kind".into()),
..Default::default()
};
let orig = Ident::declare(SPair(sym, S1))
.resolve(S2, kind_orig.clone(), src.clone())
.unwrap();
// Extern with incompatible kind.
let kind_extern = IdentKind::Meta;
assert_eq!(
orig.clone()
.extern_(S3, kind_extern.clone(), Source::default()),
Err((
orig,
TransitionError::ExternResolution(
SPair(sym, S2),
kind_orig,
(kind_extern, S3)
)
)),
);
}
}
#[test]
fn add_fragment_to_ident() {
let sym = "tofrag".into();
let src = Source {
generated: true,
..Default::default()
};
let kind = IdentKind::Meta;
let ident = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.unwrap();
let text = FragmentText::from("a fragment");
assert_eq!(
ident.set_fragment(text.clone()),
Ok(Ident::IdentFragment(SPair(sym, S2), kind, src, text)),
);
}
#[test]
fn resolved_on_fragment() {
let sym = "tofrag resolved".into();
let src = Source {
generated: true,
..Default::default()
};
let kind = IdentKind::Meta;
let ident = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), src.clone())
.unwrap();
let text = FragmentText::from("a fragment for resolved()");
assert_eq!(
ident.set_fragment(text.clone()).unwrap().resolved(),
Ok((
&Ident::IdentFragment(SPair(sym, S2), kind, src, text),
SPair(sym, S2),
)),
);
}
#[test]
fn add_fragment_to_fragment_fails() {
let sym = "badsym".into();
let ident = Ident::declare(SPair(sym, S1))
.resolve(S2, IdentKind::Meta, Source::default())
.unwrap();
let ident_with_frag = ident
.set_fragment("orig fragment".into())
.expect("set_fragment failed");
// Since it's already a fragment, this should fail.
assert_eq!(
ident_with_frag.clone().set_fragment("replacement".into()),
Err((
ident_with_frag,
TransitionError::BadFragmentDest(SPair(sym, S2))
)),
);
}
mod override_ {
use super::*;
#[test]
fn declare_virtual_ident_first() {
let sym = "virtual".into();
let over_src_name = "src".into();
let kind = IdentKind::Meta;
let virt = Ident::declare(SPair(sym, S1))
.resolve(
S2,
kind.clone(),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let over_src = Source {
virtual_: true, // this needn't be set, but see below
override_: true,
src: Some(over_src_name),
..Default::default()
};
let result = virt.resolve(S3, kind.clone(), over_src.clone());
// Overriding should clear any virtual flag that may have
// been set to prevent override-overrides.
let expected_src = Source {
virtual_: false,
..over_src
};
assert_eq!(
Ok(Ident::Opaque(SPair(sym, S3), kind, expected_src)),
result
);
}
// Override is encountered before the virtual
#[test]
fn declare_virtual_ident_after_override() {
let sym = "virtual_second".into();
let virt_src_name = "virt_src".into();
let kind = IdentKind::Meta;
let over_src = Source {
virtual_: true, // this needn't be set, but see below
override_: true,
..Default::default()
};
let over = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), over_src.clone())
.unwrap();
let virt_src = Source {
virtual_: true,
src: Some(virt_src_name),
..Default::default()
};
let result = over.resolve(S3, kind.clone(), virt_src.clone());
// Overriding should clear any virtual flag that may have
// been set to prevent override-overrides. We should also
// take the override source even though virtual was second.
let expected_src = Source {
virtual_: false,
..over_src
};
assert_eq!(
Ok(Ident::Opaque(SPair(sym, S3), kind, expected_src)),
result
);
}
#[test]
fn declare_override_non_virtual() {
let sym = "non_virtual".into();
let kind = IdentKind::Meta;
let non_virt = Ident::declare(SPair(sym, S1))
.resolve(
S2,
kind.clone(),
Source {
virtual_: false,
..Default::default()
},
)
.unwrap();
let over_src = Source {
override_: true,
..Default::default()
};
// This isn't the purpose of the test, but we want to make
// sure that the non-virtual override error occurs before
// the kind error.
let bad_kind = IdentKind::Cgen(Dim::Vector);
assert_eq!(
non_virt.clone().resolve(S3, bad_kind, over_src.clone()),
Err((
non_virt,
TransitionError::NonVirtualOverride(SPair(sym, S2), S3)
))
);
}
#[test]
fn declare_virtual_ident_incompatible_kind() {
let sym = "virtual".into();
let src_sym: SymbolId = "src".into();
let kind = IdentKind::Meta;
let virt = Ident::declare(SPair(sym, S1))
.resolve(
S2,
kind.clone(),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let over_src = Source {
override_: true,
src: Some(src_sym),
..Default::default()
};
let bad_kind = IdentKind::Cgen(Dim::Vector);
assert_eq!(
virt.clone().resolve(S3, bad_kind.clone(), over_src.clone()),
Err((
virt,
TransitionError::VirtualOverrideKind(
SPair(sym, S2),
kind,
(bad_kind, S3)
)
)),
);
}
// Encounter virtual first and override second should cause the
// fragment to be cleared to make way for the new fragment.
#[test]
fn declare_override_virtual_ident_fragment_virtual_first() {
let sym = "virtual".into();
let over_src = "src".into();
let kind = IdentKind::Meta;
// Remember: override is going to come first...
let over_src = Source {
override_: true,
src: Some(over_src),
..Default::default()
};
// ...and virt second.
let virt_src = Source {
virtual_: true,
..Default::default()
};
let over = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), over_src.clone())
.unwrap();
// So we should _keep_ this fragment, since it represent the
// override, even though it's appearing first.
let text = FragmentText::from("keep me");
let over_frag = over.set_fragment(text.clone());
assert_eq!(
over_frag,
Ok(Ident::IdentFragment(
SPair(sym, S2),
kind.clone(),
over_src.clone(),
text.clone(),
)),
);
// Overriding should _not_ have cleared the fragment since
// the override was encountered _first_, so we want to keep
// its fragment.
let result =
over_frag
.unwrap()
.resolve(S3, kind.clone(), virt_src.clone());
assert_eq!(
result,
Ok(Ident::IdentFragment(
SPair(sym, S3),
kind.clone(),
over_src.clone(),
text.clone()
)),
);
// Finally, after performing this transition, we will
// inevitably encounter the fragment for the virtual
// identifier, which we must ignore. So we must make sure
// that encountering it will not cause an error, because we
// still have an IdentFragment at this point.
assert_eq!(
Ok(Ident::IdentFragment(
SPair(sym, S3),
kind,
over_src.clone(),
text.clone()
)),
result.unwrap().set_fragment("virt fragment".into()),
);
}
// Encountering _override_ first and virtual second should _not_
// clear the fragment, otherwise the virtual fragment will take
// precedence over the override.
#[test]
fn declare_override_virtual_ident_fragment_override_first() {
let sym = "virtual".into();
let over_src = "src".into();
let kind = IdentKind::Meta;
let virt_src = Source {
virtual_: true,
..Default::default()
};
let virt = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), virt_src.clone())
.unwrap();
let text = FragmentText::from("remove me");
let virt_frag = virt.set_fragment(text.clone());
assert_eq!(
virt_frag,
Ok(Ident::IdentFragment(
SPair(sym, S2),
kind.clone(),
virt_src,
text
)),
);
let over_src = Source {
override_: true,
src: Some(over_src),
..Default::default()
};
assert_eq!(
virt_frag
.unwrap()
.resolve(S3, kind.clone(), over_src.clone()),
// The act of overriding the object should have cleared any
// existing fragment, making way for a new fragment to take its
// place as soon as it is discovered. (So, back to an
// Ident::Ident.)
Ok(Ident::Opaque(SPair(sym, S3), kind, over_src)),
);
}
#[test]
fn declare_override_virtual_ident_fragment_incompatible_type() {
let sym = "virtual".into();
let over_src = "src".into();
let kind = IdentKind::Meta;
let virt_src = Source {
virtual_: true,
..Default::default()
};
let virt = Ident::declare(SPair(sym, S1))
.resolve(S2, kind.clone(), virt_src.clone())
.unwrap();
let virt_frag = virt.set_fragment("".into()).unwrap();
let over_src = Source {
override_: true,
src: Some(over_src),
..Default::default()
};
let bad_kind = IdentKind::Cgen(Dim::Vector);
assert_eq!(
virt_frag
.clone()
.resolve(S3, bad_kind.clone(), over_src.clone()),
Err((
virt_frag,
TransitionError::VirtualOverrideKind(
SPair(sym, S2),
kind,
(bad_kind, S3)
)
)),
);
}
}
fn add_ident_kind_ignores(given: IdentKind, expected: IdentKind) {
let sym = "tofrag".into();
let src = Source {
generated: true,
..Default::default()
};
let obj = Ident::declare(SPair(sym, S1))
.resolve(S2, given, src.clone())
.unwrap();
let fragment = "a fragment".intern();
assert_eq!(
obj.set_fragment(fragment),
Ok(Ident::IdentFragment(
SPair(sym, S2),
expected,
src,
fragment
)),
);
}
#[test]
fn add_fragment_to_ident_map_head() {
add_ident_kind_ignores(IdentKind::MapHead, IdentKind::MapHead)
}
#[test]
fn add_fragment_to_ident_map_tail() {
add_ident_kind_ignores(IdentKind::MapTail, IdentKind::MapTail)
}
#[test]
fn add_fragment_to_ident_retmap_head() {
add_ident_kind_ignores(IdentKind::RetMapHead, IdentKind::RetMapHead)
}
#[test]
fn add_fragment_to_ident_retmap_tail() {
add_ident_kind_ignores(IdentKind::RetMapTail, IdentKind::RetMapTail)
}