diff options
author | Jelle Zijlstra <jelle.zijlstra@gmail.com> | 2024-06-17 15:01:49 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-17 15:01:49 (GMT) |
commit | 7c47f93dff878bdc43f5162dd878cbb375711570 (patch) | |
tree | 7b02b412d2bc0f718d9df2912fa0b6cd66205de1 /Python | |
parent | 03b89e3a3d155a06827f58d51238a731d8800cc9 (diff) | |
download | cpython-7c47f93dff878bdc43f5162dd878cbb375711570.zip cpython-7c47f93dff878bdc43f5162dd878cbb375711570.tar.gz cpython-7c47f93dff878bdc43f5162dd878cbb375711570.tar.bz2 |
[3.13] gh-119933: Improve ``SyntaxError`` message for invalid type parameters expressions (GH-119976) (#120641)
(cherry picked from commit 4bf17c381fb7b465f0f26aecb94a6c54cf9be2d3)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Diffstat (limited to 'Python')
-rw-r--r-- | Python/symtable.c | 80 |
1 files changed, 54 insertions, 26 deletions
diff --git a/Python/symtable.c b/Python/symtable.c index 35c1e4a..65dcf67 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -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; @@ -265,9 +267,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) { @@ -525,9 +527,9 @@ int _PyST_IsFunctionLike(PySTEntryObject *ste) { return ste->ste_type == FunctionBlock - || ste->ste_type == TypeVarBoundBlock + || ste->ste_type == TypeVariableBlock || ste->ste_type == TypeAliasBlock - || ste->ste_type == TypeParamBlock; + || ste->ste_type == TypeParametersBlock; } static int @@ -1494,7 +1496,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; } @@ -2069,20 +2071,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: @@ -2288,19 +2290,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; } @@ -2318,6 +2328,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 @@ -2325,11 +2341,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; @@ -2337,8 +2354,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; @@ -2346,8 +2364,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; @@ -2729,12 +2748,21 @@ symtable_raise_if_annotation_block(struct symtable *st, const char *name, expr_t enum _block_type 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; |