diff --git a/src/analyzer/config/mod.rs b/src/analyzer/config/mod.rs index c237a23d..efef5c65 100644 --- a/src/analyzer/config/mod.rs +++ b/src/analyzer/config/mod.rs @@ -15,6 +15,7 @@ pub mod json_config; #[derive(Debug)] pub struct Config { pub migration_symbols: FxHashMap, + pub in_migration: bool, pub find_unused_expressions: bool, pub find_unused_definitions: bool, pub allowed_issues: Option>, @@ -78,6 +79,7 @@ impl Config { remove_fixmes: false, all_custom_issues, ast_diff: false, + in_migration: false, } } diff --git a/src/analyzer/custom_hook.rs b/src/analyzer/custom_hook.rs index dbc61005..65d27e0c 100644 --- a/src/analyzer/custom_hook.rs +++ b/src/analyzer/custom_hook.rs @@ -58,6 +58,10 @@ pub trait InternalHook { None } + fn get_codegen_name(&self) -> Option<&str> { + None + } + // This hook is run after analysing every top-level definition (class, function etc) #[allow(unused_variables)] fn after_def_analysis( @@ -133,6 +137,15 @@ pub trait InternalHook { ) -> Vec { vec![] } + + #[allow(unused_variables)] + fn do_codegen( + &self, + codebase: &CodebaseInfo, + interner: &Interner, + analysis_result: &AnalysisResult, + ) { + } } pub trait CustomHook: InternalHook + Send + Sync + core::fmt::Debug {} diff --git a/src/analyzer/expr/call/arguments_analyzer.rs b/src/analyzer/expr/call/arguments_analyzer.rs index e340a538..8cea858f 100644 --- a/src/analyzer/expr/call/arguments_analyzer.rs +++ b/src/analyzer/expr/call/arguments_analyzer.rs @@ -781,10 +781,7 @@ fn handle_closure_arg( if matches!( analysis_data.data_flow_graph.kind, GraphKind::WholeProgram(_) - ) || !statements_analyzer - .get_config() - .migration_symbols - .is_empty() + ) || !statements_analyzer.get_config().in_migration { if let FunctionLikeIdentifier::Function(function_name) = functionlike_id { match statements_analyzer.get_interner().lookup(function_name) { diff --git a/src/analyzer/functionlike_analyzer.rs b/src/analyzer/functionlike_analyzer.rs index a0432fa6..31cf011a 100644 --- a/src/analyzer/functionlike_analyzer.rs +++ b/src/analyzer/functionlike_analyzer.rs @@ -460,11 +460,7 @@ impl<'a> FunctionLikeAnalyzer<'a> { if let Some(parent_analysis_data) = &parent_analysis_data { analysis_data.type_variable_bounds = parent_analysis_data.type_variable_bounds.clone(); - if !statements_analyzer - .get_config() - .migration_symbols - .is_empty() - { + if statements_analyzer.get_config().in_migration { analysis_data.data_flow_graph = parent_analysis_data.data_flow_graph.clone(); } } @@ -791,6 +787,10 @@ impl<'a> FunctionLikeAnalyzer<'a> { *parent_analysis_data.issue_counts.entry(kind).or_insert(0) += count; } + if matches!(parent_analysis_data.migrate_function, None | Some(true)) { + parent_analysis_data.migrate_function = analysis_data.migrate_function; + } + if statements_analyzer.get_config().add_fixmes { parent_analysis_data .expr_fixme_positions diff --git a/src/cli/lib.rs b/src/cli/lib.rs index 3c99e369..830e0793 100644 --- a/src/cli/lib.rs +++ b/src/cli/lib.rs @@ -91,11 +91,7 @@ pub fn init( .multiple(true) .help("Only output issues of this/these type(s)"), ) - .arg( - arg!(--"all-issues") - .required(false) - .help("Show all issues"), - ) + .arg(arg!(--"all-issues").required(false).help("Show all issues")) .arg( arg!(--"ignore-mixed-issues") .required(false) @@ -163,6 +159,38 @@ pub fn init( .required(false) .help("How many threads to use"), ) + .arg( + arg!(--"filter" ) + .required(false) + .help("Filter the files that are analyzed"), + ) + .arg( + arg!(--"debug") + .required(false) + .help("Add output for debugging"), + ), + ) + .subcommand( + Command::new("codegen") + .about("Generates code for the named target") + .arg(arg!(--"root" ).required(false).help( + "The root directory that Hakana runs in. Defaults to the current directory", + )) + .arg( + arg!(--"config" ) + .required(false) + .help("Hakana config path — defaults to ./hakana.json"), + ) + .arg( + arg!(--"name" ) + .required(true) + .help("The codegen you want to perform"), + ) + .arg( + arg!(--"threads" ) + .required(false) + .help("How many threads to use"), + ) .arg( arg!(--"debug") .required(false) @@ -525,6 +553,19 @@ pub fn init( header, ); } + Some(("codegen", sub_matches)) => { + do_codegen( + sub_matches, + &root_dir, + all_custom_issues, + analysis_hooks, + config_path, + &cwd, + threads, + logger, + header, + ); + } Some(("add-fixmes", sub_matches)) => { do_add_fixmes( sub_matches, @@ -829,6 +870,7 @@ fn do_migrate( (first_part.to_string(), parts.join(",")) }) .collect(); + config.in_migration = true; } else { println!( "\nERROR: File {} does not exist or could not be read\n", @@ -887,6 +929,8 @@ fn do_migration_candidates( }) .collect(); + config.in_migration = true; + if config.hooks.is_empty() { println!("Migration {} not recognised", migration_name); exit(1); @@ -901,9 +945,11 @@ fn do_migration_candidates( let config = Arc::new(config); + let filter = sub_matches.value_of("filter").map(|f| f.to_string()); + let result = hakana_workhorse::scan_and_analyze( Vec::new(), - None, + filter, None, config.clone(), None, @@ -928,6 +974,68 @@ fn do_migration_candidates( } } +fn do_codegen( + sub_matches: &clap::ArgMatches, + root_dir: &str, + all_custom_issues: FxHashSet, + analysis_hooks: Vec>, + config_path: Option<&Path>, + cwd: &String, + threads: u8, + logger: Logger, + header: &str, +) { + let codegen_name = sub_matches.value_of("name").unwrap().to_string(); + + let mut config = config::Config::new(root_dir.to_string(), all_custom_issues); + config.hooks = analysis_hooks; + + let config_path = config_path.unwrap(); + + if config_path.exists() { + config.update_from_file(cwd, config_path).ok(); + } + config.allowed_issues = None; + + let config = Arc::new(config); + + let codegen_hook = if let Some(codegen) = config + .hooks + .iter() + .filter(|hook| { + if let Some(candidate_name) = hook.get_codegen_name() { + candidate_name == &codegen_name + } else { + false + } + }) + .next() + { + codegen + } else { + println!("Codegen {} not recognised", codegen_name); + exit(1); + }; + + let result = hakana_workhorse::scan_and_analyze( + Vec::new(), + None, + None, + config.clone(), + None, + threads, + Arc::new(logger), + header, + None, + None, + None, + ); + + if let Ok(result) = result { + codegen_hook.do_codegen(&result.1.codebase, &result.1.interner, &result.0); + } +} + fn do_find_paths( cwd: &String, all_custom_issues: FxHashSet, diff --git a/src/cli/test_runners/test_runner.rs b/src/cli/test_runners/test_runner.rs index cde0989d..1308cfa9 100644 --- a/src/cli/test_runners/test_runner.rs +++ b/src/cli/test_runners/test_runner.rs @@ -162,6 +162,7 @@ impl TestRunner { (first_part.to_string(), parts.join(",")) }) .collect(); + analysis_config.in_migration = true; } else if dir.contains("/fix/") { let issue_name = dir_parts.get(1).unwrap().to_string();