#![allow(clippy::collapsible_if)]
#![allow(clippy::collapsible_match)]
use crate::auto_clone::AutoCloneAnalysis;
use crate::parser::*;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
mod borrow_analysis;
mod cache_locality;
mod forbidden_patterns;
mod function_analysis;
mod generic_analysis;
mod module_analysis;
mod mutation_detection;
mod optimization_detectors;
mod parameter_analysis;
mod passthrough_inference;
mod program_initialization;
mod self_access_and_option_refs;
mod self_analysis;
mod self_binding_mutation;
mod self_dispatch_for_loops;
mod self_field_mutation;
mod self_mutating_calls;
mod self_return_and_consumption;
mod signature_registry;
pub mod simd_loops;
pub mod stdlib_method_traits;
mod string_optimization;
mod trait_analysis;
mod type_checking;
pub mod type_collector;
mod usage_tracking;
pub use signature_registry::{FunctionSignature, SignatureRegistry};
pub use cache_locality::{
cache_locality_json_report, AccessPatternKind, AoSoACandidate, CacheLocalityAnalysis,
};
type ProgramAnalysisResult<'ast> = (
Vec<AnalyzedFunction<'ast>>,
SignatureRegistry,
HashMap<String, HashMap<String, AnalyzedFunction<'ast>>>,
);
#[derive(Debug, Clone)]
pub struct AnalyzedFunction<'ast> {
pub decl: FunctionDecl<'ast>,
pub inferred_ownership: HashMap<String, OwnershipMode>,
pub inferred_param_types: Vec<Type>,
pub mutated_variables: HashSet<String>,
pub mutated_parameters: HashSet<String>,
pub auto_clone_analysis: AutoCloneAnalysis,
pub clone_optimizations: Vec<CloneOptimization>,
pub struct_mapping_optimizations: Vec<StructMappingOptimization>,
pub string_optimizations: Vec<StringOptimization>,
pub assignment_optimizations: Vec<AssignmentOptimization>,
pub defer_drop_optimizations: Vec<DeferDropOptimization>,
pub const_static_optimizations: Vec<ConstStaticOptimization>,
pub smallvec_optimizations: Vec<SmallVecOptimization>,
pub cow_optimizations: Vec<CowOptimization>,
pub cache_locality: CacheLocalityAnalysis,
pub str_ref_optimizable_params: HashSet<String>,
}
#[derive(Debug, Clone)]
pub struct AssignmentOptimization {
pub variable: String,
pub location: usize,
pub operation: CompoundOp,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CompoundOp {
AddAssign, SubAssign, MulAssign, DivAssign, }
#[derive(Debug, Clone)]
pub struct DeferDropOptimization {
pub variable: String,
pub estimated_size: EstimatedSize,
pub reason: DeferDropReason,
pub location: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum EstimatedSize {
Small, Medium, Large, VeryLarge, }
#[derive(Debug, Clone, PartialEq)]
pub enum DeferDropReason {
LargeOwnedParameter,
LargeLocalVariable,
LargeReturnedCollection,
}
#[derive(Debug, Clone)]
pub struct ConstStaticOptimization {
pub variable: String,
pub can_be_const: bool,
}
#[derive(Debug, Clone)]
pub struct SmallVecOptimization {
pub variable: String,
pub estimated_max_size: usize, pub stack_size: usize, }
#[derive(Debug, Clone)]
pub struct CowOptimization {
pub variable: String,
pub reason: CowReason,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CowReason {
ConditionalModification, ReadHeavy, }
#[derive(Debug, Clone)]
pub struct StringOptimization {
pub optimization_type: StringOptimizationType,
pub estimated_capacity: Option<usize>,
pub location: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StringOptimizationType {
InterpolationWithCapacity,
ConcatenationChain,
LoopAccumulation,
RepeatedFormatting,
}
#[derive(Debug, Clone)]
pub struct StructMappingOptimization {
pub target_struct: String,
pub source: String,
pub field_mappings: Vec<(String, String)>,
pub strategy: MappingStrategy,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MappingStrategy {
DirectMapping,
FromRow,
Builder,
TypeConversion,
}
#[derive(Debug, Clone)]
pub struct CloneOptimization {
pub variable: String,
pub location: usize,
pub reason: CloneEliminationReason,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CloneEliminationReason {
OnlyRead,
SingleUse,
LocalOnly,
CanMove,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OwnershipMode {
Owned,
Borrowed,
MutBorrowed,
}
pub(crate) struct ImplSelfFieldContext<'ast> {
pub impl_type_base: String,
program: *const Program<'ast>,
}
impl<'ast> ImplSelfFieldContext<'ast> {
pub(crate) fn new(impl_type_base: String, program: &Program<'ast>) -> Self {
Self {
impl_type_base,
program: std::ptr::from_ref(program),
}
}
pub(crate) fn program(&self) -> &'ast Program<'ast> {
unsafe { &*self.program }
}
}
pub struct Analyzer<'ast> {
#[allow(dead_code)]
variables: HashMap<String, OwnershipMode>,
copy_enums: HashSet<String>,
copy_structs: Arc<HashSet<String>>,
trait_definitions: HashMap<String, TraitDecl<'ast>>,
pub analyzed_trait_methods: HashMap<String, HashMap<String, AnalyzedFunction<'ast>>>,
mutated_variables: HashSet<String>,
current_impl_functions: Option<HashMap<String, crate::parser::ast::FunctionDecl<'ast>>>,
self_impl_context: Option<ImplSelfFieldContext<'ast>>,
global_struct_field_types: Arc<HashMap<String, HashMap<String, Type>>>,
struct_defining_module_paths: Arc<HashMap<String, Vec<Vec<String>>>>,
pub convergence_only: bool,
}
impl<'ast> Analyzer<'ast> {
pub(super) fn new_empty(global_copy_structs: HashSet<String>) -> Self {
Self::new_empty_shared(Arc::new(global_copy_structs))
}
pub(super) fn new_empty_shared(copy_structs: Arc<HashSet<String>>) -> Self {
Self {
variables: HashMap::new(),
copy_enums: HashSet::new(),
copy_structs,
trait_definitions: HashMap::new(),
analyzed_trait_methods: HashMap::new(),
mutated_variables: HashSet::new(),
current_impl_functions: None,
self_impl_context: None,
global_struct_field_types: Arc::new(HashMap::new()),
struct_defining_module_paths: Arc::new(HashMap::new()),
convergence_only: false,
}
}
pub fn analyze_program(
&mut self,
program: &Program<'ast>,
) -> Result<ProgramAnalysisResult<'ast>, String> {
self.check_forbidden_rust_patterns(program)?;
self.analyze_program_with_global_signatures(program, &SignatureRegistry::new())
}
pub fn check_forbidden_rust_patterns(&self, program: &Program<'ast>) -> Result<(), String> {
forbidden_patterns::check_forbidden_rust_patterns(program)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_copy_type_primitives() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Int));
assert!(analyzer.is_copy_type(&Type::Int32));
assert!(analyzer.is_copy_type(&Type::Uint));
assert!(analyzer.is_copy_type(&Type::Float));
assert!(analyzer.is_copy_type(&Type::Bool));
assert!(!analyzer.is_copy_type(&Type::String));
assert!(!analyzer.is_copy_type(&Type::Vec(Box::new(Type::Int))));
}
#[test]
fn test_is_copy_type_references() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Reference(Box::new(Type::Int))));
assert!(analyzer.is_copy_type(&Type::Reference(Box::new(Type::String))));
assert!(!analyzer.is_copy_type(&Type::MutableReference(Box::new(Type::Int))));
}
#[test]
fn test_is_mutating_method() {
let analyzer = Analyzer::new();
assert!(analyzer.is_mutating_method("push"));
assert!(analyzer.is_mutating_method("push_str"));
assert!(analyzer.is_mutating_method("pop"));
assert!(analyzer.is_mutating_method("insert"));
assert!(analyzer.is_mutating_method("remove"));
assert!(analyzer.is_mutating_method("clear"));
assert!(analyzer.is_mutating_method("append"));
assert!(analyzer.is_mutating_method("take"));
assert!(analyzer.is_mutating_method("replace"));
assert!(analyzer.is_mutating_method("get_or_insert"));
assert!(!analyzer.is_mutating_method("len"));
assert!(!analyzer.is_mutating_method("is_empty"));
assert!(!analyzer.is_mutating_method("get"));
assert!(!analyzer.is_mutating_method("iter"));
assert!(!analyzer.is_mutating_method("clone"));
}
#[test]
fn test_analyzer_tracks_mutated_variables() {
let mut analyzer = Analyzer::new();
assert!(!analyzer.is_variable_mutated("x"));
assert!(!analyzer.is_variable_mutated("y"));
analyzer.mutated_variables.insert("x".to_string());
assert!(analyzer.is_variable_mutated("x"));
assert!(!analyzer.is_variable_mutated("y"));
analyzer.mutated_variables.insert("y".to_string());
assert!(analyzer.is_variable_mutated("y"));
}
#[test]
fn test_ownership_mode_display() {
assert_eq!(format!("{:?}", OwnershipMode::Owned), "Owned");
assert_eq!(format!("{:?}", OwnershipMode::Borrowed), "Borrowed");
assert_eq!(format!("{:?}", OwnershipMode::MutBorrowed), "MutBorrowed");
}
#[test]
fn test_is_generic_type_param() {
assert!(Analyzer::is_generic_type_param(&Type::Custom(
"T".to_string()
)));
assert!(Analyzer::is_generic_type_param(&Type::Custom(
"U".to_string()
)));
assert!(Analyzer::is_generic_type_param(&Type::Custom(
"A".to_string()
)));
assert!(!Analyzer::is_generic_type_param(&Type::Custom(
"Point".to_string()
)));
assert!(!Analyzer::is_generic_type_param(&Type::Custom(
"Vec".to_string()
)));
assert!(!Analyzer::is_generic_type_param(&Type::Custom(
"Item".to_string()
)));
assert!(!Analyzer::is_generic_type_param(&Type::Int));
assert!(!Analyzer::is_generic_type_param(&Type::String));
}
#[test]
fn test_analyzer_new() {
let analyzer = Analyzer::new();
assert!(analyzer.mutated_variables.is_empty());
}
#[test]
fn test_is_copy_type_option() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Option(Box::new(Type::Int))));
assert!(analyzer.is_copy_type(&Type::Option(Box::new(Type::Bool))));
assert!(analyzer.is_copy_type(&Type::Option(Box::new(Type::Float))));
assert!(!analyzer.is_copy_type(&Type::Option(Box::new(Type::String))));
assert!(!analyzer.is_copy_type(&Type::Option(Box::new(Type::Vec(Box::new(Type::Int))))));
}
#[test]
fn test_is_copy_type_result() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Result(Box::new(Type::Int), Box::new(Type::Bool))));
assert!(!analyzer.is_copy_type(&Type::Result(Box::new(Type::String), Box::new(Type::Int))));
assert!(!analyzer.is_copy_type(&Type::Result(Box::new(Type::Int), Box::new(Type::String))));
}
#[test]
fn test_is_copy_type_array() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Array(Box::new(Type::Int), 10)));
assert!(analyzer.is_copy_type(&Type::Array(Box::new(Type::Bool), 5)));
assert!(!analyzer.is_copy_type(&Type::Array(Box::new(Type::String), 3)));
}
#[test]
fn test_is_copy_type_vec() {
let analyzer = Analyzer::new();
assert!(!analyzer.is_copy_type(&Type::Vec(Box::new(Type::Int))));
assert!(!analyzer.is_copy_type(&Type::Vec(Box::new(Type::Bool))));
}
#[test]
fn test_is_copy_type_tuple() {
let analyzer = Analyzer::new();
assert!(analyzer.is_copy_type(&Type::Tuple(vec![Type::Int, Type::Bool])));
assert!(analyzer.is_copy_type(&Type::Tuple(vec![Type::Float, Type::Uint])));
assert!(!analyzer.is_copy_type(&Type::Tuple(vec![Type::Int, Type::String])));
assert!(!analyzer.is_copy_type(&Type::Tuple(vec![Type::Vec(Box::new(Type::Int))])));
}
#[test]
fn test_ownership_mode_equality() {
assert_eq!(OwnershipMode::Owned, OwnershipMode::Owned);
assert_eq!(OwnershipMode::Borrowed, OwnershipMode::Borrowed);
assert_eq!(OwnershipMode::MutBorrowed, OwnershipMode::MutBorrowed);
assert_ne!(OwnershipMode::Owned, OwnershipMode::Borrowed);
assert_ne!(OwnershipMode::Borrowed, OwnershipMode::MutBorrowed);
}
}