33use std:: borrow:: Borrow ;
44use std:: cell:: RefCell ;
55use std:: collections:: BTreeSet ;
6+ use std:: hash:: { Hash , Hasher } ;
67use std:: iter;
78use std:: ops:: RangeInclusive ;
89use std:: rc:: Rc ;
910
1011use bstr:: { BString , ByteSlice } ;
1112use itertools:: Itertools ;
12-
13+ use rustc_hash :: { FxHashMap , FxHashSet , FxHasher } ;
1314use yara_x_parser:: Span ;
1415use yara_x_parser:: ast;
1516use yara_x_parser:: ast:: WithSpan ;
@@ -26,12 +27,12 @@ use crate::compiler::errors::{
2627use crate :: compiler:: ir:: hex2hir:: hex_pattern_hir_from_ast;
2728use crate :: compiler:: ir:: {
2829 Error , Expr , ExprId , Iterable , LiteralPattern , MatchAnchor , Pattern ,
29- PatternFlags , PatternInRule , Quantifier , Range , RegexpPattern ,
30+ PatternFlags , PatternInRule , Quantifier , Range , RegexpPattern , dfs ,
3031} ;
3132use crate :: compiler:: report:: { Level , ReportBuilder } ;
3233use crate :: compiler:: {
3334 CompileContext , CompileError , FilesizeBounds , ForVars , PatternIdx ,
34- TextPatternAsHex , warnings,
35+ RegexId , RegexSetId , TextPatternAsHex , warnings,
3536} ;
3637use crate :: errors:: CustomError ;
3738use crate :: errors:: { MethodNotAllowedInWith , PotentiallySlowLoop } ;
@@ -2179,40 +2180,23 @@ macro_rules! gen_n_ary_operation {
21792180
21802181 // Make sure that all operands have one of the accepted types.
21812182 for ( hir, ast) in iter:: zip( operands_hir. iter( ) , expr. operands( ) ) {
2182- check_type( ctx, * hir, ast. span( ) , accepted_types) ?;
21832183 if let Some ( check_fn) = check_fn {
21842184 check_fn( ctx, * hir, ast. span( ) ) ?;
21852185 }
21862186 }
21872187
2188- // Iterate the operands in pairs (first, second), (second, third),
2189- // (third, fourth), etc.
2190- for ( ( lhs_hir, rhs_ast) , ( rhs_hir, lhs_ast) ) in
2188+ for ( ( lhs_hir, lhs_ast) , ( rhs_hir, rhs_ast) ) in
21912189 iter:: zip( operands_hir. iter( ) , expr. operands( ) ) . tuple_windows( )
21922190 {
2193- let lhs_ty = ctx. ir. get( * lhs_hir) . ty( ) ;
2194- let rhs_ty = ctx. ir. get( * rhs_hir) . ty( ) ;
2195-
2196- let types_are_compatible = {
2197- // If the types are the same, they are compatible.
2198- ( lhs_ty == rhs_ty) ||
2199- // If the list of compatible types contains the pair
2200- // (lhs_ty, rhs_ty) or (rhs_ty, lhs_ty), they are
2201- // compatible.
2202- compatible_types. contains( & ( lhs_ty, rhs_ty) )
2203- || compatible_types. contains( & ( rhs_ty, lhs_ty) )
2204-
2205- } ;
2206-
2207- if !types_are_compatible {
2208- return Err ( MismatchingTypes :: build(
2209- ctx. report_builder,
2210- lhs_ty. to_string( ) ,
2211- rhs_ty. to_string( ) ,
2212- ctx. report_builder. span_to_code_loc( expr. first( ) . span( ) . combine( & lhs_ast. span( ) ) ) ,
2213- ctx. report_builder. span_to_code_loc( rhs_ast. span( ) ) ,
2214- ) ) ;
2215- }
2191+ check_operands(
2192+ ctx,
2193+ * lhs_hir,
2194+ * rhs_hir,
2195+ expr. first( ) . span( ) . combine( & lhs_ast. span( ) ) ,
2196+ rhs_ast. span( ) ,
2197+ accepted_types,
2198+ compatible_types,
2199+ ) ?;
22162200 }
22172201
22182202 ctx. ir. $variant( operands_hir) . map_err( |err| {
@@ -2275,28 +2259,123 @@ gen_n_ary_operation!(
22752259 } )
22762260) ;
22772261
2278- gen_n_ary_operation ! (
2279- or_expr_from_ast,
2280- or,
2281- // Boolean operations accept integer, float and string operands.
2282- // If operands are not boolean they are casted to boolean.
2283- Type :: Bool | Type :: Integer | Type :: Float | Type :: String ,
2284- // All operand types can be mixed in a boolean operation, as they
2285- // are casted to boolean anyways.
2286- & [
2262+ fn or_expr_from_ast (
2263+ ctx : & mut CompileContext ,
2264+ expr : & ast:: NAryExpr ,
2265+ ) -> Result < ExprId , CompileError > {
2266+ let span = expr. span ( ) ;
2267+ let accepted_types =
2268+ & [ Type :: Bool , Type :: Integer , Type :: Float , Type :: String ] ;
2269+ let compatible_types = & [
22872270 ( Type :: Integer , Type :: Bool ) ,
22882271 ( Type :: Integer , Type :: Float ) ,
22892272 ( Type :: Integer , Type :: String ) ,
22902273 ( Type :: String , Type :: Bool ) ,
22912274 ( Type :: String , Type :: Float ) ,
2292- ( Type :: Float , Type :: Bool )
2293- ] ,
2294- Some ( |ctx, operand, span| {
2295- let ty = ctx. ir. get( operand) . ty( ) ;
2296- warn_if_not_bool( ctx, ty, span) ;
2297- Ok ( ( ) )
2275+ ( Type :: Float , Type :: Bool ) ,
2276+ ] ;
2277+
2278+ // Validate operand types and emit standard warnings. Ensure all operands
2279+ // in the `or` expression conform to boolean-compatible types, checking
2280+ // for potential mismatches across adjacent pairs.
2281+ let or_operands: Vec < ExprId > = expr
2282+ . operands ( )
2283+ . map ( |expr| expr_from_ast ( ctx, expr) )
2284+ . collect :: < Result < Vec < ExprId > , CompileError > > ( ) ?;
2285+
2286+ for ( hir, ast) in iter:: zip ( or_operands. iter ( ) , expr. operands ( ) ) {
2287+ let ty = ctx. ir . get ( * hir) . ty ( ) ;
2288+ warn_if_not_bool ( ctx, ty, ast. span ( ) ) ;
2289+ }
2290+
2291+ for ( ( lhs_hir, lhs_ast) , ( rhs_hir, rhs_ast) ) in
2292+ iter:: zip ( or_operands. iter ( ) , expr. operands ( ) ) . tuple_windows ( )
2293+ {
2294+ check_operands (
2295+ ctx,
2296+ * lhs_hir,
2297+ * rhs_hir,
2298+ expr. first ( ) . span ( ) . combine ( & lhs_ast. span ( ) ) ,
2299+ rhs_ast. span ( ) ,
2300+ accepted_types,
2301+ compatible_types,
2302+ ) ?;
2303+ }
2304+
2305+ // Group `matches` expressions by their left-side operand. All the `matches`
2306+ // expressions sharing the same left-side operand will be aggregated together
2307+ // into a MatchMany operation that matches the left-side operand against
2308+ // a set of regular expressions.
2309+ let mut matches_by_lhs: FxHashMap < u64 , Vec < ( usize , ExprId , RegexId ) > > =
2310+ FxHashMap :: default ( ) ;
2311+
2312+ for ( i, & op) in or_operands. iter ( ) . enumerate ( ) {
2313+ if let Expr :: Matches { lhs, rhs } = ctx. ir . get ( op)
2314+ && let Expr :: Const ( TypeValue :: Regexp ( Some ( re) ) ) = ctx. ir . get ( * rhs)
2315+ {
2316+ let mut hasher = FxHasher :: default ( ) ;
2317+ for evt in ctx. ir . dfs_iter ( * lhs) {
2318+ if let dfs:: Event :: Enter ( ( _, expr, _) ) = evt {
2319+ Hash :: hash ( expr, & mut hasher) ;
2320+ }
2321+ }
2322+ let lhs_expr_hash = hasher. finish ( ) ;
2323+ let re_id = ctx. regex_pool . get_or_intern ( re. as_str ( ) ) ;
2324+ matches_by_lhs
2325+ . entry ( lhs_expr_hash)
2326+ . or_default ( )
2327+ . push ( ( i, * lhs, re_id) ) ;
2328+ }
2329+ }
2330+
2331+ // Collapse grouped regular expressions into `MatchesMany`. For any target
2332+ // expression associated with two or more regular expressions, construct a
2333+ // unified `RegexSet`, replace the individual `matches` nodes with a single
2334+ // `MatchesMany` expression, and record the collapsed indices.
2335+ let mut final_operands = Vec :: new ( ) ;
2336+ let mut collapsed_indices = FxHashSet :: default ( ) ;
2337+
2338+ for ( _, group) in matches_by_lhs {
2339+ if group. len ( ) >= 2 {
2340+ let set_id = RegexSetId :: from ( ctx. regex_sets . len ( ) as i32 ) ;
2341+ let re_ids: Vec < _ > =
2342+ group. iter ( ) . map ( |& ( _, _, re_id) | re_id) . collect ( ) ;
2343+ ctx. regex_sets . insert ( set_id, re_ids) ;
2344+
2345+ let first_lhs = group[ 0 ] . 1 ;
2346+ let multimatch = ctx. ir . matches_regex_set ( first_lhs, set_id) ;
2347+
2348+ final_operands. push ( multimatch) ;
2349+
2350+ for ( i, _, _) in group {
2351+ collapsed_indices. insert ( i) ;
2352+ }
2353+ }
2354+ }
2355+
2356+ // Assemble final operands and construct the `or` expression. Preserve all
2357+ // non-collapsed operands in their original relative order. If all operands
2358+ // collapse into a single expression, return it directly without a wrapping
2359+ // `or` node.
2360+ for ( i, & op) in or_operands. iter ( ) . enumerate ( ) {
2361+ if !collapsed_indices. contains ( & i) {
2362+ final_operands. push ( op) ;
2363+ }
2364+ }
2365+
2366+ if final_operands. len ( ) == 1 {
2367+ return Ok ( final_operands[ 0 ] ) ;
2368+ }
2369+
2370+ ctx. ir . or ( final_operands) . map_err ( |err| match err {
2371+ Error :: NumberOutOfRange => NumberOutOfRange :: build (
2372+ ctx. report_builder ,
2373+ i64:: MIN ,
2374+ i64:: MAX ,
2375+ ctx. report_builder . span_to_code_loc ( span) ,
2376+ ) ,
22982377 } )
2299- ) ;
2378+ }
23002379
23012380gen_unary_op ! ( minus_expr_from_ast, minus, Type :: Integer | Type :: Float , None ) ;
23022381
0 commit comments