diff --git a/src/analyzer/expr/call/arguments_analyzer.rs b/src/analyzer/expr/call/arguments_analyzer.rs index 2e240e30..ede54664 100644 --- a/src/analyzer/expr/call/arguments_analyzer.rs +++ b/src/analyzer/expr/call/arguments_analyzer.rs @@ -1069,9 +1069,32 @@ fn handle_possibly_matching_inout_param( ); } - if functionlike_id == &FunctionLikeIdentifier::Function(StrId::PREG_MATCH_WITH_MATCHES) - && argument_offset == 2 + if matches!( + functionlike_id, + FunctionLikeIdentifier::Function( + StrId::PREG_MATCH_WITH_MATCHES | StrId::PREG_MATCH_ALL_WITH_MATCHES + ) + ) && argument_offset == 2 { + let argument_node = DataFlowNode::get_for_method_argument( + functionlike_id.to_string(statements_analyzer.get_interner()), + 0, + Some(statements_analyzer.get_hpos(all_args[1].1.pos())), + Some(statements_analyzer.get_hpos(function_call_pos)), + ); + + analysis_data + .data_flow_graph + .add_node(argument_node.clone()); + + analysis_data.data_flow_graph.add_path( + &argument_node, + &out_node, + PathKind::Aggregate, + vec![], + vec![], + ); + let argument_node = DataFlowNode::get_for_method_argument( functionlike_id.to_string(statements_analyzer.get_interner()), 1, diff --git a/src/analyzer/expr/call/expression_call_analyzer.rs b/src/analyzer/expr/call/expression_call_analyzer.rs index d3c599bc..1cef134c 100644 --- a/src/analyzer/expr/call/expression_call_analyzer.rs +++ b/src/analyzer/expr/call/expression_call_analyzer.rs @@ -66,6 +66,7 @@ pub(crate) fn analyze( start_line: 0, }, ); + lambda_storage.user_defined = true; lambda_storage.params = closure_params .iter() .map(|fn_param| { diff --git a/src/analyzer/expr/call/function_call_return_type_fetcher.rs b/src/analyzer/expr/call/function_call_return_type_fetcher.rs index 8341ac83..19af4adf 100644 --- a/src/analyzer/expr/call/function_call_return_type_fetcher.rs +++ b/src/analyzer/expr/call/function_call_return_type_fetcher.rs @@ -731,7 +731,6 @@ fn add_dataflow( // todo conditionally remove taints - let function_call_node = if let GraphKind::WholeProgram(_) = &data_flow_graph.kind { DataFlowNode::get_for_method_return( functionlike_id.to_string(statements_analyzer.get_interner()), @@ -951,6 +950,7 @@ fn get_special_argument_nodes( | StrId::BASE64_DECODE | StrId::URLENCODE | StrId::URLDECODE + | StrId::GZINFLATE | StrId::LIB_DICT_FILTER | StrId::LIB_DICT_FILTER_ASYNC | StrId::LIB_DICT_FILTER_KEYS @@ -963,6 +963,7 @@ fn get_special_argument_nodes( | StrId::LIB_VEC_FILTER_WITH_KEY | StrId::LIB_VEC_DROP | StrId::LIB_VEC_REVERSE + | StrId::LIB_DICT_REVERSE | StrId::LIB_VEC_UNIQUE | StrId::LIB_KEYSET_FILTER | StrId::LIB_KEYSET_FILTER_NULLS @@ -980,28 +981,30 @@ fn get_special_argument_nodes( | StrId::LOG | StrId::IP2LONG | StrId::BIN2HEX - | StrId::HEX2BIN => (vec![(0, PathKind::Default)], None), - StrId::LIB_VEC_DIFF + | StrId::HEX2BIN + | StrId::ESCAPESHELLARG => (vec![(0, PathKind::Default)], None), + StrId::LIB_REGEX_FIRST_MATCH => (vec![(0, PathKind::Default)], Some(PathKind::Default)), + StrId::LIB_DICT_SELECT_KEYS + | StrId::LIB_VEC_TAKE + | StrId::LIB_DICT_TAKE + | StrId::LIB_STR_SLICE + | StrId::LIB_STR_FORMAT_NUMBER + | StrId::LIB_DICT_DIFF_BY_KEY + | StrId::NUMBER_FORMAT + | StrId::LIB_DICT_CHUNK + | StrId::LIB_VEC_DIFF | StrId::LIB_KEYSET_DIFF | StrId::LIB_KEYSET_INTERSECT | StrId::LIB_VEC_INTERSECT | StrId::LIB_VEC_SLICE | StrId::LIB_VEC_RANGE | StrId::LIB_VEC_CHUNK + | StrId::LIB_KEYSET_CHUNK | StrId::LIB_STR_STRIP_PREFIX | StrId::LIB_STR_STRIP_SUFFIX | StrId::LIB_STR_REPEAT | StrId::SUBSTR - | StrId::LIB_DICT_ASSOCIATE - | StrId::LIB_REGEX_FIRST_MATCH => { - (vec![(0, PathKind::Default)], Some(PathKind::Default)) - } - StrId::LIB_DICT_SELECT_KEYS - | StrId::LIB_VEC_TAKE - | StrId::LIB_DICT_TAKE - | StrId::LIB_STR_SLICE - | StrId::LIB_STR_FORMAT_NUMBER - | StrId::LIB_DICT_DIFF_BY_KEY => { + | StrId::LIB_DICT_ASSOCIATE => { (vec![(0, PathKind::Default)], Some(PathKind::Aggregate)) } StrId::LIB_C_IS_EMPTY @@ -1035,7 +1038,10 @@ fn get_special_argument_nodes( | StrId::GET_CLASS | StrId::CTYPE_LOWER | StrId::SHA1 - | StrId::MD5 => (vec![(0, PathKind::Aggregate)], None), + | StrId::MD5 + | StrId::DIRNAME + | StrId::CRC32 + | StrId::FILTER_VAR => (vec![(0, PathKind::Aggregate)], None), StrId::LIB_MATH_ALMOST_EQUALS | StrId::LIB_MATH_BASE_CONVERT | StrId::LIB_MATH_EXP @@ -1059,6 +1065,7 @@ fn get_special_argument_nodes( | StrId::STRPOS | StrId::SUBSTR_COUNT | StrId::STRCMP + | StrId::STRNATCASECMP | StrId::LIB_KEYSET_EQUAL => (vec![], Some(PathKind::Aggregate)), StrId::LIB_C_CONTAINS | StrId::LIB_C_CONTAINS_KEY @@ -1066,8 +1073,14 @@ fn get_special_argument_nodes( | StrId::PREG_MATCH | StrId::LIB_REGEX_MATCHES | StrId::PREG_MATCH_WITH_MATCHES - | StrId::PREG_MATCH_ALL_WITH_MATCHES => ( - vec![(0, PathKind::Aggregate), (1, PathKind::Aggregate)], + | StrId::PREG_MATCH_ALL_WITH_MATCHES + | StrId::HASH => ( + vec![ + (0, PathKind::Aggregate), + (1, PathKind::Aggregate), + (3, PathKind::Aggregate), + (4, PathKind::Aggregate), + ], None, ), StrId::JSON_ENCODE | StrId::SERIALIZE => (vec![(0, PathKind::Serialize)], None), @@ -1087,9 +1100,16 @@ fn get_special_argument_nodes( None, ) } - StrId::LIB_STR_REPLACE | StrId::LIB_STR_REPLACE_CI => { - (vec![(0, PathKind::Default), (2, PathKind::Default)], None) - } + StrId::PREG_REPLACE_WITH_COUNT => ( + vec![ + (0, PathKind::Aggregate), + (1, PathKind::Default), + (2, PathKind::Default), + (0, PathKind::Aggregate), + ], + None, + ), + StrId::PREG_GREP => (vec![(0, PathKind::Aggregate), (1, PathKind::Default)], None), StrId::LIB_STR_REPLACE_EVERY => ( vec![ (0, PathKind::Default), @@ -1097,7 +1117,14 @@ fn get_special_argument_nodes( ], None, ), - StrId::LIB_REGEX_REPLACE => ( + + StrId::STR_PAD + | StrId::LIB_STR_PAD_LEFT + | StrId::LIB_STR_PAD_RIGHT + | StrId::CHUNK_SPLIT + | StrId::LIB_REGEX_REPLACE + | StrId::LIB_STR_REPLACE + | StrId::LIB_STR_REPLACE_CI => ( vec![ (0, PathKind::Default), (1, PathKind::Aggregate), @@ -1105,9 +1132,6 @@ fn get_special_argument_nodes( ], None, ), - StrId::STR_PAD | StrId::CHUNK_SPLIT => { - (vec![(0, PathKind::Default), (2, PathKind::Default)], None) - } StrId::IMPLODE | StrId::JOIN => ( vec![ (0, PathKind::Default), @@ -1136,6 +1160,19 @@ fn get_special_argument_nodes( )], None, ), + StrId::LIB_VEC_ZIP => ( + vec![ + ( + 0, + PathKind::UnknownArrayAssignment(ArrayDataKind::ArrayValue), + ), + ( + 1, + PathKind::UnknownArrayAssignment(ArrayDataKind::ArrayValue), + ), + ], + None, + ), StrId::PATHINFO => ( vec![ ( @@ -1176,6 +1213,7 @@ fn get_special_argument_nodes( PathKind::UnknownArrayAssignment(ArrayDataKind::ArrayValue), ), (1, PathKind::Aggregate), + (2, PathKind::Aggregate), ], None, ), @@ -1220,7 +1258,6 @@ fn get_special_argument_nodes( )], None, ), - StrId::LIB_DICT_CHUNK => (vec![(0, PathKind::Default), (1, PathKind::Aggregate)], None), StrId::LIB_C_FIRST | StrId::LIB_C_FIRSTX | StrId::LIB_C_LAST diff --git a/src/code_info_builder/functionlike_scanner.rs b/src/code_info_builder/functionlike_scanner.rs index 15762a26..e54d9af8 100644 --- a/src/code_info_builder/functionlike_scanner.rs +++ b/src/code_info_builder/functionlike_scanner.rs @@ -316,7 +316,7 @@ pub(crate) fn get_functionlike( file_source.file_path, ); - functionlike_info.user_defined = user_defined && name.is_some(); + functionlike_info.user_defined = user_defined; functionlike_info.is_closure = name.is_none(); if let Some(name_pos) = name_pos { diff --git a/src/str/build.rs b/src/str/build.rs index 4fe010ab..ff9ef38d 100644 --- a/src/str/build.rs +++ b/src/str/build.rs @@ -72,8 +72,10 @@ fn main() -> Result<()> { "HH\\Lib\\Dict\\map_with_key", "HH\\Lib\\Dict\\map_with_key_async", "HH\\Lib\\Dict\\merge", + "HH\\Lib\\Dict\\reverse", "HH\\Lib\\Dict\\select_keys", "HH\\Lib\\Dict\\take", + "HH\\Lib\\Keyset\\chunk", "HH\\Lib\\Keyset\\diff", "HH\\Lib\\Keyset\\equal", "HH\\Lib\\Keyset\\filter", @@ -134,6 +136,8 @@ fn main() -> Result<()> { "HH\\Lib\\Str\\join", "HH\\Lib\\Str\\length", "HH\\Lib\\Str\\lowercase", + "HH\\Lib\\Str\\pad_left", + "HH\\Lib\\Str\\pad_right", "HH\\Lib\\Str\\repeat", "HH\\Lib\\Str\\replace", "HH\\Lib\\Str\\replace_ci", @@ -171,6 +175,7 @@ fn main() -> Result<()> { "HH\\Lib\\Vec\\sort", "HH\\Lib\\Vec\\take", "HH\\Lib\\Vec\\unique", + "HH\\Lib\\Vec\\zip", "HH\\MemberOf", "HH\\Shapes", "HH\\Traversable", @@ -224,20 +229,25 @@ fn main() -> Result<()> { "convert_uudecode", "convert_uuencode", "count", + "crc32", "ctype_lower", "date", "date_format", "debug_backtrace", "dirname", "echo", + "escapeshellarg", "explode", "extension", "file_get_contents", "filename", + "filter_var", "fromItems", "function_exists", "get_class", "get_object_vars", + "gzinflate", + "hash", "hash_equals", "hash_hmac", "hex2bin", @@ -268,16 +278,19 @@ fn main() -> Result<()> { "microtime", "mktime", "nl2br", + "number_format", "ord", "parent", "password_hash", "pathinfo", "preg_filter", + "preg_grep", "preg_match", "preg_match_all_with_matches", "preg_match_with_matches", "preg_quote", "preg_replace", + "preg_replace_with_count", "preg_split", "print_r", "printf", @@ -310,6 +323,7 @@ fn main() -> Result<()> { "stripcslashes", "stripslashes", "stristr", + "strnatcasecmp", "strpad", "strpbrk", "strpos", diff --git a/tests/unused/UnusedExpression/markAsUsedInPureClosure/input.hack b/tests/unused/UnusedExpression/markAsUsedInPureClosure/input.hack new file mode 100644 index 00000000..5b0776be --- /dev/null +++ b/tests/unused/UnusedExpression/markAsUsedInPureClosure/input.hack @@ -0,0 +1,5 @@ +function foo(int $i): void { + $a = (int $b) ==> $b; + $c = $a($i); + echo $c; +} \ No newline at end of file