@@ -2579,11 +2579,77 @@ def _visit_comprehension_inner(
2579
2579
# Literals and displays
2580
2580
2581
2581
def visit_JoinedStr (self , node : ast .JoinedStr ) -> Value :
2582
- # JoinedStr is the node type for f-strings.
2583
- # Not too much to check here. Perhaps we can add checks that format specifiers
2584
- # are valid.
2585
- self ._generic_visit_list (node .values )
2586
- return TypedValue (str )
2582
+ elements = self ._generic_visit_list (node .values )
2583
+ limit = self .options .get_value_for (UnionSimplificationLimit )
2584
+ possible_values : List [List [str ]] = [[]]
2585
+ for elt in elements :
2586
+ subvals = list (flatten_values (elt ))
2587
+ # Bail out if the list of possible values gets too long.
2588
+ if len (possible_values ) * len (subvals ) > limit :
2589
+ return TypedValue (str )
2590
+ to_add = []
2591
+ for subval in subvals :
2592
+ if not isinstance (subval , KnownValue ):
2593
+ return TypedValue (str )
2594
+ if not isinstance (subval .val , str ):
2595
+ return TypedValue (str )
2596
+ to_add .append (subval .val )
2597
+ possible_values = [
2598
+ lst + [new_elt ] for lst in possible_values for new_elt in to_add
2599
+ ]
2600
+ return unite_values (* [KnownValue ("" .join (lst )) for lst in possible_values ])
2601
+
2602
+ def visit_FormattedValue (self , node : ast .FormattedValue ) -> Value :
2603
+ val = self .visit (node .value )
2604
+ format_spec_val = (
2605
+ self .visit (node .format_spec ) if node .format_spec else KnownValue ("" )
2606
+ )
2607
+ if isinstance (format_spec_val , KnownValue ) and isinstance (
2608
+ format_spec_val .val , str
2609
+ ):
2610
+ format_spec = format_spec_val .val
2611
+ else :
2612
+ # TODO: statically check whether the format specifier is valid.
2613
+ return TypedValue (str )
2614
+ possible_vals = []
2615
+ for subval in flatten_values (val ):
2616
+ possible_vals .append (
2617
+ self ._visit_single_formatted_value (subval , node , format_spec )
2618
+ )
2619
+ return unite_and_simplify (
2620
+ * possible_vals , limit = self .options .get_value_for (UnionSimplificationLimit )
2621
+ )
2622
+
2623
+ def _visit_single_formatted_value (
2624
+ self , val : Value , node : ast .FormattedValue , format_spec : str
2625
+ ) -> Value :
2626
+ if not isinstance (val , KnownValue ):
2627
+ return TypedValue (str )
2628
+ output = val .val
2629
+ if node .conversion != - 1 :
2630
+ unsupported_conversion = False
2631
+ try :
2632
+ if node .conversion == ord ("a" ):
2633
+ output = ascii (output )
2634
+ elif node .conversion == ord ("s" ):
2635
+ output = str (output )
2636
+ elif node .conversion == ord ("r" ):
2637
+ output = repr (output )
2638
+ else :
2639
+ unsupported_conversion = True
2640
+ except Exception :
2641
+ # str/repr/ascii failed
2642
+ return TypedValue (str )
2643
+ if unsupported_conversion :
2644
+ raise NotImplementedError (
2645
+ f"Unsupported converion specifier { node .conversion } "
2646
+ )
2647
+ try :
2648
+ output = format (output , format_spec )
2649
+ except Exception :
2650
+ # format failed
2651
+ return TypedValue (str )
2652
+ return KnownValue (output )
2587
2653
2588
2654
def visit_Constant (self , node : ast .Constant ) -> Value :
2589
2655
# replaces Num, Str, etc. in 3.8+
0 commit comments