use crate::{
command_line as cli,
diagnostics::{codes::Severity, Diagnostic, Diagnostics},
naming::ast::ModuleDefinition,
};
use clap::*;
use move_ir_types::location::*;
use move_symbol_pool::Symbol;
use petgraph::{algo::astar as petgraph_astar, graphmap::DiGraphMap};
use std::{
collections::BTreeMap,
fmt,
hash::Hash,
sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
};
pub mod ast_debug;
pub mod remembering_unique_map;
pub mod unique_map;
pub mod unique_set;
pub use move_command_line_common::parser::{
parse_address_number as parse_address, parse_u128, parse_u64, parse_u8, NumberFormat,
};
pub use move_command_line_common::address::NumericalAddress;
pub fn parse_named_address(s: &str) -> anyhow::Result<(String, NumericalAddress)> {
let before_after = s.split('=').collect::<Vec<_>>();
if before_after.len() != 2 {
anyhow::bail!(
"Invalid named address assignment. Must be of the form <address_name>=<address>, but \
found '{}'",
s
);
}
let name = before_after[0].parse()?;
let addr = NumericalAddress::parse_str(before_after[1])
.map_err(|err| anyhow::format_err!("{}", err))?;
Ok((name, addr))
}
pub trait TName: Eq + Ord + Clone {
type Key: Ord + Clone;
type Loc: Copy;
fn drop_loc(self) -> (Self::Loc, Self::Key);
fn add_loc(loc: Self::Loc, key: Self::Key) -> Self;
fn borrow(&self) -> (&Self::Loc, &Self::Key);
}
pub trait Identifier {
fn value(&self) -> Symbol;
fn loc(&self) -> Loc;
}
pub type Name = Spanned<Symbol>;
impl TName for Name {
type Key = Symbol;
type Loc = Loc;
fn drop_loc(self) -> (Loc, Symbol) {
(self.loc, self.value)
}
fn add_loc(loc: Loc, key: Symbol) -> Self {
sp(loc, key)
}
fn borrow(&self) -> (&Loc, &Symbol) {
(&self.loc, &self.value)
}
}
pub fn shortest_cycle<'a, T: Ord + Hash>(
dependency_graph: &DiGraphMap<&'a T, ()>,
start: &'a T,
) -> Vec<&'a T> {
let shortest_path = dependency_graph
.neighbors(start)
.fold(None, |shortest_path, neighbor| {
let path_opt = petgraph_astar(
dependency_graph,
neighbor,
|finish| finish == start,
|_e| 1,
|_| 0,
);
match (shortest_path, path_opt) {
(p, None) | (None, p) => p,
(Some((acc_len, acc_path)), Some((cur_len, cur_path))) => {
Some(if cur_len < acc_len {
(cur_len, cur_path)
} else {
(acc_len, acc_path)
})
}
}
});
let (_, mut path) = shortest_path.unwrap();
path.insert(0, start);
path
}
pub type NamedAddressMap = BTreeMap<Symbol, NumericalAddress>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NamedAddressMapIndex(usize);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NamedAddressMaps(Vec<NamedAddressMap>);
impl NamedAddressMaps {
pub fn new() -> Self {
Self(vec![])
}
pub fn insert(&mut self, m: NamedAddressMap) -> NamedAddressMapIndex {
let index = self.0.len();
self.0.push(m);
NamedAddressMapIndex(index)
}
pub fn get(&self, idx: NamedAddressMapIndex) -> &NamedAddressMap {
&self.0[idx.0]
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PackagePaths<Path: Into<Symbol> = Symbol, NamedAddress: Into<Symbol> = Symbol> {
pub name: Option<Symbol>,
pub paths: Vec<Path>,
pub named_address_map: BTreeMap<NamedAddress, NumericalAddress>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct IndexedPackagePath {
pub package: Option<Symbol>,
pub path: Symbol,
pub named_address_map: NamedAddressMapIndex,
}
pub type AttributeDeriver = dyn Fn(&mut CompilationEnv, &mut ModuleDefinition);
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CompilationEnv {
flags: Flags,
diags: Diagnostics,
}
impl CompilationEnv {
pub fn new(flags: Flags) -> Self {
Self {
flags,
diags: Diagnostics::new(),
}
}
pub fn add_diag(&mut self, diag: Diagnostic) {
self.diags.add(diag)
}
pub fn add_diags(&mut self, diags: Diagnostics) {
self.diags.extend(diags)
}
pub fn has_diags(&self) -> bool {
!self.diags.is_empty()
}
pub fn count_diags(&self) -> usize {
self.diags.len()
}
pub fn check_diags_at_or_above_severity(
&mut self,
threshold: Severity,
) -> Result<(), Diagnostics> {
match self.diags.max_severity() {
Some(max) if max >= threshold => Err(std::mem::take(&mut self.diags)),
Some(_) | None => Ok(()),
}
}
pub fn take_final_warning_diags(&mut self) -> Diagnostics {
let final_diags = std::mem::take(&mut self.diags);
debug_assert!(final_diags
.max_severity()
.map(|s| s == Severity::Warning)
.unwrap_or(true));
final_diags
}
pub fn flags(&self) -> &Flags {
&self.flags
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Counter(usize);
impl Counter {
pub fn next() -> u64 {
static COUNTER_NEXT: AtomicUsize = AtomicUsize::new(0);
COUNTER_NEXT.fetch_add(1, AtomicOrdering::AcqRel) as u64
}
}
pub fn format_delim<T: fmt::Display, I: IntoIterator<Item = T>>(items: I, delim: &str) -> String {
items
.into_iter()
.map(|item| format!("{}", item))
.collect::<Vec<_>>()
.join(delim)
}
pub fn format_comma<T: fmt::Display, I: IntoIterator<Item = T>>(items: I) -> String {
format_delim(items, ", ")
}
#[derive(Clone, Debug, Eq, PartialEq, Parser)]
pub struct Flags {
#[clap(
short = cli::TEST_SHORT,
long = cli::TEST,
)]
test: bool,
#[clap(
long = cli::FLAVOR,
)]
flavor: String,
#[clap(
long = cli::BYTECODE_VERSION,
)]
bytecode_version: Option<u32>,
#[clap(
name = "SOURCES_SHADOW_DEPS",
short = cli::SHADOW_SHORT,
long = cli::SHADOW,
)]
shadow: bool,
#[clap(skip)]
keep_testing_functions: bool,
}
impl Flags {
pub fn empty() -> Self {
Self {
test: false,
shadow: false,
flavor: "".to_string(),
bytecode_version: None,
keep_testing_functions: false,
}
}
pub fn testing() -> Self {
Self {
test: true,
shadow: false,
flavor: "".to_string(),
bytecode_version: None,
keep_testing_functions: false,
}
}
pub fn set_flavor(self, flavor: impl ToString) -> Self {
Self {
flavor: flavor.to_string(),
..self
}
}
pub fn set_keep_testing_functions(self, value: bool) -> Self {
Self {
keep_testing_functions: value,
..self
}
}
pub fn set_sources_shadow_deps(self, sources_shadow_deps: bool) -> Self {
Self {
shadow: sources_shadow_deps,
..self
}
}
pub fn is_empty(&self) -> bool {
self == &Self::empty()
}
pub fn is_testing(&self) -> bool {
self.test
}
pub fn keep_testing_functions(&self) -> bool {
self.test || self.keep_testing_functions
}
pub fn sources_shadow_deps(&self) -> bool {
self.shadow
}
pub fn has_flavor(&self, flavor: &str) -> bool {
self.flavor == flavor
}
pub fn bytecode_version(&self) -> Option<u32> {
self.bytecode_version
}
}
pub mod known_attributes {
use once_cell::sync::Lazy;
use std::{collections::BTreeSet, fmt};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AttributePosition {
AddressBlock,
Module,
Script,
Use,
Friend,
Constant,
Struct,
Function,
Spec,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum KnownAttribute {
Testing(TestingAttribute),
Native(NativeAttribute),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TestingAttribute {
TestOnly,
Test,
ExpectedFailure,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum NativeAttribute {
BytecodeInstruction,
}
impl fmt::Display for AttributePosition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AddressBlock => write!(f, "address block"),
Self::Module => write!(f, "module"),
Self::Script => write!(f, "script"),
Self::Use => write!(f, "use"),
Self::Friend => write!(f, "friend"),
Self::Constant => write!(f, "constant"),
Self::Struct => write!(f, "struct"),
Self::Function => write!(f, "function"),
Self::Spec => write!(f, "spec"),
}
}
}
impl KnownAttribute {
pub fn resolve(attribute_str: impl AsRef<str>) -> Option<Self> {
Some(match attribute_str.as_ref() {
TestingAttribute::TEST => Self::Testing(TestingAttribute::Test),
TestingAttribute::TEST_ONLY => Self::Testing(TestingAttribute::TestOnly),
TestingAttribute::EXPECTED_FAILURE => {
Self::Testing(TestingAttribute::ExpectedFailure)
}
NativeAttribute::BYTECODE_INSTRUCTION => {
Self::Native(NativeAttribute::BytecodeInstruction)
}
_ => return None,
})
}
pub const fn name(&self) -> &str {
match self {
Self::Testing(a) => a.name(),
Self::Native(a) => a.name(),
}
}
pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
match self {
Self::Testing(a) => a.expected_positions(),
Self::Native(a) => a.expected_positions(),
}
}
}
impl TestingAttribute {
pub const TEST: &'static str = "test";
pub const EXPECTED_FAILURE: &'static str = "expected_failure";
pub const TEST_ONLY: &'static str = "test_only";
pub const CODE_ASSIGNMENT_NAME: &'static str = "abort_code";
pub const fn name(&self) -> &str {
match self {
Self::Test => Self::TEST,
Self::TestOnly => Self::TEST_ONLY,
Self::ExpectedFailure => Self::EXPECTED_FAILURE,
}
}
pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
static TEST_ONLY_POSITIONS: Lazy<BTreeSet<AttributePosition>> = Lazy::new(|| {
IntoIterator::into_iter([
AttributePosition::AddressBlock,
AttributePosition::Module,
AttributePosition::Use,
AttributePosition::Friend,
AttributePosition::Constant,
AttributePosition::Struct,
AttributePosition::Function,
])
.collect()
});
static TEST_POSITIONS: Lazy<BTreeSet<AttributePosition>> =
Lazy::new(|| IntoIterator::into_iter([AttributePosition::Function]).collect());
static EXPECTED_FAILURE_POSITIONS: Lazy<BTreeSet<AttributePosition>> =
Lazy::new(|| IntoIterator::into_iter([AttributePosition::Function]).collect());
match self {
TestingAttribute::TestOnly => &*TEST_ONLY_POSITIONS,
TestingAttribute::Test => &*TEST_POSITIONS,
TestingAttribute::ExpectedFailure => &*EXPECTED_FAILURE_POSITIONS,
}
}
}
impl NativeAttribute {
pub const BYTECODE_INSTRUCTION: &'static str = "bytecode_instruction";
pub const fn name(&self) -> &str {
match self {
NativeAttribute::BytecodeInstruction => Self::BYTECODE_INSTRUCTION,
}
}
pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
static BYTECODE_INSTRUCTION_POSITIONS: Lazy<BTreeSet<AttributePosition>> =
Lazy::new(|| IntoIterator::into_iter([AttributePosition::Function]).collect());
match self {
NativeAttribute::BytecodeInstruction => &*BYTECODE_INSTRUCTION_POSITIONS,
}
}
}
}