@@ -34,6 +34,8 @@ use hashql_mir::{
3434 } ,
3535} ;
3636use insta:: { Settings , assert_snapshot} ;
37+ use sqruff_lib:: core:: { config:: FluffConfig , linter:: core:: Linter } ;
38+ use sqruff_lib_core:: dialects:: init:: DialectKind ;
3739
3840use crate :: {
3941 context:: EvalContext ,
@@ -156,6 +158,13 @@ fn compile_filter_islands<'heap>(fixture: &Fixture<'heap>, heap: &'heap Heap) ->
156158
157159 let mut island_reports = Vec :: new ( ) ;
158160
161+ let mut linter_config = FluffConfig :: default ( ) ;
162+ linter_config
163+ . override_dialect ( DialectKind :: Postgres )
164+ . expect ( "dialect should be loaded" ) ;
165+ let mut linter =
166+ Linter :: new ( linter_config, None , None , false ) . expect ( "linter should be created" ) ;
167+
159168 for ( island_id, entry_block) in postgres_islands {
160169 let island = & residual. islands [ island_id] ;
161170
@@ -172,10 +181,21 @@ fn compile_filter_islands<'heap>(fixture: &Fixture<'heap>, heap: &'heap Heap) ->
172181
173182 let sql = expression. transpile_to_string ( ) ;
174183
184+ let linted = linter
185+ . lint_string ( & format ! ( "SELECT {sql}" ) , None , true )
186+ . expect ( "should be valid SQL" ) ;
187+
188+ let mut fixed = linted. fix_string ( ) ;
189+ let fixed: String = fixed[ 7 ..]
190+ . lines ( )
191+ . map ( |line| & line[ 4 ..] )
192+ . intersperse ( "\n " )
193+ . collect ( ) ;
194+
175195 island_reports. push ( FilterIslandReport {
176196 entry_block,
177197 target : island. target ( ) ,
178- sql,
198+ sql : fixed ,
179199 } ) ;
180200 }
181201
@@ -266,10 +286,24 @@ fn compile_full_query_with_mask<'heap>(
266286 "unexpected diagnostics from full compilation" ,
267287 ) ;
268288
289+ let mut linter_config = FluffConfig :: default ( ) ;
290+ linter_config
291+ . override_dialect ( DialectKind :: Postgres )
292+ . expect ( "dialect should be loaded" ) ;
293+ let mut linter =
294+ Linter :: new ( linter_config, None , None , false ) . expect ( "linter should be created" ) ;
295+
269296 let sql = prepared_query. transpile ( ) . to_string ( ) ;
297+ let linted = linter
298+ . lint_string ( & sql, None , true )
299+ . expect ( "should be valid SQL" ) ;
300+
270301 let parameters = format ! ( "{}" , prepared_query. parameters) ;
271302
272- QueryReport { sql, parameters }
303+ QueryReport {
304+ sql : linted. fix_string ( ) ,
305+ parameters,
306+ }
273307}
274308
275309fn snapshot_settings ( ) -> Settings {
@@ -918,7 +952,7 @@ fn unary_not() {
918952
919953 bb0( ) {
920954 x = input. load! "val" ;
921- result = un. ! x;
955+ result = un. ~ x;
922956 return result;
923957 }
924958 } ) ;
@@ -1021,7 +1055,7 @@ fn temporal_decision_time_interval() {
10211055 assert_snapshot ! ( "temporal_decision_time_interval" , report. to_string( ) ) ;
10221056}
10231057
1024- /// `BinOp::BitAnd` → `BinaryOperator::BitwiseAnd` with `::bigint` casts on both operands .
1058+ /// `BinOp::BitAnd` on integers → `BinaryOperator::BitwiseAnd` with `::bigint` casts.
10251059#[ test]
10261060fn binary_bitand_bigint_cast ( ) {
10271061 let heap = Heap :: new ( ) ;
@@ -1047,3 +1081,84 @@ fn binary_bitand_bigint_cast() {
10471081 let _guard = settings. bind_to_scope ( ) ;
10481082 assert_snapshot ! ( "binary_bitand_bigint_cast" , report. to_string( ) ) ;
10491083}
1084+
1085+ /// `BinOp::BitAnd` on booleans → `VariadicOperator::And` with `::boolean` casts.
1086+ #[ test]
1087+ fn binary_bitand_boolean_and ( ) {
1088+ let heap = Heap :: new ( ) ;
1089+ let interner = Interner :: new ( & heap) ;
1090+ let env = Environment :: new ( & heap) ;
1091+
1092+ let body = body ! ( interner, env; [ graph:: read:: filter] @0 /2 -> Bool {
1093+ decl env: ( ) , vertex: [ Opaque sym:: path:: Entity ; ?] ,
1094+ x: Bool , y: Bool , result: Bool ;
1095+
1096+ bb0( ) {
1097+ x = input. load! "a" ;
1098+ y = input. load! "b" ;
1099+ result = bin. & x y;
1100+ return result;
1101+ }
1102+ } ) ;
1103+
1104+ let fixture = Fixture :: new ( & heap, env, body) ;
1105+ let report = compile_filter_islands ( & fixture, & heap) ;
1106+
1107+ let settings = snapshot_settings ( ) ;
1108+ let _guard = settings. bind_to_scope ( ) ;
1109+ assert_snapshot ! ( "binary_bitand_boolean_and" , report. to_string( ) ) ;
1110+ }
1111+
1112+ /// `BinOp::BitOr` on integers → `BinaryOperator::BitwiseOr` with `::bigint` casts.
1113+ #[ test]
1114+ fn binary_bitor_bigint_cast ( ) {
1115+ let heap = Heap :: new ( ) ;
1116+ let interner = Interner :: new ( & heap) ;
1117+ let env = Environment :: new ( & heap) ;
1118+
1119+ let body = body ! ( interner, env; [ graph:: read:: filter] @0 /2 -> Int {
1120+ decl env: ( ) , vertex: [ Opaque sym:: path:: Entity ; ?] ,
1121+ x: Int , y: Int , result: Int ;
1122+
1123+ bb0( ) {
1124+ x = input. load! "a" ;
1125+ y = input. load! "b" ;
1126+ result = bin. | x y;
1127+ return result;
1128+ }
1129+ } ) ;
1130+
1131+ let fixture = Fixture :: new ( & heap, env, body) ;
1132+ let report = compile_filter_islands ( & fixture, & heap) ;
1133+
1134+ let settings = snapshot_settings ( ) ;
1135+ let _guard = settings. bind_to_scope ( ) ;
1136+ assert_snapshot ! ( "binary_bitor_bigint_cast" , report. to_string( ) ) ;
1137+ }
1138+
1139+ /// `BinOp::BitOr` on booleans → `VariadicOperator::Or` with `::boolean` casts.
1140+ #[ test]
1141+ fn binary_bitor_boolean_or ( ) {
1142+ let heap = Heap :: new ( ) ;
1143+ let interner = Interner :: new ( & heap) ;
1144+ let env = Environment :: new ( & heap) ;
1145+
1146+ let body = body ! ( interner, env; [ graph:: read:: filter] @0 /2 -> Bool {
1147+ decl env: ( ) , vertex: [ Opaque sym:: path:: Entity ; ?] ,
1148+ x: Bool , y: Bool , result: Bool ;
1149+
1150+ bb0( ) {
1151+ x = input. load! "a" ;
1152+ y = input. load! "b" ;
1153+ result = bin. | x y;
1154+ return result;
1155+ }
1156+ } ) ;
1157+
1158+ let fixture = Fixture :: new ( & heap, env, body) ;
1159+ let report = compile_filter_islands ( & fixture, & heap) ;
1160+
1161+ let settings = snapshot_settings ( ) ;
1162+ let _guard = settings. bind_to_scope ( ) ;
1163+ assert_snapshot ! ( "binary_bitor_boolean_or" , report. to_string( ) ) ;
1164+ }
0 commit comments