pub(crate) mod codegen;
pub(crate) mod doc;
pub(crate) mod ensures;
pub(crate) mod invariant;
pub(crate) mod parse;
pub(crate) mod requires;
pub(crate) mod traits;
pub(crate) use ensures::ensures;
pub(crate) use invariant::invariant;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
pub(crate) use requires::requires;
use syn::{Expr, ItemFn};
pub(crate) use traits::{contract_trait_item_impl, contract_trait_item_trait};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum ContractMode {
Always,
Disabled,
Debug,
Test,
LogOnly,
}
impl ContractMode {
pub(crate) fn name(self) -> Option<&'static str> {
match self {
ContractMode::Always => Some(""),
ContractMode::Disabled => None,
ContractMode::Debug => Some("debug_"),
ContractMode::Test => Some("test_"),
ContractMode::LogOnly => None,
}
}
pub(crate) fn final_mode(self) -> Self {
if self == ContractMode::Disabled || self == ContractMode::Test {
return self;
}
if cfg!(feature = "disable_contracts") {
ContractMode::Disabled
} else if cfg!(feature = "override_debug") {
if self == ContractMode::LogOnly {
self
} else {
ContractMode::Debug
}
} else if cfg!(feature = "override_log") {
ContractMode::LogOnly
} else {
self
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ContractType {
Requires,
Ensures,
Invariant,
}
impl ContractType {
pub(crate) fn message_name(self) -> &'static str {
match self {
ContractType::Requires => "Pre-condition",
ContractType::Ensures => "Post-condition",
ContractType::Invariant => "Invariant",
}
}
pub(crate) fn contract_type_and_mode(ident: &str) -> Option<(ContractType, ContractMode)> {
match ident {
"requires" => Some((ContractType::Requires, ContractMode::Always)),
"ensures" => Some((ContractType::Ensures, ContractMode::Always)),
"invariant" => Some((ContractType::Invariant, ContractMode::Always)),
"debug_requires" => Some((ContractType::Requires, ContractMode::Debug)),
"debug_ensures" => Some((ContractType::Ensures, ContractMode::Debug)),
"debug_invariant" => Some((ContractType::Invariant, ContractMode::Debug)),
"test_requires" => Some((ContractType::Requires, ContractMode::Test)),
"test_ensures" => Some((ContractType::Ensures, ContractMode::Test)),
"test_invariant" => Some((ContractType::Invariant, ContractMode::Test)),
_ => None,
}
}
}
#[derive(Debug)]
pub(crate) struct Contract {
pub(crate) _span: Span,
pub(crate) ty: ContractType,
pub(crate) mode: ContractMode,
pub(crate) assertions: Vec<Expr>,
pub(crate) streams: Vec<TokenStream>,
pub(crate) desc: Option<String>,
}
impl Contract {
pub(crate) fn from_toks(ty: ContractType, mode: ContractMode, toks: TokenStream) -> Self {
let (assertions, streams, desc) = parse::parse_attributes(toks);
let span = Span::call_site();
Self {
_span: span,
ty,
mode,
assertions,
streams,
desc,
}
}
}
#[derive(Debug)]
pub(crate) struct FuncWithContracts {
pub(crate) contracts: Vec<Contract>,
pub(crate) function: ItemFn,
}
impl FuncWithContracts {
pub(crate) fn new_with_initial_contract(
mut func: ItemFn,
cty: ContractType,
cmode: ContractMode,
ctoks: TokenStream,
) -> Self {
let mut contracts: Vec<Contract> = {
let initial_contract = Contract::from_toks(cty, cmode, ctoks);
vec![initial_contract]
};
let contract_attrs = func
.attrs
.iter()
.filter_map(|a| {
let name = a.path().segments.last().unwrap().ident.to_string();
let (ty, mode) = ContractType::contract_type_and_mode(&name)?;
Some((ty, mode, a))
})
.map(|(ty, mode, attr)| {
fn extract_first_arg_stream(attr: &syn::Attribute) -> TokenStream {
match &attr.meta {
syn::Meta::List(list) => list.tokens.clone(),
syn::Meta::Path(path) => path.to_token_stream(),
syn::Meta::NameValue(nv) => nv.value.to_token_stream(),
}
}
let toks = extract_first_arg_stream(attr);
Contract::from_toks(ty, mode, toks)
});
contracts.extend(contract_attrs);
{
let attrs = std::mem::take(&mut func.attrs);
let other_attrs = attrs
.into_iter()
.filter(|attr| {
ContractType::contract_type_and_mode(
&attr.path().segments.last().unwrap().ident.to_string(),
)
.is_none()
})
.collect();
func.attrs = other_attrs;
}
Self {
function: func,
contracts,
}
}
pub(crate) fn generate(mut self) -> TokenStream {
let doc_attrs = doc::generate_attributes(&self.contracts);
let olds = codegen::extract_old_calls(&mut self.contracts);
codegen::generate(self, doc_attrs, olds)
}
}