pub mod analysis_db;
pub mod arithmetic_checker;
pub mod contract_interface_builder;
pub mod errors;
pub mod read_only_checker;
pub mod trait_checker;
pub mod type_checker;
pub mod types;
use crate::types::StacksEpochId;
#[cfg(feature = "sqlite")]
use crate::vm::database::MemoryBackingStore;
pub use self::types::{AnalysisPass, ContractAnalysis};
use crate::vm::costs::LimitedCostTracker;
use crate::vm::database::STORE_CONTRACT_SRC_INTERFACE;
use crate::vm::representations::SymbolicExpression;
use crate::vm::types::{QualifiedContractIdentifier, TypeSignature};
use crate::vm::ClarityVersion;
pub use self::analysis_db::AnalysisDatabase;
pub use self::errors::{CheckError, CheckErrors, CheckResult};
use self::arithmetic_checker::ArithmeticOnlyChecker;
use self::contract_interface_builder::build_contract_interface;
use self::read_only_checker::ReadOnlyChecker;
use self::trait_checker::TraitChecker;
use self::type_checker::v2_05::TypeChecker as TypeChecker2_05;
use self::type_checker::v2_1::TypeChecker as TypeChecker2_1;
use crate::vm::ast::build_ast_with_rules;
use crate::vm::ast::ASTRules;
#[cfg(feature = "sqlite")]
pub fn mem_type_check(
snippet: &str,
version: ClarityVersion,
epoch: StacksEpochId,
) -> CheckResult<(Option<TypeSignature>, ContractAnalysis)> {
let contract_identifier = QualifiedContractIdentifier::transient();
let mut contract = build_ast_with_rules(
&contract_identifier,
snippet,
&mut (),
version,
epoch,
ASTRules::PrecheckSize,
)
.unwrap()
.expressions;
let mut marf = MemoryBackingStore::new();
let mut analysis_db = marf.as_analysis_db();
let cost_tracker = LimitedCostTracker::new_free();
match run_analysis(
&QualifiedContractIdentifier::transient(),
&mut contract,
&mut analysis_db,
false,
cost_tracker,
epoch,
version,
) {
Ok(x) => {
let first_type = x
.type_map
.as_ref()
.unwrap()
.get_type(&x.expressions.last().unwrap())
.cloned();
Ok((first_type, x))
}
Err((e, _)) => Err(e),
}
}
#[cfg(test)]
pub fn type_check(
contract_identifier: &QualifiedContractIdentifier,
expressions: &mut [SymbolicExpression],
analysis_db: &mut AnalysisDatabase,
insert_contract: bool,
epoch: &StacksEpochId,
version: &ClarityVersion,
) -> CheckResult<ContractAnalysis> {
run_analysis(
&contract_identifier,
expressions,
analysis_db,
insert_contract,
LimitedCostTracker::new_free(),
epoch.clone(),
version.clone(),
)
.map_err(|(e, _cost_tracker)| e)
}
pub fn run_analysis(
contract_identifier: &QualifiedContractIdentifier,
expressions: &mut [SymbolicExpression],
analysis_db: &mut AnalysisDatabase,
save_contract: bool,
cost_tracker: LimitedCostTracker,
epoch: StacksEpochId,
version: ClarityVersion,
) -> Result<ContractAnalysis, (CheckError, LimitedCostTracker)> {
let mut contract_analysis = ContractAnalysis::new(
contract_identifier.clone(),
expressions.to_vec(),
cost_tracker,
epoch,
version,
);
let result = analysis_db.execute(|db| {
ReadOnlyChecker::run_pass(&epoch, &mut contract_analysis, db)?;
match epoch {
StacksEpochId::Epoch20 | StacksEpochId::Epoch2_05 => {
TypeChecker2_05::run_pass(&epoch, &mut contract_analysis, db)
}
StacksEpochId::Epoch21
| StacksEpochId::Epoch22
| StacksEpochId::Epoch23
| StacksEpochId::Epoch24 => {
TypeChecker2_1::run_pass(&epoch, &mut contract_analysis, db)
}
StacksEpochId::Epoch10 => unreachable!("Epoch 1.0 is not a valid epoch for analysis"),
}?;
TraitChecker::run_pass(&epoch, &mut contract_analysis, db)?;
ArithmeticOnlyChecker::check_contract_cost_eligible(&mut contract_analysis);
if STORE_CONTRACT_SRC_INTERFACE {
let interface = build_contract_interface(&contract_analysis);
contract_analysis.contract_interface = Some(interface);
}
if save_contract {
db.insert_contract(&contract_identifier, &contract_analysis)?;
}
Ok(())
});
match result {
Ok(_) => Ok(contract_analysis),
Err(e) => Err((e, contract_analysis.take_contract_cost_tracker())),
}
}
#[cfg(test)]
mod tests;