mirror of
https://github.com/python/cpython.git
synced 2025-07-08 03:45:36 +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