diff --git a/src/aast_utils/naming_visitor.rs b/src/aast_utils/naming_visitor.rs index b84d5421..78259aaf 100644 --- a/src/aast_utils/naming_visitor.rs +++ b/src/aast_utils/naming_visitor.rs @@ -284,7 +284,9 @@ impl<'ast> Visitor<'ast> for Scanner<'_> { // ); if nc.in_class_id || nc.in_function_id || nc.in_xhp_id || nc.in_constant_id { - if let std::collections::hash_map::Entry::Vacant(e) = self.resolved_names.entry(id.0.start_offset()) { + if let std::collections::hash_map::Entry::Vacant(e) = + self.resolved_names.entry(id.0.start_offset()) + { let resolved_name = if nc.in_xhp_id { nc.get_resolved_name( self.interner, diff --git a/src/analyzer/algebra_analyzer.rs b/src/analyzer/algebra_analyzer.rs index 4ed29fa9..0d765cfc 100644 --- a/src/analyzer/algebra_analyzer.rs +++ b/src/analyzer/algebra_analyzer.rs @@ -11,8 +11,8 @@ use oxidized::ast::Pos; use rustc_hash::FxHashSet; use crate::{ - scope_analyzer::ScopeAnalyzer, statements_analyzer::StatementsAnalyzer, - function_analysis_data::FunctionAnalysisData, + function_analysis_data::FunctionAnalysisData, scope_analyzer::ScopeAnalyzer, + statements_analyzer::StatementsAnalyzer, }; pub(crate) fn check_for_paradox( diff --git a/src/analyzer/expr/assertion_finder.rs b/src/analyzer/expr/assertion_finder.rs index 922b05ee..9d7d0a9a 100644 --- a/src/analyzer/expr/assertion_finder.rs +++ b/src/analyzer/expr/assertion_finder.rs @@ -166,13 +166,19 @@ fn process_custom_assertions( ) -> FxHashMap>> { let mut if_true_assertions = analysis_data .if_true_assertions - .get(&(conditional_pos.start_offset() as u32, conditional_pos.end_offset() as u32)) + .get(&( + conditional_pos.start_offset() as u32, + conditional_pos.end_offset() as u32, + )) .cloned() .unwrap_or(FxHashMap::default()); let if_false_assertions = analysis_data .if_false_assertions - .get(&(conditional_pos.start_offset() as u32, conditional_pos.end_offset() as u32)) + .get(&( + conditional_pos.start_offset() as u32, + conditional_pos.end_offset() as u32, + )) .cloned() .unwrap_or(FxHashMap::default()); @@ -216,7 +222,7 @@ fn get_is_assertions( ) { t } else { - return vec![] + return vec![]; }; if let Some((codebase, _)) = assertion_context.codebase { @@ -271,7 +277,10 @@ fn get_is_assertions( if let (Some(lhs_type), Some((codebase, interner))) = ( analysis_data .expr_types - .get(&(var_expr.1.start_offset() as u32, var_expr.1.end_offset() as u32)) + .get(&( + var_expr.1.start_offset() as u32, + var_expr.1.end_offset() as u32, + )) .clone(), assertion_context.codebase, ) { diff --git a/src/analyzer/expr/assignment/instance_property_assignment_analyzer.rs b/src/analyzer/expr/assignment/instance_property_assignment_analyzer.rs index 2320b5f7..3cfbadc0 100644 --- a/src/analyzer/expr/assignment/instance_property_assignment_analyzer.rs +++ b/src/analyzer/expr/assignment/instance_property_assignment_analyzer.rs @@ -162,7 +162,9 @@ pub(crate) fn analyze( } } - if let Some((property_id, invalid_class_property_type)) = invalid_assignment_value_types.iter().next() { + if let Some((property_id, invalid_class_property_type)) = + invalid_assignment_value_types.iter().next() + { analysis_data.maybe_add_issue( Issue::new( IssueKind::InvalidPropertyAssignmentValue, @@ -213,9 +215,10 @@ pub(crate) fn analyze_regular_assignment( context.inside_general_use = was_inside_general_use; - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_WRITE_PROPS); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_WRITE_PROPS, + ); let lhs_type = analysis_data.get_rc_expr_type(&stmt_var.pos()).cloned(); diff --git a/src/analyzer/expr/binop/arithmetic_analyzer.rs b/src/analyzer/expr/binop/arithmetic_analyzer.rs index 99bccd89..ed7b930b 100644 --- a/src/analyzer/expr/binop/arithmetic_analyzer.rs +++ b/src/analyzer/expr/binop/arithmetic_analyzer.rs @@ -249,10 +249,10 @@ pub(crate) fn assign_arithmetic_type( .data_flow_graph .add_node(decision_node.clone()); - if let Some(lhs_type) = analysis_data - .expr_types - .get(&(lhs_expr.1.start_offset() as u32, lhs_expr.1.end_offset() as u32)) - { + if let Some(lhs_type) = analysis_data.expr_types.get(&( + lhs_expr.1.start_offset() as u32, + lhs_expr.1.end_offset() as u32, + )) { cond_type.parent_nodes.insert(decision_node.clone()); for old_parent_node in &lhs_type.parent_nodes { @@ -266,10 +266,10 @@ pub(crate) fn assign_arithmetic_type( } } - if let Some(rhs_type) = analysis_data - .expr_types - .get(&(rhs_expr.1.start_offset() as u32, rhs_expr.1.end_offset() as u32)) - { + if let Some(rhs_type) = analysis_data.expr_types.get(&( + rhs_expr.1.start_offset() as u32, + rhs_expr.1.end_offset() as u32, + )) { cond_type.parent_nodes.insert(decision_node.clone()); for old_parent_node in &rhs_type.parent_nodes { diff --git a/src/analyzer/expr/call/argument_analyzer.rs b/src/analyzer/expr/call/argument_analyzer.rs index 2a8ca5a3..a385c76a 100644 --- a/src/analyzer/expr/call/argument_analyzer.rs +++ b/src/analyzer/expr/call/argument_analyzer.rs @@ -737,25 +737,26 @@ fn add_dataflow( }; // TODO add plugin hooks for adding/removing taints - let argument_value_node = if data_flow_graph.kind == GraphKind::FunctionBody { - DataFlowNode { - id: "call to ".to_string() - + functionlike_id - .to_string(statements_analyzer.get_interner()) - .as_str(), - kind: DataFlowNodeKind::VariableUseSink { - pos: statements_analyzer.get_hpos(input_expr.pos()), - }, - } - } else { - DataFlowNode::get_for_assignment( - "call to ".to_string() - + functionlike_id - .to_string(statements_analyzer.get_interner()) - .as_str(), - statements_analyzer.get_hpos(input_expr.pos()), - ) - }; + let argument_value_node = + if data_flow_graph.kind == GraphKind::FunctionBody && context.inside_general_use { + DataFlowNode { + id: "call to ".to_string() + + functionlike_id + .to_string(statements_analyzer.get_interner()) + .as_str(), + kind: DataFlowNodeKind::VariableUseSink { + pos: statements_analyzer.get_hpos(input_expr.pos()), + }, + } + } else { + DataFlowNode::get_for_assignment( + "call to ".to_string() + + functionlike_id + .to_string(statements_analyzer.get_interner()) + .as_str(), + statements_analyzer.get_hpos(input_expr.pos()), + ) + }; for parent_node in &input_type.parent_nodes { data_flow_graph.add_path( @@ -771,9 +772,15 @@ fn add_dataflow( ); } - if data_flow_graph.kind == GraphKind::FunctionBody { - data_flow_graph.add_node(argument_value_node); - } else { + data_flow_graph.add_path( + &argument_value_node, + &method_node, + PathKind::Default, + None, + None, + ); + + if matches!(data_flow_graph.kind, GraphKind::WholeProgram(_)) { let mut taints = get_argument_taints( functionlike_id, argument_offset, @@ -784,16 +791,6 @@ fn add_dataflow( taints.extend(sinks.clone()); } - data_flow_graph.add_node(argument_value_node.clone()); - - data_flow_graph.add_path( - &argument_value_node, - &method_node, - PathKind::Default, - None, - None, - ); - if !taints.is_empty() { let method_node_sink = DataFlowNode { id: method_node.get_id().clone(), @@ -809,6 +806,8 @@ fn add_dataflow( data_flow_graph.add_node(method_node); } + + data_flow_graph.add_node(argument_value_node); } pub(crate) fn get_removed_taints_in_comments( diff --git a/src/analyzer/expr/call/arguments_analyzer.rs b/src/analyzer/expr/call/arguments_analyzer.rs index 567b6b98..a72533c3 100644 --- a/src/analyzer/expr/call/arguments_analyzer.rs +++ b/src/analyzer/expr/call/arguments_analyzer.rs @@ -22,7 +22,7 @@ use hakana_reflection_info::classlike_info::ClassLikeInfo; use hakana_reflection_info::codebase_info::CodebaseInfo; use hakana_reflection_info::data_flow::graph::GraphKind; use hakana_reflection_info::functionlike_identifier::FunctionLikeIdentifier; -use hakana_reflection_info::functionlike_info::FunctionLikeInfo; +use hakana_reflection_info::functionlike_info::{FnEffect, FunctionLikeInfo}; use hakana_reflection_info::functionlike_parameter::{DefaultType, FunctionLikeParameter}; use hakana_reflection_info::t_atomic::TAtomic; use hakana_reflection_info::t_union::{populate_union_type, TUnion}; @@ -175,7 +175,9 @@ pub(crate) fn check_arguments_match( for (_, arg_expr) in args.iter() { let was_inside_call = context.inside_general_use; - context.inside_general_use = true; + if matches!(functionlike_info.effects, FnEffect::Some(_)) { + context.inside_general_use = true; + } // don't analyse closures here if !matches!(arg_expr.2, aast::Expr_::Lfun(_) | aast::Expr_::Efun(_)) { @@ -407,6 +409,12 @@ pub(crate) fn check_arguments_match( continue; }; + let was_inside_call = context.inside_general_use; + + if matches!(functionlike_info.effects, FnEffect::Some(_)) { + context.inside_general_use = true; + } + argument_analyzer::check_argument_matches( statements_analyzer, functionlike_id, @@ -425,6 +433,10 @@ pub(crate) fn check_arguments_match( function_name_pos, ); + if !was_inside_call { + context.inside_general_use = false; + } + if let GraphKind::WholeProgram(_) = &analysis_data.data_flow_graph.kind { if let Some(removed_taints) = &function_param.removed_taints_when_returning_true { if let Some(expr_var_id) = expression_identifier::get_var_id( @@ -473,6 +485,12 @@ pub(crate) fn check_arguments_match( get_mixed_any() }; + let was_inside_call = context.inside_general_use; + + if matches!(functionlike_info.effects, FnEffect::Some(_)) { + context.inside_general_use = true; + } + argument_analyzer::check_argument_matches( statements_analyzer, functionlike_id, @@ -490,6 +508,8 @@ pub(crate) fn check_arguments_match( function_call_pos, function_name_pos, ); + + context.inside_general_use = was_inside_call; } } } diff --git a/src/analyzer/expr/call/atomic_static_call_analyzer.rs b/src/analyzer/expr/call/atomic_static_call_analyzer.rs index 7e66fc61..97907e7c 100644 --- a/src/analyzer/expr/call/atomic_static_call_analyzer.rs +++ b/src/analyzer/expr/call/atomic_static_call_analyzer.rs @@ -159,7 +159,8 @@ pub(crate) fn analyze( &method_name.unwrap(), (expr.2, expr.3, expr.4), lhs_type_part, - pos,Some(&expr.1.0), + pos, + Some(&expr.1 .0), analysis_data, context, if_body_context, diff --git a/src/analyzer/expr/call/new_analyzer.rs b/src/analyzer/expr/call/new_analyzer.rs index 16c0fd99..bf8b41cd 100644 --- a/src/analyzer/expr/call/new_analyzer.rs +++ b/src/analyzer/expr/call/new_analyzer.rs @@ -230,9 +230,10 @@ fn analyze_atomic( match statements_analyzer.get_interner().lookup(&classlike_name) { "ReflectionClass" | "ReflectionTypeAlias" => { - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_WRITE_GLOBALS); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_WRITE_GLOBALS, + ); } _ => {} } diff --git a/src/analyzer/expr/call_analyzer.rs b/src/analyzer/expr/call_analyzer.rs index 6f96b093..142358fd 100644 --- a/src/analyzer/expr/call_analyzer.rs +++ b/src/analyzer/expr/call_analyzer.rs @@ -65,7 +65,13 @@ pub(crate) fn analyze( ast_defs::PropOrMethod::IsMethod => { return instance_call_analyzer::analyze( statements_analyzer, - (lhs_expr, rhs_expr, &expr.targs, &expr.args, &expr.unpacked_arg), + ( + lhs_expr, + rhs_expr, + &expr.targs, + &expr.args, + &expr.unpacked_arg, + ), &pos, analysis_data, context, @@ -91,7 +97,13 @@ pub(crate) fn analyze( return static_call_analyzer::analyze( statements_analyzer, - (class_id, rhs_expr, &expr.targs, &expr.args, &expr.unpacked_arg), + ( + class_id, + rhs_expr, + &expr.targs, + &expr.args, + &expr.unpacked_arg, + ), &pos, analysis_data, context, @@ -339,7 +351,7 @@ pub(crate) fn check_method_args( if_body_context, template_result, pos, - method_name_pos + method_name_pos, )?; apply_effects(functionlike_storage, analysis_data, pos, &call_expr.1); @@ -358,26 +370,28 @@ pub(crate) fn apply_effects( expr_args: &Vec<(ast_defs::ParamKind, aast::Expr<(), ()>)>, ) { if function_storage.name == STR_ASIO_JOIN { - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_IMPURE); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_IMPURE, + ); return; } match function_storage.effects { FnEffect::Some(stored_effects) => { if stored_effects > EFFECT_WRITE_PROPS { - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), stored_effects); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + stored_effects, + ); } } FnEffect::Arg(arg_offset) => { if let Some((_, arg_expr)) = expr_args.get(arg_offset as usize) { - if let Some(arg_type) = analysis_data - .expr_types - .get(&(arg_expr.pos().start_offset() as u32, arg_expr.pos().end_offset() as u32)) - { + if let Some(arg_type) = analysis_data.expr_types.get(&( + arg_expr.pos().start_offset() as u32, + arg_expr.pos().end_offset() as u32, + )) { for arg_atomic_type in &arg_type.types { if let TAtomic::TClosure { effects, .. } = arg_atomic_type { if let Some(evaluated_effects) = effects { @@ -386,9 +400,10 @@ pub(crate) fn apply_effects( *evaluated_effects, ); } else { - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_IMPURE); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_IMPURE, + ); } } } diff --git a/src/analyzer/expr/echo_analyzer.rs b/src/analyzer/expr/echo_analyzer.rs index 348445c2..eee880f1 100644 --- a/src/analyzer/expr/echo_analyzer.rs +++ b/src/analyzer/expr/echo_analyzer.rs @@ -39,6 +39,8 @@ pub(crate) fn analyze( let arg_type = analysis_data.get_rc_expr_type(arg_expr.pos()).cloned(); + context.inside_general_use = true; + argument_analyzer::verify_type( statements_analyzer, &arg_type.unwrap_or(Rc::new(get_mixed_any())), @@ -54,6 +56,8 @@ pub(crate) fn analyze( true, call_pos, ); + + context.inside_general_use = false; } analysis_data.expr_effects.insert( diff --git a/src/analyzer/expr/exit_analyzer.rs b/src/analyzer/expr/exit_analyzer.rs index 2f9e8741..11ee489e 100644 --- a/src/analyzer/expr/exit_analyzer.rs +++ b/src/analyzer/expr/exit_analyzer.rs @@ -43,6 +43,8 @@ pub(crate) fn analyze( // TODO handle exit taint sink + context.inside_general_use = true; + argument_analyzer::verify_type( statements_analyzer, &arg_type.unwrap_or(Rc::new(get_mixed_any())), @@ -60,6 +62,8 @@ pub(crate) fn analyze( true, call_pos, ); + + context.inside_general_use = false; } analysis_data.set_expr_type(&call_pos, get_nothing()); diff --git a/src/analyzer/expr/fetch/array_fetch_analyzer.rs b/src/analyzer/expr/fetch/array_fetch_analyzer.rs index af970236..3ab9f28f 100644 --- a/src/analyzer/expr/fetch/array_fetch_analyzer.rs +++ b/src/analyzer/expr/fetch/array_fetch_analyzer.rs @@ -182,10 +182,10 @@ pub(crate) fn add_array_fetch_dataflow( } } - if let Some(stmt_var_type) = analysis_data - .expr_types - .get(&(array_expr_pos.start_offset() as u32, array_expr_pos.end_offset() as u32)) - { + if let Some(stmt_var_type) = analysis_data.expr_types.get(&( + array_expr_pos.start_offset() as u32, + array_expr_pos.end_offset() as u32, + )) { if !stmt_var_type.parent_nodes.is_empty() { // TODO Add events dispatchers diff --git a/src/analyzer/expr/fetch/static_property_fetch_analyzer.rs b/src/analyzer/expr/fetch/static_property_fetch_analyzer.rs index 8525e045..cfa7a2b5 100644 --- a/src/analyzer/expr/fetch/static_property_fetch_analyzer.rs +++ b/src/analyzer/expr/fetch/static_property_fetch_analyzer.rs @@ -92,9 +92,10 @@ pub(crate) fn analyze( return Ok(()); } - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_READ_PROPS); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_READ_PROPS, + ); analysis_data.set_expr_type(&stmt_class.1, get_named_object(classlike_name.clone())); diff --git a/src/analyzer/expr/include_analyzer.rs b/src/analyzer/expr/include_analyzer.rs index d0335b12..41dc26b6 100644 --- a/src/analyzer/expr/include_analyzer.rs +++ b/src/analyzer/expr/include_analyzer.rs @@ -1,7 +1,7 @@ use crate::expression_analyzer; +use crate::function_analysis_data::FunctionAnalysisData; use crate::scope_context::ScopeContext; use crate::statements_analyzer::StatementsAnalyzer; -use crate::function_analysis_data::FunctionAnalysisData; use crate::stmt_analyzer::AnalysisError; use hakana_reflection_info::code_location::HPos; use hakana_reflection_info::function_context::FunctionLikeIdentifier; @@ -30,6 +30,8 @@ pub(crate) fn analyze( let arg_type = analysis_data.get_expr_type(expr.pos()).cloned(); + context.inside_general_use = true; + argument_analyzer::verify_type( statements_analyzer, &arg_type.unwrap_or(get_mixed_any()), @@ -46,7 +48,9 @@ pub(crate) fn analyze( call_pos, ); + context.inside_general_use = false; + // TODO handle mutations - Ok(()) + Ok(()) } diff --git a/src/analyzer/expr/isset_analyzer.rs b/src/analyzer/expr/isset_analyzer.rs index 2aaae8f6..4dad89f6 100644 --- a/src/analyzer/expr/isset_analyzer.rs +++ b/src/analyzer/expr/isset_analyzer.rs @@ -1,7 +1,7 @@ -use crate::{expression_analyzer, stmt_analyzer::AnalysisError}; +use crate::function_analysis_data::FunctionAnalysisData; use crate::scope_context::ScopeContext; use crate::statements_analyzer::StatementsAnalyzer; -use crate::function_analysis_data::FunctionAnalysisData; +use crate::{expression_analyzer, stmt_analyzer::AnalysisError}; use hakana_type::get_bool; use oxidized::{aast, ast::Pos}; diff --git a/src/analyzer/expr/prefixed_string_analyzer.rs b/src/analyzer/expr/prefixed_string_analyzer.rs index 3d795bbc..04af5955 100644 --- a/src/analyzer/expr/prefixed_string_analyzer.rs +++ b/src/analyzer/expr/prefixed_string_analyzer.rs @@ -41,10 +41,10 @@ pub(crate) fn analyze( if_body_context, )?; - let inner_type = if let Some(t) = analysis_data - .expr_types - .get(&(boxed.1.pos().start_offset() as u32, boxed.1.pos().end_offset() as u32)) - { + let inner_type = if let Some(t) = analysis_data.expr_types.get(&( + boxed.1.pos().start_offset() as u32, + boxed.1.pos().end_offset() as u32, + )) { (**t).clone() } else { get_string() diff --git a/src/analyzer/expr/unop_analyzer.rs b/src/analyzer/expr/unop_analyzer.rs index aab97746..56c0a3b1 100644 --- a/src/analyzer/expr/unop_analyzer.rs +++ b/src/analyzer/expr/unop_analyzer.rs @@ -35,7 +35,10 @@ pub(crate) fn analyze( (pos.start_offset() as u32, pos.end_offset() as u32), *analysis_data .expr_effects - .get(&(expr.1.pos().start_offset() as u32, expr.1.pos().end_offset() as u32)) + .get(&( + expr.1.pos().start_offset() as u32, + expr.1.pos().end_offset() as u32, + )) .unwrap_or(&0), ); diff --git a/src/analyzer/expr/variable_fetch_analyzer.rs b/src/analyzer/expr/variable_fetch_analyzer.rs index de22bef3..c57ee652 100644 --- a/src/analyzer/expr/variable_fetch_analyzer.rs +++ b/src/analyzer/expr/variable_fetch_analyzer.rs @@ -60,9 +60,10 @@ pub(crate) fn analyze( analysis_data.set_rc_expr_type(&pos, superglobal_type); - analysis_data - .expr_effects - .insert((pos.start_offset() as u32, pos.end_offset() as u32), EFFECT_READ_GLOBALS); + analysis_data.expr_effects.insert( + (pos.start_offset() as u32, pos.end_offset() as u32), + EFFECT_READ_GLOBALS, + ); } else if let Some(var_type) = context.vars_in_scope.get(&lid.1 .1) { let mut var_type = (**var_type).clone(); diff --git a/src/analyzer/scope_context/mod.rs b/src/analyzer/scope_context/mod.rs index f7d8dcb0..cc14bcf6 100644 --- a/src/analyzer/scope_context/mod.rs +++ b/src/analyzer/scope_context/mod.rs @@ -9,8 +9,8 @@ use oxidized::ast_defs::Pos; use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - reconciler::assertion_reconciler, statements_analyzer::StatementsAnalyzer, - stmt::control_analyzer::BreakContext, function_analysis_data::FunctionAnalysisData, + function_analysis_data::FunctionAnalysisData, reconciler::assertion_reconciler, + statements_analyzer::StatementsAnalyzer, stmt::control_analyzer::BreakContext, }; pub mod control_action; diff --git a/src/analyzer/stmt/break_analyzer.rs b/src/analyzer/stmt/break_analyzer.rs index 224c9524..81116b63 100644 --- a/src/analyzer/stmt/break_analyzer.rs +++ b/src/analyzer/stmt/break_analyzer.rs @@ -1,11 +1,13 @@ use std::rc::Rc; use super::control_analyzer::BreakContext; +use crate::{ + function_analysis_data::FunctionAnalysisData, statements_analyzer::StatementsAnalyzer, +}; use crate::{ scope_analyzer::ScopeAnalyzer, scope_context::{control_action::ControlAction, loop_scope::LoopScope, ScopeContext}, }; -use crate::{statements_analyzer::StatementsAnalyzer, function_analysis_data::FunctionAnalysisData}; use hakana_type::{combine_optional_union_types, combine_union_types}; use rustc_hash::FxHashMap; diff --git a/src/analyzer/stmt/continue_analyzer.rs b/src/analyzer/stmt/continue_analyzer.rs index 2a9c50a0..d18c99e4 100644 --- a/src/analyzer/stmt/continue_analyzer.rs +++ b/src/analyzer/stmt/continue_analyzer.rs @@ -8,7 +8,9 @@ use crate::{ scope_context::{control_action::ControlAction, loop_scope::LoopScope, ScopeContext}, }; -use crate::{statements_analyzer::StatementsAnalyzer, function_analysis_data::FunctionAnalysisData}; +use crate::{ + function_analysis_data::FunctionAnalysisData, statements_analyzer::StatementsAnalyzer, +}; pub(crate) fn analyze( statements_analyzer: &StatementsAnalyzer, diff --git a/src/analyzer/stmt/loop_/assignment_map_visitor.rs b/src/analyzer/stmt/loop_/assignment_map_visitor.rs index 222017de..c042856e 100644 --- a/src/analyzer/stmt/loop_/assignment_map_visitor.rs +++ b/src/analyzer/stmt/loop_/assignment_map_visitor.rs @@ -126,10 +126,10 @@ impl<'ast> Visitor<'ast> for Scanner { } } - if let aast::Expr_::Id(_) = &boxed.func .2 { + if let aast::Expr_::Id(_) = &boxed.func.2 { // do nothing } else { - match &boxed.func .2 { + match &boxed.func.2 { aast::Expr_::ObjGet(boxed) => { let (lhs_expr, _, _, prop_or_method) = (&boxed.0, &boxed.1, &boxed.2, &boxed.3); diff --git a/src/analyzer/stmt/while_analyzer.rs b/src/analyzer/stmt/while_analyzer.rs index 85701a9f..6fec6398 100644 --- a/src/analyzer/stmt/while_analyzer.rs +++ b/src/analyzer/stmt/while_analyzer.rs @@ -3,7 +3,8 @@ use crate::{ function_analysis_data::FunctionAnalysisData, scope_analyzer::ScopeAnalyzer, scope_context::{control_action::ControlAction, loop_scope::LoopScope, ScopeContext}, - statements_analyzer::StatementsAnalyzer, stmt_analyzer::AnalysisError, + statements_analyzer::StatementsAnalyzer, + stmt_analyzer::AnalysisError, }; use oxidized::{aast, ast_defs}; use std::rc::Rc; diff --git a/src/analyzer/stmt_analyzer.rs b/src/analyzer/stmt_analyzer.rs index 901a9af8..253abc06 100644 --- a/src/analyzer/stmt_analyzer.rs +++ b/src/analyzer/stmt_analyzer.rs @@ -232,7 +232,7 @@ pub(crate) fn analyze( ); return Err(AnalysisError::UserError); } - aast::Stmt_::DeclareLocal(_) => {}, + aast::Stmt_::DeclareLocal(_) => {} aast::Stmt_::Concurrent(boxed) => { for boxed_stmt in &boxed.0 { analyze( @@ -243,7 +243,7 @@ pub(crate) fn analyze( loop_scope, )?; } - }, + } } context.cond_referenced_var_ids = FxHashSet::default(); diff --git a/src/cli/lib.rs b/src/cli/lib.rs index 85de6128..15162edc 100644 --- a/src/cli/lib.rs +++ b/src/cli/lib.rs @@ -1221,7 +1221,9 @@ fn write_output_files( fn update_files(analysis_result: &mut AnalysisResult, root_dir: &String, interner: &Interner) { let mut replacement_and_insertion_keys = analysis_result - .replacements.keys().copied() + .replacements + .keys() + .copied() .collect::>(); replacement_and_insertion_keys.extend(analysis_result.insertions.keys().copied()); diff --git a/src/code_info/analysis_result.rs b/src/code_info/analysis_result.rs index c6afb841..46f8a5a2 100644 --- a/src/code_info/analysis_result.rs +++ b/src/code_info/analysis_result.rs @@ -60,10 +60,7 @@ impl AnalysisResult { self.replacements.extend(other.replacements); self.insertions.extend(other.insertions); for (id, c) in other.mixed_source_counts { - self.mixed_source_counts - .entry(id) - .or_default() - .extend(c); + self.mixed_source_counts.entry(id).or_default().extend(c); } self.program_dataflow_graph .add_graph(other.program_dataflow_graph); diff --git a/src/code_info/codebase_info/symbols.rs b/src/code_info/codebase_info/symbols.rs index e867b791..6ba31368 100644 --- a/src/code_info/codebase_info/symbols.rs +++ b/src/code_info/codebase_info/symbols.rs @@ -37,28 +37,23 @@ impl Symbols { self.all.insert(*fq_class_name, SymbolKind::Class); if let Some(file_path) = file_path { - self.classlike_files - .insert(*fq_class_name, file_path); + self.classlike_files.insert(*fq_class_name, file_path); } } pub fn add_enum_class_name(&mut self, fq_class_name: &StrId, file_path: Option) { - self.all - .insert(*fq_class_name, SymbolKind::EnumClass); + self.all.insert(*fq_class_name, SymbolKind::EnumClass); if let Some(file_path) = file_path { - self.classlike_files - .insert(*fq_class_name, file_path); + self.classlike_files.insert(*fq_class_name, file_path); } } pub fn add_interface_name(&mut self, fq_class_name: &StrId, file_path: Option) { - self.all - .insert(*fq_class_name, SymbolKind::Interface); + self.all.insert(*fq_class_name, SymbolKind::Interface); if let Some(file_path) = file_path { - self.classlike_files - .insert(*fq_class_name, file_path); + self.classlike_files.insert(*fq_class_name, file_path); } } @@ -66,8 +61,7 @@ impl Symbols { self.all.insert(*fq_class_name, SymbolKind::Trait); if let Some(file_path) = file_path { - self.classlike_files - .insert(*fq_class_name, file_path); + self.classlike_files.insert(*fq_class_name, file_path); } } @@ -75,8 +69,7 @@ impl Symbols { self.all.insert(*fq_class_name, SymbolKind::Enum); if let Some(file_path) = file_path { - self.classlike_files - .insert(*fq_class_name, file_path); + self.classlike_files.insert(*fq_class_name, file_path); } } diff --git a/src/code_info/data_flow/graph.rs b/src/code_info/data_flow/graph.rs index 3e08d5f3..6182afb4 100644 --- a/src/code_info/data_flow/graph.rs +++ b/src/code_info/data_flow/graph.rs @@ -131,18 +131,12 @@ impl DataFlowGraph { } for (key, edges) in graph.forward_edges { - self.forward_edges - .entry(key) - .or_default() - .extend(edges); + self.forward_edges.entry(key).or_default().extend(edges); } if self.kind == GraphKind::FunctionBody { for (key, edges) in graph.backward_edges { - self.backward_edges - .entry(key) - .or_default() - .extend(edges); + self.backward_edges.entry(key).or_default().extend(edges); } for (key, count) in graph.mixed_source_counts { if let Some(existing_count) = self.mixed_source_counts.get_mut(&key) { diff --git a/src/code_info/data_flow/mod.rs b/src/code_info/data_flow/mod.rs index 1f2da3c9..24456d43 100644 --- a/src/code_info/data_flow/mod.rs +++ b/src/code_info/data_flow/mod.rs @@ -2,4 +2,3 @@ pub mod graph; pub mod node; pub mod path; pub mod tainted_node; - diff --git a/src/code_info/functionlike_identifier.rs b/src/code_info/functionlike_identifier.rs index 21c22ef8..67f5999c 100644 --- a/src/code_info/functionlike_identifier.rs +++ b/src/code_info/functionlike_identifier.rs @@ -11,10 +11,7 @@ pub enum FunctionLikeIdentifier { impl FunctionLikeIdentifier { pub fn as_method_identifier(&self) -> Option { if let FunctionLikeIdentifier::Method(fq_classlike_name, method_name) = &self { - Some(MethodIdentifier( - *fq_classlike_name, - *method_name, - )) + Some(MethodIdentifier(*fq_classlike_name, *method_name)) } else { None } diff --git a/src/code_info/taint.rs b/src/code_info/taint.rs index abd40998..0f2cb53b 100644 --- a/src/code_info/taint.rs +++ b/src/code_info/taint.rs @@ -26,8 +26,9 @@ impl SourceType { } } -#[derive(Clone, PartialEq, Eq, Hash, Display, Debug, Serialize, Deserialize, EnumString)] -#[derive(Default)] +#[derive( + Clone, PartialEq, Eq, Hash, Display, Debug, Serialize, Deserialize, EnumString, Default, +)] pub enum SinkType { #[default] HtmlTag, @@ -46,8 +47,6 @@ pub enum SinkType { Custom(String), } - - const PAIRS: [(SourceType, SinkType); 31] = [ // All the places we don't want GET data to go (SourceType::UriRequestHeader, SinkType::Sql), diff --git a/src/code_info_builder/classlike_scanner.rs b/src/code_info_builder/classlike_scanner.rs index 1cc38792..1299a9ea 100644 --- a/src/code_info_builder/classlike_scanner.rs +++ b/src/code_info_builder/classlike_scanner.rs @@ -206,16 +206,13 @@ pub(crate) fn scan( if let oxidized::tast::Hint_::Happly(name, params) = &*extended_interface.1 { signature_end = name.0.end_offset() as u32; - let interface_name = - *resolved_names.get(&name.0.start_offset()).unwrap(); + let interface_name = *resolved_names.get(&name.0.start_offset()).unwrap(); if !params.is_empty() { signature_end = params.last().unwrap().0.end_offset() as u32; } - storage - .direct_class_interfaces - .insert(interface_name); + storage.direct_class_interfaces.insert(interface_name); storage.all_class_interfaces.insert(interface_name); if class_name == &STR_SIMPLE_XML_ELEMENT && interface_name == STR_TRAVERSABLE { @@ -352,16 +349,13 @@ pub(crate) fn scan( if let oxidized::tast::Hint_::Happly(name, params) = &*extended_interface.1 { signature_end = name.0.end_offset() as u32; - let interface_name = - *resolved_names.get(&name.0.start_offset()).unwrap(); + let interface_name = *resolved_names.get(&name.0.start_offset()).unwrap(); if !params.is_empty() { signature_end = params.last().unwrap().0.end_offset() as u32; } - storage - .direct_class_interfaces - .insert(interface_name); + storage.direct_class_interfaces.insert(interface_name); storage.all_class_interfaces.insert(interface_name); storage.template_extended_offsets.insert( @@ -515,13 +509,9 @@ pub(crate) fn scan( .wrapping_add(hasher.finish()); if let Some(type_params) = type_params { - storage.template_extended_offsets.insert( - name, - type_params - .into_iter() - .map(Arc::new) - .collect(), - ); + storage + .template_extended_offsets + .insert(name, type_params.into_iter().map(Arc::new).collect()); } } } diff --git a/src/code_info_builder/functionlike_scanner.rs b/src/code_info_builder/functionlike_scanner.rs index c0965926..d74a10d8 100644 --- a/src/code_info_builder/functionlike_scanner.rs +++ b/src/code_info_builder/functionlike_scanner.rs @@ -178,8 +178,7 @@ pub(crate) fn get_functionlike( all_custom_issues, ); - let mut functionlike_info = - FunctionLikeInfo::new(name, definition_location, meta_start); + let mut functionlike_info = FunctionLikeInfo::new(name, definition_location, meta_start); let mut template_supers = FxHashMap::default(); @@ -649,7 +648,11 @@ fn convert_param_nodes( None }; param.is_inout = matches!(param_node.callconv, ast_defs::ParamKind::Pinout(_)); - param.signature_type_location = param_node.type_hint.1.as_ref().map(|param_type| HPos::new(¶m_type.0, file_source.file_path, None)); + param.signature_type_location = param_node + .type_hint + .1 + .as_ref() + .map(|param_type| HPos::new(¶m_type.0, file_source.file_path, None)); for user_attribute in ¶m_node.user_attributes { let name = resolved_names .get(&user_attribute.name.0.start_offset()) diff --git a/src/code_info_builder/lib.rs b/src/code_info_builder/lib.rs index b11c0b8c..618fe836 100644 --- a/src/code_info_builder/lib.rs +++ b/src/code_info_builder/lib.rs @@ -126,10 +126,7 @@ impl<'ast> Visitor<'ast> for Scanner<'_> { } fn visit_gconst(&mut self, c: &mut Context, gc: &aast::Gconst<(), ()>) -> Result<(), ()> { - let name = *self - .resolved_names - .get(&gc.name.0.start_offset()) - .unwrap(); + let name = *self.resolved_names.get(&gc.name.0.start_offset()).unwrap(); self.codebase .const_files @@ -475,8 +472,7 @@ impl<'ast> Visitor<'ast> for Scanner<'_> { &m.tparams, &m.params, &m.ret, - self - .uses + self.uses .symbol_member_uses .get(&(c.classlike_name.unwrap(), c.member_name.unwrap())) .unwrap_or(&vec![]), @@ -541,10 +537,7 @@ impl<'ast> Visitor<'ast> for Scanner<'_> { } fn visit_fun_def(&mut self, c: &mut Context, f: &aast::FunDef<(), ()>) -> Result<(), ()> { - let name = *self - .resolved_names - .get(&f.name.0.start_offset()) - .unwrap(); + let name = *self.resolved_names.get(&f.name.0.start_offset()).unwrap(); let functionlike_storage = self.visit_function( false, diff --git a/src/code_info_builder/typehint_resolver.rs b/src/code_info_builder/typehint_resolver.rs index 2a0b07d6..62a012e5 100644 --- a/src/code_info_builder/typehint_resolver.rs +++ b/src/code_info_builder/typehint_resolver.rs @@ -251,7 +251,8 @@ fn get_function_type_from_hints( classlike_name, type_context, resolved_names, - ).map(Box::new), + ) + .map(Box::new), is_variadic: false, is_optional: false, } @@ -266,7 +267,8 @@ fn get_function_type_from_hints( classlike_name, type_context, resolved_names, - ).map(Box::new), + ) + .map(Box::new), is_variadic: true, is_optional: false, }; @@ -281,7 +283,8 @@ fn get_function_type_from_hints( classlike_name, type_context, resolved_names, - ).map(Box::new), + ) + .map(Box::new), effects: if let Some(contexts) = &function_info.ctxs { Some(if contexts.1.is_empty() { EFFECT_PURE diff --git a/src/ttype/type_comparator/dict_type_comparator.rs b/src/ttype/type_comparator/dict_type_comparator.rs index 73b33784..943c7b87 100644 --- a/src/ttype/type_comparator/dict_type_comparator.rs +++ b/src/ttype/type_comparator/dict_type_comparator.rs @@ -135,7 +135,10 @@ pub(crate) fn is_contained_by( ) { all_types_contain = false; - update_failed_result_from_nested(atomic_comparison_result, nested_comparison_result); + update_failed_result_from_nested( + atomic_comparison_result, + nested_comparison_result, + ); } let mut nested_comparison_result = TypeComparisonResult::new(); @@ -151,7 +154,10 @@ pub(crate) fn is_contained_by( ) { all_types_contain = false; - update_failed_result_from_nested(atomic_comparison_result, nested_comparison_result); + update_failed_result_from_nested( + atomic_comparison_result, + nested_comparison_result, + ); } } } else { diff --git a/tests/unused/UnusedExpression/cFirstUnused/input.hack b/tests/unused/UnusedExpression/cFirstUnused/input.hack new file mode 100644 index 00000000..e08a3cf0 --- /dev/null +++ b/tests/unused/UnusedExpression/cFirstUnused/input.hack @@ -0,0 +1,6 @@ +function foo(vec $v): void { + $c = \HH\Lib\C\first($v); + $d = vec[0, 1, 2]; + $e = \HH\Lib\C\last($d); + echo $e; +} \ No newline at end of file diff --git a/tests/unused/UnusedExpression/cFirstUnused/output.txt b/tests/unused/UnusedExpression/cFirstUnused/output.txt new file mode 100644 index 00000000..0d4183ae --- /dev/null +++ b/tests/unused/UnusedExpression/cFirstUnused/output.txt @@ -0,0 +1,2 @@ +ERROR: UnusedParameter - input.hack:1:13 - Unused param $v +ERROR: UnusedAssignmentStatement - input.hack:2:5 - Assignment to $c is unused, and this expression has no effect \ No newline at end of file diff --git a/tests/unused/UnusedExpression/usedAwaitablePipedToJoin/output.txt b/tests/unused/UnusedExpression/usedAwaitablePipedToJoin/output.txt index 67820ca3..a760b749 100644 --- a/tests/unused/UnusedExpression/usedAwaitablePipedToJoin/output.txt +++ b/tests/unused/UnusedExpression/usedAwaitablePipedToJoin/output.txt @@ -1,4 +1,7 @@ ERROR: UnusedAssignment - input.hack:6:5 - Assignment to $a is unused +ERROR: UnusedPipeVariable - input.hack:6:10 - The pipe data in this expression is not used anywhere ERROR: UnusedAwaitable - input.hack:7:5 - Assignment to awaitable $b is unused ERROR: UnusedPipeVariable - input.hack:7:10 - The pipe data in this expression is not used anywhere -ERROR: UnusedAssignment - input.hack:8:5 - Assignment to $c is unused \ No newline at end of file +ERROR: UnusedAssignment - input.hack:8:5 - Assignment to $c is unused +ERROR: UnusedPipeVariable - input.hack:8:10 - The pipe data in this expression is not used anywhere +ERROR: UnusedPipeVariable - input.hack:8:10 - The pipe data in this expression is not used anywhere