[DEV-7086] TAMER: Disallow IdentObject::resolve redeclarations

Except under well-defined circumstances.
master
Mike Gerwitz 2020-04-03 12:05:14 -04:00
parent 0868453dab
commit da5057058d
1 changed files with 63 additions and 24 deletions

View File

@ -261,6 +261,9 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
/// The kind of identifier cannot change,
/// but the argument is provided here for convenience so that the
/// caller does not need to perform such a check itself.
///
/// If no extern or virtual override is possible,
/// an identifier cannot be redeclared and this operation will fail.
fn resolve(
self,
kind: IdentKind,
@ -338,12 +341,26 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
Ok(IdentObject::Ident(name, kind, src))
}
IdentObject::Missing(name) | IdentObject::Ident(name, _, _) => {
// These represent the prolog and epilogue of maps. This
// situation will be resolved in the future.
IdentObject::IdentFragment(_, IdentKind::MapHead, _, _)
| IdentObject::IdentFragment(_, IdentKind::MapTail, _, _)
| IdentObject::IdentFragment(_, IdentKind::RetMapHead, _, _)
| IdentObject::IdentFragment(_, IdentKind::RetMapTail, _, _) => {
Ok(self)
}
IdentObject::Missing(name) => {
Ok(IdentObject::Ident(name, kind, src))
}
// TODO
_ => Ok(self),
_ => {
let err = TransitionError::Redeclare {
name: self.name().unwrap().to_string(),
};
Err((self, err))
}
}
}
@ -393,6 +410,8 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
Ok(self)
}
// These represent the prolog and epilogue of maps. This
// situation will be resolved in the future.
IdentObject::IdentFragment(_, IdentKind::MapHead, _, _)
| IdentObject::IdentFragment(_, IdentKind::MapTail, _, _)
| IdentObject::IdentFragment(_, IdentKind::RetMapHead, _, _)
@ -414,10 +433,12 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
/// An error attempting to transition from one [`IdentObject`] state to
/// another.
///
/// TODO: Provide enough information to construct a useful message.
#[derive(Clone, Debug, PartialEq)]
pub enum TransitionError {
/// Attempted to redeclare a concrete, non-virtual identifier without an
/// override.
Redeclare { name: String },
/// Extern resolution failure.
///
/// An extern could not be resolved because the provided identifier had
@ -449,6 +470,12 @@ pub enum TransitionError {
impl std::fmt::Display for TransitionError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Redeclare { name } => write!(
fmt,
"cannot redeclare identifier `{}`",
name,
),
Self::ExternResolution {
name,
expected,
@ -762,6 +789,37 @@ mod test {
);
}
// 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 = symbol_dummy!(1, "redecl");
let kind = IdentKind::Meta;
let src = Source::default();
let first = IdentObject::declare(&sym)
.resolve(kind.clone(), src.clone())
.unwrap();
// Resolve twice, as if we encountered two local symbols.
let result = first
.clone()
.resolve(kind.clone(), src.clone())
.expect_err("expected error redeclaring identifier");
match result {
(orig, TransitionError::Redeclare { name }) => {
assert_eq!(first, orig);
assert_eq!(*sym, name);
}
_ => {
panic!("expected TransitionError::Redeclare: {:?}", result)
}
}
}
mod extern_ {
use super::*;
@ -932,25 +990,6 @@ mod test {
}
}
// TODO: incompatible
#[test]
fn redeclare_returns_existing_compatible() {
let sym = symbol_dummy!(1, "symdup");
let first = IdentObject::declare(&sym)
.resolve(IdentKind::Meta, Source::default())
.unwrap();
// Same declaration a second time
assert_eq!(
Ok(first.clone()),
first.clone().resolve(
first.kind().unwrap().clone(),
first.src().unwrap().clone(),
)
);
}
#[test]
fn add_fragment_to_ident() {
let sym = symbol_dummy!(1, "tofrag");