1+ // Contains functions that perform validation and parsing of arguments and parameters.
2+ // Checks apply both to functions and to lambdas.
13use crate :: ast;
24use crate :: error:: { LexicalError , LexicalErrorType } ;
35use rustc_hash:: FxHashSet ;
46
5- pub struct ArgumentList {
7+ pub ( crate ) struct ArgumentList {
68 pub args : Vec < ast:: Expr > ,
79 pub keywords : Vec < ast:: Keyword > ,
810}
911
1012type ParameterDefs = ( Vec < ast:: Arg > , Vec < ast:: Arg > , Vec < ast:: Expr > ) ;
1113type ParameterDef = ( ast:: Arg , Option < ast:: Expr > ) ;
1214
13- pub fn validate_arguments ( arguments : ast:: Arguments ) -> Result < ast:: Arguments , LexicalError > {
15+ // Perform validation of function/lambda arguments in a function definition.
16+ pub ( crate ) fn validate_arguments (
17+ arguments : ast:: Arguments ,
18+ ) -> Result < ast:: Arguments , LexicalError > {
1419 let mut all_args: Vec < & ast:: Located < ast:: ArgData > > = vec ! [ ] ;
1520
1621 all_args. extend ( arguments. posonlyargs . iter ( ) ) ;
@@ -29,6 +34,7 @@ pub fn validate_arguments(arguments: ast::Arguments) -> Result<ast::Arguments, L
2934 let mut all_arg_names = FxHashSet :: with_hasher ( Default :: default ( ) ) ;
3035 for arg in all_args {
3136 let arg_name = & arg. node . arg ;
37+ // Check for duplicate arguments in the function definition.
3238 if !all_arg_names. insert ( arg_name) {
3339 return Err ( LexicalError {
3440 error : LexicalErrorType :: DuplicateArgumentError ( arg_name. to_string ( ) ) ,
@@ -40,7 +46,8 @@ pub fn validate_arguments(arguments: ast::Arguments) -> Result<ast::Arguments, L
4046 Ok ( arguments)
4147}
4248
43- pub fn parse_params (
49+ // Parse parameters as supplied during a function/lambda *definition*.
50+ pub ( crate ) fn parse_params (
4451 params : ( Vec < ParameterDef > , Vec < ParameterDef > ) ,
4552) -> Result < ParameterDefs , LexicalError > {
4653 let mut pos_only = Vec :: with_capacity ( params. 0 . len ( ) ) ;
@@ -52,7 +59,7 @@ pub fn parse_params(
5259 defaults. push ( default) ;
5360 } else if !defaults. is_empty ( ) {
5461 // Once we have started with defaults, all remaining arguments must
55- // have defaults
62+ // have defaults.
5663 return Err ( LexicalError {
5764 error : LexicalErrorType :: DefaultArgumentError ,
5865 location : name. location ,
@@ -79,7 +86,8 @@ type FunctionArgument = (
7986 ast:: Expr ,
8087) ;
8188
82- pub fn parse_args ( func_args : Vec < FunctionArgument > ) -> Result < ArgumentList , LexicalError > {
89+ // Parse arguments as supplied during a function/lambda *call*.
90+ pub ( crate ) fn parse_args ( func_args : Vec < FunctionArgument > ) -> Result < ArgumentList , LexicalError > {
8391 let mut args = vec ! [ ] ;
8492 let mut keywords = vec ! [ ] ;
8593
@@ -89,6 +97,7 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
8997 for ( name, value) in func_args {
9098 match name {
9199 Some ( ( start, end, name) ) => {
100+ // Check for duplicate keyword arguments in the call.
92101 if let Some ( keyword_name) = & name {
93102 if keyword_names. contains ( keyword_name) {
94103 return Err ( LexicalError {
@@ -111,13 +120,14 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
111120 ) ) ;
112121 }
113122 None => {
114- // Allow starred arguments after keyword arguments but
115- // not after double-starred arguments.
123+ // Positional arguments mustn't follow keyword arguments.
116124 if !keywords. is_empty ( ) && !is_starred ( & value) {
117125 return Err ( LexicalError {
118126 error : LexicalErrorType :: PositionalArgumentError ,
119127 location : value. location ,
120128 } ) ;
129+ // Allow starred arguments after keyword arguments but
130+ // not after double-starred arguments.
121131 } else if double_starred {
122132 return Err ( LexicalError {
123133 error : LexicalErrorType :: UnpackedArgumentError ,
@@ -132,6 +142,86 @@ pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, Lexi
132142 Ok ( ArgumentList { args, keywords } )
133143}
134144
145+ // Check if an expression is a starred expression.
135146fn is_starred ( exp : & ast:: Expr ) -> bool {
136147 matches ! ( exp. node, ast:: ExprKind :: Starred { .. } )
137148}
149+
150+ #[ cfg( test) ]
151+ mod tests {
152+ use crate :: error:: { LexicalErrorType , ParseErrorType } ;
153+ use crate :: parser:: parse_program;
154+
155+ macro_rules! function_and_lambda {
156+ ( $( $name: ident: $code: expr, ) * ) => {
157+ $(
158+ #[ test]
159+ fn $name( ) {
160+ let parse_ast = parse_program( $code, "<test>" ) ;
161+ insta:: assert_debug_snapshot!( parse_ast) ;
162+ }
163+ ) *
164+ }
165+ }
166+
167+ function_and_lambda ! {
168+ test_function_no_args: "def f(): pass" ,
169+ test_function_pos_args: "def f(a, b, c): pass" ,
170+ test_function_pos_args_with_defaults: "def f(a, b=20, c=30): pass" ,
171+ test_function_kw_only_args: "def f(*, a, b, c): pass" ,
172+ test_function_kw_only_args_with_defaults: "def f(*, a, b=20, c=30): pass" ,
173+ test_function_pos_and_kw_only_args: "def f(a, b, c, *, d, e, f): pass" ,
174+ test_function_pos_and_kw_only_args_with_defaults: "def f(a, b, c, *, d, e=20, f=30): pass" ,
175+ test_function_pos_and_kw_only_args_with_defaults_and_varargs: "def f(a, b, c, *args, d, e=20, f=30): pass" ,
176+ test_function_pos_and_kw_only_args_with_defaults_and_varargs_and_kwargs: "def f(a, b, c, *args, d, e=20, f=30, **kwargs): pass" ,
177+ test_lambda_no_args: "lambda: 1" ,
178+ test_lambda_pos_args: "lambda a, b, c: 1" ,
179+ test_lambda_pos_args_with_defaults: "lambda a, b=20, c=30: 1" ,
180+ test_lambda_kw_only_args: "lambda *, a, b, c: 1" ,
181+ test_lambda_kw_only_args_with_defaults: "lambda *, a, b=20, c=30: 1" ,
182+ test_lambda_pos_and_kw_only_args: "lambda a, b, c, *, d, e: 0" ,
183+ }
184+
185+ fn function_parse_error ( src : & str ) -> LexicalErrorType {
186+ let parse_ast = parse_program ( src, "<test>" ) ;
187+ parse_ast
188+ . map_err ( |e| match e. error {
189+ ParseErrorType :: Lexical ( e) => e,
190+ _ => panic ! ( "Expected LexicalError" ) ,
191+ } )
192+ . expect_err ( "Expected error" )
193+ }
194+
195+ macro_rules! function_and_lambda_error {
196+ ( $( $name: ident: $code: expr, $error: expr, ) * ) => {
197+ $(
198+ #[ test]
199+ fn $name( ) {
200+ let error = function_parse_error( $code) ;
201+ assert_eq!( error, $error) ;
202+ }
203+ ) *
204+ }
205+ }
206+
207+ function_and_lambda_error ! {
208+ // Check definitions
209+ test_duplicates_f1: "def f(a, a): pass" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
210+ test_duplicates_f2: "def f(a, *, a): pass" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
211+ test_duplicates_f3: "def f(a, a=20): pass" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
212+ test_duplicates_f4: "def f(a, *a): pass" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
213+ test_duplicates_f5: "def f(a, *, **a): pass" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
214+ test_duplicates_l1: "lambda a, a: 1" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
215+ test_duplicates_l2: "lambda a, *, a: 1" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
216+ test_duplicates_l3: "lambda a, a=20: 1" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
217+ test_duplicates_l4: "lambda a, *a: 1" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
218+ test_duplicates_l5: "lambda a, *, **a: 1" , LexicalErrorType :: DuplicateArgumentError ( "a" . to_string( ) ) ,
219+ test_default_arg_error_f: "def f(a, b=20, c): pass" , LexicalErrorType :: DefaultArgumentError ,
220+ test_default_arg_error_l: "lambda a, b=20, c: 1" , LexicalErrorType :: DefaultArgumentError ,
221+
222+ // Check some calls.
223+ test_positional_arg_error_f: "f(b=20, c)" , LexicalErrorType :: PositionalArgumentError ,
224+ test_unpacked_arg_error_f: "f(**b, *c)" , LexicalErrorType :: UnpackedArgumentError ,
225+ test_duplicate_kw_f1: "f(a=20, a=30)" , LexicalErrorType :: DuplicateKeywordArgumentError ( "a" . to_string( ) ) ,
226+ }
227+ }
0 commit comments