@@ -812,6 +812,117 @@ def test_other_bizarre_things_in_annotations_fail(self):
812812 )
813813 self .assertEqual (s , expected_failure_message )
814814
815+ def test_kwarg_splats_disallowed_in_function_call_annotations (self ):
816+ expected_error_msg = (
817+ "Error on line 0:\n "
818+ "Cannot use a kwarg splat in a function-call annotation\n "
819+ )
820+ dataset = (
821+ 'module fo\n fo.barbaz\n o: bool(**{None: "bang!"})' ,
822+ 'module fo\n fo.barbaz -> bool(**{None: "bang!"})' ,
823+ 'module fo\n fo.barbaz -> bool(**{"bang": 42})' ,
824+ 'module fo\n fo.barbaz\n o: bool(**{"bang": None})' ,
825+ )
826+ for fn in dataset :
827+ with self .subTest (fn = fn ):
828+ out = self .parse_function_should_fail (fn )
829+ self .assertEqual (out , expected_error_msg )
830+
831+ def test_self_param_placement (self ):
832+ expected_error_msg = (
833+ "Error on line 0:\n "
834+ "A 'self' parameter, if specified, must be the very first thing "
835+ "in the parameter block.\n "
836+ )
837+ block = """
838+ module foo
839+ foo.func
840+ a: int
841+ self: self(type="PyObject *")
842+ """
843+ out = self .parse_function_should_fail (block )
844+ self .assertEqual (out , expected_error_msg )
845+
846+ def test_self_param_cannot_be_optional (self ):
847+ expected_error_msg = (
848+ "Error on line 0:\n "
849+ "A 'self' parameter cannot be marked optional.\n "
850+ )
851+ block = """
852+ module foo
853+ foo.func
854+ self: self(type="PyObject *") = None
855+ """
856+ out = self .parse_function_should_fail (block )
857+ self .assertEqual (out , expected_error_msg )
858+
859+ def test_defining_class_param_placement (self ):
860+ expected_error_msg = (
861+ "Error on line 0:\n "
862+ "A 'defining_class' parameter, if specified, must either be the "
863+ "first thing in the parameter block, or come just after 'self'.\n "
864+ )
865+ block = """
866+ module foo
867+ foo.func
868+ self: self(type="PyObject *")
869+ a: int
870+ cls: defining_class
871+ """
872+ out = self .parse_function_should_fail (block )
873+ self .assertEqual (out , expected_error_msg )
874+
875+ def test_defining_class_param_cannot_be_optional (self ):
876+ expected_error_msg = (
877+ "Error on line 0:\n "
878+ "A 'defining_class' parameter cannot be marked optional.\n "
879+ )
880+ block = """
881+ module foo
882+ foo.func
883+ cls: defining_class(type="PyObject *") = None
884+ """
885+ out = self .parse_function_should_fail (block )
886+ self .assertEqual (out , expected_error_msg )
887+
888+ def test_unused_param (self ):
889+ block = self .parse ("""
890+ module foo
891+ foo.func
892+ fn: object
893+ k: float
894+ i: float(unused=True)
895+ /
896+ *
897+ flag: bool(unused=True) = False
898+ """ )
899+ sig = block .signatures [1 ] # Function index == 1
900+ params = sig .parameters
901+ conv = lambda fn : params [fn ].converter
902+ dataset = (
903+ {"name" : "fn" , "unused" : False },
904+ {"name" : "k" , "unused" : False },
905+ {"name" : "i" , "unused" : True },
906+ {"name" : "flag" , "unused" : True },
907+ )
908+ for param in dataset :
909+ name , unused = param .values ()
910+ with self .subTest (name = name , unused = unused ):
911+ p = conv (name )
912+ # Verify that the unused flag is parsed correctly.
913+ self .assertEqual (unused , p .unused )
914+
915+ # Now, check that we'll produce correct code.
916+ decl = p .simple_declaration (in_parser = False )
917+ if unused :
918+ self .assertIn ("Py_UNUSED" , decl )
919+ else :
920+ self .assertNotIn ("Py_UNUSED" , decl )
921+
922+ # Make sure the Py_UNUSED macro is not used in the parser body.
923+ parser_decl = p .simple_declaration (in_parser = True )
924+ self .assertNotIn ("Py_UNUSED" , parser_decl )
925+
815926 def parse (self , text ):
816927 c = FakeClinic ()
817928 parser = DSLParser (c )
0 commit comments