mirror of
				https://github.com/python/cpython.git
				synced 2025-11-01 06:01:29 +00:00 
			
		
		
		
	gh-119933: Improve `SyntaxError` message for invalid type parameters expressions (#119976)
				
					
				
			Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
		
							parent
							
								
									274f844830
								
							
						
					
					
						commit
						4bf17c381f
					
				
					 9 changed files with 277 additions and 55 deletions
				
			
		|  | @ -58,13 +58,13 @@ | |||
| #define ANNOTATION_NOT_ALLOWED \ | ||||
| "%s cannot be used within an annotation" | ||||
| 
 | ||||
| #define TYPEVAR_BOUND_NOT_ALLOWED \ | ||||
| "%s cannot be used within a TypeVar bound" | ||||
| #define EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE \ | ||||
| "%s cannot be used within %s" | ||||
| 
 | ||||
| #define TYPEALIAS_NOT_ALLOWED \ | ||||
| #define EXPR_NOT_ALLOWED_IN_TYPE_ALIAS \ | ||||
| "%s cannot be used within a type alias" | ||||
| 
 | ||||
| #define TYPEPARAM_NOT_ALLOWED \ | ||||
| #define EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS \ | ||||
| "%s cannot be used within the definition of a generic" | ||||
| 
 | ||||
| #define DUPLICATE_TYPE_PARAM \ | ||||
|  | @ -106,6 +106,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block, | |||
|     ste->ste_mangled_names = NULL; | ||||
| 
 | ||||
|     ste->ste_type = block; | ||||
|     ste->ste_scope_info = NULL; | ||||
| 
 | ||||
|     ste->ste_nested = 0; | ||||
|     ste->ste_free = 0; | ||||
|     ste->ste_varargs = 0; | ||||
|  | @ -269,9 +271,9 @@ static void _dump_symtable(PySTEntryObject* ste, PyObject* prefix) | |||
|         case ClassBlock: blocktype = "ClassBlock"; break; | ||||
|         case ModuleBlock: blocktype = "ModuleBlock"; break; | ||||
|         case AnnotationBlock: blocktype = "AnnotationBlock"; break; | ||||
|         case TypeVarBoundBlock: blocktype = "TypeVarBoundBlock"; break; | ||||
|         case TypeVariableBlock: blocktype = "TypeVariableBlock"; break; | ||||
|         case TypeAliasBlock: blocktype = "TypeAliasBlock"; break; | ||||
|         case TypeParamBlock: blocktype = "TypeParamBlock"; break; | ||||
|         case TypeParametersBlock: blocktype = "TypeParametersBlock"; break; | ||||
|     } | ||||
|     const char *comptype = ""; | ||||
|     switch (ste->ste_comprehension) { | ||||
|  | @ -544,9 +546,9 @@ _PyST_IsFunctionLike(PySTEntryObject *ste) | |||
| { | ||||
|     return ste->ste_type == FunctionBlock | ||||
|         || ste->ste_type == AnnotationBlock | ||||
|         || ste->ste_type == TypeVarBoundBlock | ||||
|         || ste->ste_type == TypeVariableBlock | ||||
|         || ste->ste_type == TypeAliasBlock | ||||
|         || ste->ste_type == TypeParamBlock; | ||||
|         || ste->ste_type == TypeParametersBlock; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
|  | @ -1519,7 +1521,7 @@ symtable_enter_type_param_block(struct symtable *st, identifier name, | |||
|                                int end_lineno, int end_col_offset) | ||||
| { | ||||
|     _Py_block_ty current_type = st->st_cur->ste_type; | ||||
|     if(!symtable_enter_block(st, name, TypeParamBlock, ast, lineno, | ||||
|     if(!symtable_enter_block(st, name, TypeParametersBlock, ast, lineno, | ||||
|                              col_offset, end_lineno, end_col_offset)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | @ -2122,20 +2124,20 @@ symtable_extend_namedexpr_scope(struct symtable *st, expr_ty e) | |||
|         } | ||||
|         /* Disallow usage in ClassBlock and type scopes */ | ||||
|         if (ste->ste_type == ClassBlock || | ||||
|             ste->ste_type == TypeParamBlock || | ||||
|             ste->ste_type == TypeParametersBlock || | ||||
|             ste->ste_type == TypeAliasBlock || | ||||
|             ste->ste_type == TypeVarBoundBlock) { | ||||
|             ste->ste_type == TypeVariableBlock) { | ||||
|             switch (ste->ste_type) { | ||||
|                 case ClassBlock: | ||||
|                     PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_CLASS); | ||||
|                     break; | ||||
|                 case TypeParamBlock: | ||||
|                 case TypeParametersBlock: | ||||
|                     PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEPARAM); | ||||
|                     break; | ||||
|                 case TypeAliasBlock: | ||||
|                     PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEALIAS); | ||||
|                     break; | ||||
|                 case TypeVarBoundBlock: | ||||
|                 case TypeVariableBlock: | ||||
|                     PyErr_Format(PyExc_SyntaxError, NAMED_EXPR_COMP_IN_TYPEVAR_BOUND); | ||||
|                     break; | ||||
|                 default: | ||||
|  | @ -2341,19 +2343,27 @@ symtable_visit_expr(struct symtable *st, expr_ty e) | |||
| } | ||||
| 
 | ||||
| static int | ||||
| symtable_visit_type_param_bound_or_default(struct symtable *st, expr_ty e, identifier name, void *key) | ||||
| symtable_visit_type_param_bound_or_default( | ||||
|     struct symtable *st, expr_ty e, identifier name, | ||||
|     void *key, const char *ste_scope_info) | ||||
| { | ||||
|     if (e) { | ||||
|         int is_in_class = st->st_cur->ste_can_see_class_scope; | ||||
|         if (!symtable_enter_block(st, name, TypeVarBoundBlock, key, LOCATION(e))) | ||||
|         if (!symtable_enter_block(st, name, TypeVariableBlock, key, LOCATION(e))) | ||||
|             return 0; | ||||
| 
 | ||||
|         st->st_cur->ste_can_see_class_scope = is_in_class; | ||||
|         if (is_in_class && !symtable_add_def(st, &_Py_ID(__classdict__), USE, LOCATION(e))) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
| 
 | ||||
|         assert(ste_scope_info != NULL); | ||||
|         st->st_cur->ste_scope_info = ste_scope_info; | ||||
|         VISIT(st, expr, e); | ||||
|         if (!symtable_exit_block(st)) | ||||
| 
 | ||||
|         if (!symtable_exit_block(st)) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|     return 1; | ||||
| } | ||||
|  | @ -2371,6 +2381,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) | |||
|         if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) | ||||
|             VISIT_QUIT(st, 0); | ||||
| 
 | ||||
|         const char *ste_scope_info = NULL; | ||||
|         const expr_ty bound = tp->v.TypeVar.bound; | ||||
|         if (bound != NULL) { | ||||
|             ste_scope_info = bound->kind == Tuple_kind ? "a TypeVar constraint" : "a TypeVar bound"; | ||||
|         } | ||||
| 
 | ||||
|         // We must use a different key for the bound and default. The obvious choice would be to
 | ||||
|         // use the .bound and .default_value pointers, but that fails when the expression immediately
 | ||||
|         // inside the bound or default is a comprehension: we would reuse the same key for
 | ||||
|  | @ -2378,11 +2394,12 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) | |||
|         // The only requirement for the key is that it is unique and it matches the logic in
 | ||||
|         // compile.c where the scope is retrieved.
 | ||||
|         if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.bound, tp->v.TypeVar.name, | ||||
|                                                         (void *)tp)) { | ||||
|                                                         (void *)tp, ste_scope_info)) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
| 
 | ||||
