11use crate :: db:: tests:: TestDb ;
22use crate :: symbol:: { builtins_symbol, known_module_symbol} ;
33use crate :: types:: {
4- BoundMethodType , IntersectionBuilder , KnownClass , KnownInstanceType , SubclassOfType , TupleType ,
5- Type , UnionType ,
4+ BoundMethodType , CallableType , IntersectionBuilder , KnownClass , KnownInstanceType , Parameter ,
5+ Parameters , Signature , SubclassOfType , TupleType , Type , UnionType ,
66} ;
77use crate :: { Db , KnownModule } ;
8+ use hashbrown:: HashSet ;
89use quickcheck:: { Arbitrary , Gen } ;
10+ use ruff_python_ast:: name:: Name ;
911
1012/// A test representation of a type that can be transformed unambiguously into a real Type,
1113/// given a db.
@@ -45,6 +47,59 @@ pub(crate) enum Ty {
4547 class : & ' static str ,
4648 method : & ' static str ,
4749 } ,
50+ Callable {
51+ params : CallableParams ,
52+ returns : Option < Box < Ty > > ,
53+ } ,
54+ }
55+
56+ #[ derive( Debug , Clone , PartialEq ) ]
57+ pub ( crate ) enum CallableParams {
58+ GradualForm ,
59+ List ( Vec < Param > ) ,
60+ }
61+
62+ impl CallableParams {
63+ pub ( crate ) fn into_parameters ( self , db : & TestDb ) -> Parameters < ' _ > {
64+ match self {
65+ CallableParams :: GradualForm => Parameters :: gradual_form ( ) ,
66+ CallableParams :: List ( params) => Parameters :: new ( params. into_iter ( ) . map ( |param| {
67+ let mut parameter = match param. kind {
68+ ParamKind :: PositionalOnly => Parameter :: positional_only ( param. name ) ,
69+ ParamKind :: PositionalOrKeyword => {
70+ Parameter :: positional_or_keyword ( param. name . unwrap ( ) )
71+ }
72+ ParamKind :: Variadic => Parameter :: variadic ( param. name . unwrap ( ) ) ,
73+ ParamKind :: KeywordOnly => Parameter :: keyword_only ( param. name . unwrap ( ) ) ,
74+ ParamKind :: KeywordVariadic => Parameter :: keyword_variadic ( param. name . unwrap ( ) ) ,
75+ } ;
76+ if let Some ( annotated_ty) = param. annotated_ty {
77+ parameter = parameter. with_annotated_type ( annotated_ty. into_type ( db) ) ;
78+ }
79+ if let Some ( default_ty) = param. default_ty {
80+ parameter = parameter. with_default_type ( default_ty. into_type ( db) ) ;
81+ }
82+ parameter
83+ } ) ) ,
84+ }
85+ }
86+ }
87+
88+ #[ derive( Debug , Clone , PartialEq ) ]
89+ pub ( crate ) struct Param {
90+ kind : ParamKind ,
91+ name : Option < Name > ,
92+ annotated_ty : Option < Ty > ,
93+ default_ty : Option < Ty > ,
94+ }
95+
96+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
97+ enum ParamKind {
98+ PositionalOnly ,
99+ PositionalOrKeyword ,
100+ Variadic ,
101+ KeywordOnly ,
102+ KeywordVariadic ,
48103}
49104
50105#[ salsa:: tracked]
@@ -131,6 +186,13 @@ impl Ty {
131186
132187 create_bound_method ( db, function, builtins_class)
133188 }
189+ Ty :: Callable { params, returns } => Type :: Callable ( CallableType :: new (
190+ db,
191+ Signature :: new (
192+ params. into_parameters ( db) ,
193+ returns. map ( |ty| ty. into_type ( db) ) ,
194+ ) ,
195+ ) ) ,
134196 }
135197 }
136198}
@@ -205,7 +267,7 @@ fn arbitrary_type(g: &mut Gen, size: u32) -> Ty {
205267 if size == 0 {
206268 arbitrary_core_type ( g)
207269 } else {
208- match u32:: arbitrary ( g) % 4 {
270+ match u32:: arbitrary ( g) % 5 {
209271 0 => arbitrary_core_type ( g) ,
210272 1 => Ty :: Union (
211273 ( 0 ..* g. choose ( & [ 2 , 3 ] ) . unwrap ( ) )
@@ -225,11 +287,103 @@ fn arbitrary_type(g: &mut Gen, size: u32) -> Ty {
225287 . map ( |_| arbitrary_type ( g, size - 1 ) )
226288 . collect ( ) ,
227289 } ,
290+ 4 => Ty :: Callable {
291+ params : match u32:: arbitrary ( g) % 2 {
292+ 0 => CallableParams :: GradualForm ,
293+ 1 => CallableParams :: List ( arbitrary_parameter_list ( g, size) ) ,
294+ _ => unreachable ! ( ) ,
295+ } ,
296+ returns : arbitrary_optional_type ( g, size - 1 ) . map ( Box :: new) ,
297+ } ,
228298 _ => unreachable ! ( ) ,
229299 }
230300 }
231301}
232302
303+ fn arbitrary_parameter_list ( g : & mut Gen , size : u32 ) -> Vec < Param > {
304+ let mut params: Vec < Param > = vec ! [ ] ;
305+ let mut used_names = HashSet :: new ( ) ;
306+
307+ // First, choose the number of parameters to generate.
308+ for _ in 0 ..* g. choose ( & [ 0 , 1 , 2 , 3 , 4 , 5 ] ) . unwrap ( ) {
309+ // Next, choose the kind of parameters that can be generated based on the last parameter.
310+ let next_kind = match params. last ( ) . map ( |p| p. kind ) {
311+ None | Some ( ParamKind :: PositionalOnly ) => * g
312+ . choose ( & [
313+ ParamKind :: PositionalOnly ,
314+ ParamKind :: PositionalOrKeyword ,
315+ ParamKind :: Variadic ,
316+ ParamKind :: KeywordOnly ,
317+ ParamKind :: KeywordVariadic ,
318+ ] )
319+ . unwrap ( ) ,
320+ Some ( ParamKind :: PositionalOrKeyword ) => * g
321+ . choose ( & [
322+ ParamKind :: PositionalOrKeyword ,
323+ ParamKind :: Variadic ,
324+ ParamKind :: KeywordOnly ,
325+ ParamKind :: KeywordVariadic ,
326+ ] )
327+ . unwrap ( ) ,
328+ Some ( ParamKind :: Variadic | ParamKind :: KeywordOnly ) => * g
329+ . choose ( & [ ParamKind :: KeywordOnly , ParamKind :: KeywordVariadic ] )
330+ . unwrap ( ) ,
331+ Some ( ParamKind :: KeywordVariadic ) => {
332+ // There can't be any other parameter kind after a keyword variadic parameter.
333+ break ;
334+ }
335+ } ;
336+
337+ let name = loop {
338+ let name = if matches ! ( next_kind, ParamKind :: PositionalOnly ) {
339+ arbitrary_optional_name ( g)
340+ } else {
341+ Some ( arbitrary_name ( g) )
342+ } ;
343+ if let Some ( name) = name {
344+ if used_names. insert ( name. clone ( ) ) {
345+ break Some ( name) ;
346+ }
347+ } else {
348+ break None ;
349+ }
350+ } ;
351+
352+ params. push ( Param {
353+ kind : next_kind,
354+ name,
355+ annotated_ty : arbitrary_optional_type ( g, size) ,
356+ default_ty : if matches ! ( next_kind, ParamKind :: Variadic | ParamKind :: KeywordVariadic ) {
357+ None
358+ } else {
359+ arbitrary_optional_type ( g, size)
360+ } ,
361+ } ) ;
362+ }
363+
364+ params
365+ }
366+
367+ fn arbitrary_optional_type ( g : & mut Gen , size : u32 ) -> Option < Ty > {
368+ match u32:: arbitrary ( g) % 2 {
369+ 0 => None ,
370+ 1 => Some ( arbitrary_type ( g, size) ) ,
371+ _ => unreachable ! ( ) ,
372+ }
373+ }
374+
375+ fn arbitrary_name ( g : & mut Gen ) -> Name {
376+ Name :: new ( format ! ( "n{}" , u32 :: arbitrary( g) % 10 ) )
377+ }
378+
379+ fn arbitrary_optional_name ( g : & mut Gen ) -> Option < Name > {
380+ match u32:: arbitrary ( g) % 2 {
381+ 0 => None ,
382+ 1 => Some ( arbitrary_name ( g) ) ,
383+ _ => unreachable ! ( ) ,
384+ }
385+ }
386+
233387impl Arbitrary for Ty {
234388 fn arbitrary ( g : & mut Gen ) -> Ty {
235389 const MAX_SIZE : u32 = 2 ;
0 commit comments