@@ -308,10 +308,13 @@ pub fn any_over_pattern(pattern: &Pattern, func: &dyn Fn(&Expr) -> bool) -> bool
308
308
}
309
309
}
310
310
311
- pub fn any_over_f_string_element ( element : & FStringElement , func : & dyn Fn ( & Expr ) -> bool ) -> bool {
311
+ pub fn any_over_f_string_element (
312
+ element : & ast:: FStringElement ,
313
+ func : & dyn Fn ( & Expr ) -> bool ,
314
+ ) -> bool {
312
315
match element {
313
- FStringElement :: Literal ( _) => false ,
314
- FStringElement :: Expression ( ast:: FStringExpressionElement {
316
+ ast :: FStringElement :: Literal ( _) => false ,
317
+ ast :: FStringElement :: Expression ( ast:: FStringExpressionElement {
315
318
expression,
316
319
format_spec,
317
320
..
@@ -1171,21 +1174,10 @@ impl Truthiness {
1171
1174
}
1172
1175
Expr :: NoneLiteral ( _) => Self :: Falsey ,
1173
1176
Expr :: EllipsisLiteral ( _) => Self :: Truthy ,
1174
- Expr :: FString ( ast:: ExprFString { value, .. } ) => {
1175
- if value. iter ( ) . all ( |part| match part {
1176
- ast:: FStringPart :: Literal ( string_literal) => string_literal. is_empty ( ) ,
1177
- ast:: FStringPart :: FString ( f_string) => f_string. elements . is_empty ( ) ,
1178
- } ) {
1177
+ Expr :: FString ( f_string) => {
1178
+ if is_empty_f_string ( f_string) {
1179
1179
Self :: Falsey
1180
- } else if value
1181
- . elements ( )
1182
- . any ( |f_string_element| match f_string_element {
1183
- ast:: FStringElement :: Literal ( ast:: FStringLiteralElement {
1184
- value, ..
1185
- } ) => !value. is_empty ( ) ,
1186
- ast:: FStringElement :: Expression ( _) => true ,
1187
- } )
1188
- {
1180
+ } else if is_non_empty_f_string ( f_string) {
1189
1181
Self :: Truthy
1190
1182
} else {
1191
1183
Self :: Unknown
@@ -1243,6 +1235,99 @@ impl Truthiness {
1243
1235
}
1244
1236
}
1245
1237
1238
+ /// Returns `true` if the expression definitely resolves to a non-empty string, when used as an
1239
+ /// f-string expression, or `false` if the expression may resolve to an empty string.
1240
+ fn is_non_empty_f_string ( expr : & ast:: ExprFString ) -> bool {
1241
+ fn inner ( expr : & Expr ) -> bool {
1242
+ match expr {
1243
+ // When stringified, these expressions are always non-empty.
1244
+ Expr :: Lambda ( _) => true ,
1245
+ Expr :: Dict ( _) => true ,
1246
+ Expr :: Set ( _) => true ,
1247
+ Expr :: ListComp ( _) => true ,
1248
+ Expr :: SetComp ( _) => true ,
1249
+ Expr :: DictComp ( _) => true ,
1250
+ Expr :: Compare ( _) => true ,
1251
+ Expr :: NumberLiteral ( _) => true ,
1252
+ Expr :: BooleanLiteral ( _) => true ,
1253
+ Expr :: NoneLiteral ( _) => true ,
1254
+ Expr :: EllipsisLiteral ( _) => true ,
1255
+ Expr :: List ( _) => true ,
1256
+ Expr :: Tuple ( _) => true ,
1257
+
1258
+ // These expressions must resolve to the inner expression.
1259
+ Expr :: IfExp ( ast:: ExprIfExp { body, orelse, .. } ) => inner ( body) && inner ( orelse) ,
1260
+ Expr :: NamedExpr ( ast:: ExprNamedExpr { value, .. } ) => inner ( value) ,
1261
+
1262
+ // These expressions are complex. We can't determine whether they're empty or not.
1263
+ Expr :: BoolOp ( ast:: ExprBoolOp { .. } ) => false ,
1264
+ Expr :: BinOp ( ast:: ExprBinOp { .. } ) => false ,
1265
+ Expr :: UnaryOp ( ast:: ExprUnaryOp { .. } ) => false ,
1266
+ Expr :: GeneratorExp ( _) => false ,
1267
+ Expr :: Await ( _) => false ,
1268
+ Expr :: Yield ( _) => false ,
1269
+ Expr :: YieldFrom ( _) => false ,
1270
+ Expr :: Call ( _) => false ,
1271
+ Expr :: Attribute ( _) => false ,
1272
+ Expr :: Subscript ( _) => false ,
1273
+ Expr :: Starred ( _) => false ,
1274
+ Expr :: Name ( _) => false ,
1275
+ Expr :: Slice ( _) => false ,
1276
+ Expr :: IpyEscapeCommand ( _) => false ,
1277
+
1278
+ // These literals may or may not be empty.
1279
+ Expr :: FString ( f_string) => is_non_empty_f_string ( f_string) ,
1280
+ Expr :: StringLiteral ( ast:: ExprStringLiteral { value, .. } ) => !value. is_empty ( ) ,
1281
+ Expr :: BytesLiteral ( ast:: ExprBytesLiteral { value, .. } ) => !value. is_empty ( ) ,
1282
+ }
1283
+ }
1284
+
1285
+ expr. value . iter ( ) . any ( |part| match part {
1286
+ ast:: FStringPart :: Literal ( string_literal) => !string_literal. is_empty ( ) ,
1287
+ ast:: FStringPart :: FString ( f_string) => {
1288
+ f_string. elements . iter ( ) . all ( |element| match element {
1289
+ FStringElement :: Literal ( string_literal) => !string_literal. is_empty ( ) ,
1290
+ FStringElement :: Expression ( f_string) => inner ( & f_string. expression ) ,
1291
+ } )
1292
+ }
1293
+ } )
1294
+ }
1295
+
1296
+ /// Returns `true` if the expression definitely resolves to the empty string, when used as an f-string
1297
+ /// expression.
1298
+ fn is_empty_f_string ( expr : & ast:: ExprFString ) -> bool {
1299
+ fn inner ( expr : & Expr ) -> bool {
1300
+ match expr {
1301
+ Expr :: StringLiteral ( ast:: ExprStringLiteral { value, .. } ) => value. is_empty ( ) ,
1302
+ Expr :: BytesLiteral ( ast:: ExprBytesLiteral { value, .. } ) => value. is_empty ( ) ,
1303
+ Expr :: FString ( ast:: ExprFString { value, .. } ) => {
1304
+ value
1305
+ . elements ( )
1306
+ . all ( |f_string_element| match f_string_element {
1307
+ FStringElement :: Literal ( ast:: FStringLiteralElement { value, .. } ) => {
1308
+ value. is_empty ( )
1309
+ }
1310
+ FStringElement :: Expression ( ast:: FStringExpressionElement {
1311
+ expression,
1312
+ ..
1313
+ } ) => inner ( expression) ,
1314
+ } )
1315
+ }
1316
+ _ => false ,
1317
+ }
1318
+ }
1319
+
1320
+ expr. value . iter ( ) . all ( |part| match part {
1321
+ ast:: FStringPart :: Literal ( string_literal) => string_literal. is_empty ( ) ,
1322
+ ast:: FStringPart :: FString ( f_string) => {
1323
+ f_string. elements . iter ( ) . all ( |element| match element {
1324
+ FStringElement :: Literal ( string_literal) => string_literal. is_empty ( ) ,
1325
+ FStringElement :: Expression ( f_string) => inner ( & f_string. expression ) ,
1326
+ } )
1327
+ }
1328
+ } )
1329
+ }
1330
+
1246
1331
pub fn generate_comparison (
1247
1332
left : & Expr ,
1248
1333
ops : & [ CmpOp ] ,
0 commit comments