|         if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVar.default_value, tp->v.TypeVar.name, | ||||
|                                                         (void *)((uintptr_t)tp + 1))) { | ||||
|                                                         (void *)((uintptr_t)tp + 1), "a TypeVar default")) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
|         break; | ||||
|  | @ -2390,8 +2407,9 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) | |||
|         if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
| 
 | ||||
|         if (!symtable_visit_type_param_bound_or_default(st, tp->v.TypeVarTuple.default_value, tp->v.TypeVarTuple.name, | ||||
|                                                         (void *)tp)) { | ||||
|                                                         (void *)tp, "a TypeVarTuple default")) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
|         break; | ||||
|  | @ -2399,8 +2417,9 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp) | |||
|         if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp))) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
| 
 | ||||
|         if (!symtable_visit_type_param_bound_or_default(st, tp->v.ParamSpec.default_value, tp->v.ParamSpec.name, | ||||
|                                                         (void *)tp)) { | ||||
|                                                         (void *)tp, "a ParamSpec default")) { | ||||
|             VISIT_QUIT(st, 0); | ||||
|         } | ||||
|         break; | ||||
|  | @ -2829,12 +2848,21 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t | |||
|     _Py_block_ty type = st->st_cur->ste_type; | ||||
|     if (type == AnnotationBlock) | ||||
|         PyErr_Format(PyExc_SyntaxError, ANNOTATION_NOT_ALLOWED, name); | ||||
|     else if (type == TypeVarBoundBlock) | ||||
|         PyErr_Format(PyExc_SyntaxError, TYPEVAR_BOUND_NOT_ALLOWED, name); | ||||
|     else if (type == TypeAliasBlock) | ||||
|         PyErr_Format(PyExc_SyntaxError, TYPEALIAS_NOT_ALLOWED, name); | ||||
|     else if (type == TypeParamBlock) | ||||
|         PyErr_Format(PyExc_SyntaxError, TYPEPARAM_NOT_ALLOWED, name); | ||||
|     else if (type == TypeVariableBlock) { | ||||
|         const char *info = st->st_cur->ste_scope_info; | ||||
|         assert(info != NULL); // e.g., info == "a ParamSpec default"
 | ||||
|         PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_VARIABLE, name, info); | ||||
|     } | ||||
|     else if (type == TypeAliasBlock) { | ||||
|         // for now, we do not have any extra information
 | ||||
|         assert(st->st_cur->ste_scope_info == NULL); | ||||
|         PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_ALIAS, name); | ||||
|     } | ||||
|     else if (type == TypeParametersBlock) { | ||||
|         // for now, we do not have any extra information
 | ||||
|         assert(st->st_cur->ste_scope_info == NULL); | ||||
|         PyErr_Format(PyExc_SyntaxError, EXPR_NOT_ALLOWED_IN_TYPE_PARAMETERS, name); | ||||
|     } | ||||
|     else | ||||
|         return 1; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Bénédikt Tran
						Bénédikt Tran