From 5ce7811b74d5af9c6d43594728bfa445f23e089f Mon Sep 17 00:00:00 2001 From: 11happy Date: Sun, 12 Oct 2025 15:48:01 +0530 Subject: [PATCH 01/11] feat: implement semantic syntax error annotated name connot be global Signed-off-by: 11happy --- ...AnnotatedGlobal_annotated_global_3.10.snap | 33 +++++++++++++++++++ .../ruff_python_parser/src/semantic_errors.rs | 13 +++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap new file mode 100644 index 0000000000..6611db27bc --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap @@ -0,0 +1,33 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +invalid-syntax: annotated name `a` can't be global + --> :5:5 + | +3 | def f1(): +4 | global a +5 | a: str = 'foo' + | ^ +6 | +7 | b: int = 1 + | + +invalid-syntax: annotated name `b` can't be global + --> :11:9 + | + 9 | def inner1(): +10 | global b +11 | b: str = 'nested' + | ^ +12 | +13 | c: int = 1 + | + +invalid-syntax: annotated name `c` can't be global + --> :16:5 + | +14 | def f2(): +15 | global c +16 | c: list[str] = [] + | ^ + | diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index 0c7ceef4a4..dd3c4a832f 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -272,7 +272,9 @@ impl SemanticSyntaxChecker { fn check_annotation(stmt: &ast::Stmt, ctx: &Ctx) { match stmt { - Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) => { + Stmt::AnnAssign(ast::StmtAnnAssign { + target, annotation, .. + }) => { if ctx.python_version() > PythonVersion::PY313 { // test_ok valid_annotation_py313 // # parse_options: {"target-version": "3.13"} @@ -297,6 +299,15 @@ impl SemanticSyntaxChecker { }; visitor.visit_expr(annotation); } + if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { + if ctx.global(id.as_str()).is_some() { + Self::add_error( + ctx, + SemanticSyntaxErrorKind::AnnotatedGlobal(id.to_string()), + target.range(), + ); + } + } } Stmt::FunctionDef(ast::StmtFunctionDef { type_params, From 472bf6ebfc3814ea1746dc17858db779df9ca85b Mon Sep 17 00:00:00 2001 From: 11happy Date: Wed, 15 Oct 2025 21:50:25 +0530 Subject: [PATCH 02/11] test: add more test & update snapshot Signed-off-by: 11happy --- ...error_AnnotatedGlobal_annotated_global_3.10.snap | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap index 6611db27bc..92830e2891 100644 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap @@ -30,4 +30,17 @@ invalid-syntax: annotated name `c` can't be global 15 | global c 16 | c: list[str] = [] | ^ +17 | +18 | d: int = 1 + | + +invalid-syntax: annotated name `d` can't be global + --> :21:5 + | +19 | def f3(): +20 | global d +21 | d: str + | ^ +22 | +23 | e: int = 1 | From 181ead1d3d7870c75e5c4b51e5793a1fe7a34e57 Mon Sep 17 00:00:00 2001 From: 11happy Date: Wed, 22 Oct 2025 21:37:47 +0530 Subject: [PATCH 03/11] test add new edge case Signed-off-by: 11happy --- ...yntax_error_AnnotatedGlobal_annotated_global_3.10.snap | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap index 92830e2891..d038561f17 100644 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap @@ -44,3 +44,11 @@ invalid-syntax: annotated name `d` can't be global 22 | 23 | e: int = 1 | + +invalid-syntax: annotated name `f` can't be global + --> :28:1 + | +27 | global f +28 | f: int + | ^ + | From 1398a7516ff72108a374f859dbb6579fc2807140 Mon Sep 17 00:00:00 2001 From: 11happy Date: Fri, 5 Dec 2025 07:22:35 +0000 Subject: [PATCH 04/11] merge & fix false positive Signed-off-by: 11happy --- .../semantic_errors/annonated_global.py | 27 +++++++++++ crates/ruff_linter/src/linter.rs | 1 + ...syntax_error_annonated_global.py_3.14.snap | 46 +++++++++++++++++++ .../ruff_python_parser/src/semantic_errors.rs | 2 +- 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap diff --git a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py new file mode 100644 index 0000000000..2614c60e4a --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py @@ -0,0 +1,27 @@ +a: int = 1 +def f1(): + global a + a: str = "foo" + +b: int = 1 +def outer(): + def inner(): + global b + b: str = "nested" + +c: int = 1 +def f2(): + global c + c: list[str] = [] + +d: int = 1 +def f3(): + global d + d: str + +e: int = 1 +def f4(): + e: str = "happy" + +global f +f: int = 1 \ No newline at end of file diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 719d5ac9c5..f19799cdf0 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -982,6 +982,7 @@ mod tests { #[test_case(Path::new("write_to_debug.py"), PythonVersion::PY310)] #[test_case(Path::new("invalid_expression.py"), PythonVersion::PY312)] #[test_case(Path::new("global_parameter.py"), PythonVersion::PY310)] + #[test_case(Path::new("annotated_global.py"), PythonVersion::latest())] fn test_semantic_errors(path: &Path, python_version: PythonVersion) -> Result<()> { let snapshot = format!( "semantic_syntax_error_{}_{}", diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap new file mode 100644 index 0000000000..cea88632e7 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap @@ -0,0 +1,46 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +invalid-syntax: annotated name `a` can't be global + --> resources/test/fixtures/semantic_errors/annonated_global.py:4:5 + | +2 | def f1(): +3 | global a +4 | a: str = "foo" + | ^ +5 | +6 | b: int = 1 + | + +invalid-syntax: annotated name `b` can't be global + --> resources/test/fixtures/semantic_errors/annonated_global.py:10:9 + | + 8 | def inner(): + 9 | global b +10 | b: str = "nested" + | ^ +11 | +12 | c: int = 1 + | + +invalid-syntax: annotated name `c` can't be global + --> resources/test/fixtures/semantic_errors/annonated_global.py:15:5 + | +13 | def f2(): +14 | global c +15 | c: list[str] = [] + | ^ +16 | +17 | d: int = 1 + | + +invalid-syntax: annotated name `d` can't be global + --> resources/test/fixtures/semantic_errors/annonated_global.py:20:5 + | +18 | def f3(): +19 | global d +20 | d: str + | ^ +21 | +22 | e: int = 1 + | diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index dd3c4a832f..5fdf41f0c2 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -300,7 +300,7 @@ impl SemanticSyntaxChecker { visitor.visit_expr(annotation); } if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { - if ctx.global(id.as_str()).is_some() { + if ctx.global(id.as_str()).is_some() && ctx.in_function_scope() { Self::add_error( ctx, SemanticSyntaxErrorKind::AnnotatedGlobal(id.to_string()), From 8f021b7e0510b3fbb577a575f8a9ea7d81d099d7 Mon Sep 17 00:00:00 2001 From: Bhuminjay Soni Date: Fri, 5 Dec 2025 12:57:55 +0530 Subject: [PATCH 05/11] delete prev snap --- ...AnnotatedGlobal_annotated_global_3.10.snap | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap deleted file mode 100644 index d038561f17..0000000000 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_AnnotatedGlobal_annotated_global_3.10.snap +++ /dev/null @@ -1,54 +0,0 @@ ---- -source: crates/ruff_linter/src/linter.rs ---- -invalid-syntax: annotated name `a` can't be global - --> :5:5 - | -3 | def f1(): -4 | global a -5 | a: str = 'foo' - | ^ -6 | -7 | b: int = 1 - | - -invalid-syntax: annotated name `b` can't be global - --> :11:9 - | - 9 | def inner1(): -10 | global b -11 | b: str = 'nested' - | ^ -12 | -13 | c: int = 1 - | - -invalid-syntax: annotated name `c` can't be global - --> :16:5 - | -14 | def f2(): -15 | global c -16 | c: list[str] = [] - | ^ -17 | -18 | d: int = 1 - | - -invalid-syntax: annotated name `d` can't be global - --> :21:5 - | -19 | def f3(): -20 | global d -21 | d: str - | ^ -22 | -23 | e: int = 1 - | - -invalid-syntax: annotated name `f` can't be global - --> :28:1 - | -27 | global f -28 | f: int - | ^ - | From 088f889587c59006c12039f8c648d19b3dc591a1 Mon Sep 17 00:00:00 2001 From: 11happy Date: Fri, 5 Dec 2025 07:43:11 +0000 Subject: [PATCH 06/11] snap name Signed-off-by: 11happy --- crates/ruff_linter/src/linter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index f19799cdf0..d85a75ec75 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -982,7 +982,7 @@ mod tests { #[test_case(Path::new("write_to_debug.py"), PythonVersion::PY310)] #[test_case(Path::new("invalid_expression.py"), PythonVersion::PY312)] #[test_case(Path::new("global_parameter.py"), PythonVersion::PY310)] - #[test_case(Path::new("annotated_global.py"), PythonVersion::latest())] + #[test_case(Path::new("annotated_global.py"), PythonVersion::PY314)] fn test_semantic_errors(path: &Path, python_version: PythonVersion) -> Result<()> { let snapshot = format!( "semantic_syntax_error_{}_{}", From 8eb769f542ddc51b0e2d9934b262ddccb02f6191 Mon Sep 17 00:00:00 2001 From: 11happy Date: Fri, 5 Dec 2025 08:22:37 +0000 Subject: [PATCH 07/11] fix spelling Signed-off-by: 11happy --- .../semantic_errors/annotated_global.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py diff --git a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py new file mode 100644 index 0000000000..2614c60e4a --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py @@ -0,0 +1,27 @@ +a: int = 1 +def f1(): + global a + a: str = "foo" + +b: int = 1 +def outer(): + def inner(): + global b + b: str = "nested" + +c: int = 1 +def f2(): + global c + c: list[str] = [] + +d: int = 1 +def f3(): + global d + d: str + +e: int = 1 +def f4(): + e: str = "happy" + +global f +f: int = 1 \ No newline at end of file From 7c8cf7c4d3a409351e262c4f9eec6620c5923873 Mon Sep 17 00:00:00 2001 From: 11happy Date: Fri, 5 Dec 2025 08:29:04 +0000 Subject: [PATCH 08/11] remove prev file Signed-off-by: 11happy --- .../semantic_errors/annonated_global.py | 27 ----------- ...syntax_error_annotated_global.py_3.14.snap | 46 +++++++++++++++++++ 2 files changed, 46 insertions(+), 27 deletions(-) delete mode 100644 crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py create mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap diff --git a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py deleted file mode 100644 index 2614c60e4a..0000000000 --- a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annonated_global.py +++ /dev/null @@ -1,27 +0,0 @@ -a: int = 1 -def f1(): - global a - a: str = "foo" - -b: int = 1 -def outer(): - def inner(): - global b - b: str = "nested" - -c: int = 1 -def f2(): - global c - c: list[str] = [] - -d: int = 1 -def f3(): - global d - d: str - -e: int = 1 -def f4(): - e: str = "happy" - -global f -f: int = 1 \ No newline at end of file diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap new file mode 100644 index 0000000000..ae70243ad1 --- /dev/null +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap @@ -0,0 +1,46 @@ +--- +source: crates/ruff_linter/src/linter.rs +--- +invalid-syntax: annotated name `a` can't be global + --> resources/test/fixtures/semantic_errors/annotated_global.py:4:5 + | +2 | def f1(): +3 | global a +4 | a: str = "foo" + | ^ +5 | +6 | b: int = 1 + | + +invalid-syntax: annotated name `b` can't be global + --> resources/test/fixtures/semantic_errors/annotated_global.py:10:9 + | + 8 | def inner(): + 9 | global b +10 | b: str = "nested" + | ^ +11 | +12 | c: int = 1 + | + +invalid-syntax: annotated name `c` can't be global + --> resources/test/fixtures/semantic_errors/annotated_global.py:15:5 + | +13 | def f2(): +14 | global c +15 | c: list[str] = [] + | ^ +16 | +17 | d: int = 1 + | + +invalid-syntax: annotated name `d` can't be global + --> resources/test/fixtures/semantic_errors/annotated_global.py:20:5 + | +18 | def f3(): +19 | global d +20 | d: str + | ^ +21 | +22 | e: int = 1 + | From 5469fee677c29f3dadf2f8d9bf07c91ad65ba8d8 Mon Sep 17 00:00:00 2001 From: Bhuminjay Soni Date: Fri, 5 Dec 2025 13:59:32 +0530 Subject: [PATCH 09/11] Delete crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap --- ...syntax_error_annonated_global.py_3.14.snap | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap deleted file mode 100644 index cea88632e7..0000000000 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annonated_global.py_3.14.snap +++ /dev/null @@ -1,46 +0,0 @@ ---- -source: crates/ruff_linter/src/linter.rs ---- -invalid-syntax: annotated name `a` can't be global - --> resources/test/fixtures/semantic_errors/annonated_global.py:4:5 - | -2 | def f1(): -3 | global a -4 | a: str = "foo" - | ^ -5 | -6 | b: int = 1 - | - -invalid-syntax: annotated name `b` can't be global - --> resources/test/fixtures/semantic_errors/annonated_global.py:10:9 - | - 8 | def inner(): - 9 | global b -10 | b: str = "nested" - | ^ -11 | -12 | c: int = 1 - | - -invalid-syntax: annotated name `c` can't be global - --> resources/test/fixtures/semantic_errors/annonated_global.py:15:5 - | -13 | def f2(): -14 | global c -15 | c: list[str] = [] - | ^ -16 | -17 | d: int = 1 - | - -invalid-syntax: annotated name `d` can't be global - --> resources/test/fixtures/semantic_errors/annonated_global.py:20:5 - | -18 | def f3(): -19 | global d -20 | d: str - | ^ -21 | -22 | e: int = 1 - | From d233adf92bda004a9b20bd1b13669f4e173a2c51 Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Mon, 8 Dec 2025 14:55:03 -0500 Subject: [PATCH 10/11] annotate tests with expected results, add module-level error case --- .../fixtures/semantic_errors/annotated_global.py | 15 +++++++++------ ...tic_syntax_error_annotated_global.py_3.14.snap | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py index 2614c60e4a..581beed7f3 100644 --- a/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py +++ b/crates/ruff_linter/resources/test/fixtures/semantic_errors/annotated_global.py @@ -1,27 +1,30 @@ a: int = 1 def f1(): global a - a: str = "foo" + a: str = "foo" # error b: int = 1 def outer(): def inner(): global b - b: str = "nested" + b: str = "nested" # error c: int = 1 def f2(): global c - c: list[str] = [] + c: list[str] = [] # error d: int = 1 def f3(): global d - d: str + d: str # error e: int = 1 def f4(): - e: str = "happy" + e: str = "happy" # okay global f -f: int = 1 \ No newline at end of file +f: int = 1 # okay + +g: int = 1 +global g # error diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap index ae70243ad1..c8d3eb32f6 100644 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap @@ -6,7 +6,7 @@ invalid-syntax: annotated name `a` can't be global | 2 | def f1(): 3 | global a -4 | a: str = "foo" +4 | a: str = "foo" # error | ^ 5 | 6 | b: int = 1 @@ -17,7 +17,7 @@ invalid-syntax: annotated name `b` can't be global | 8 | def inner(): 9 | global b -10 | b: str = "nested" +10 | b: str = "nested" # error | ^ 11 | 12 | c: int = 1 @@ -28,7 +28,7 @@ invalid-syntax: annotated name `c` can't be global | 13 | def f2(): 14 | global c -15 | c: list[str] = [] +15 | c: list[str] = [] # error | ^ 16 | 17 | d: int = 1 @@ -39,7 +39,7 @@ invalid-syntax: annotated name `d` can't be global | 18 | def f3(): 19 | global d -20 | d: str +20 | d: str # error | ^ 21 | 22 | e: int = 1 From bce4e2ca2bde057c1b870576f8f0a42b73fe0dfc Mon Sep 17 00:00:00 2001 From: 11happy Date: Sun, 14 Dec 2025 13:45:05 +0530 Subject: [PATCH 11/11] check start location for target and global Signed-off-by: 11happy --- ...tic_syntax_error_annotated_global.py_3.14.snap | 10 ++++++++++ crates/ruff_python_parser/src/semantic_errors.rs | 15 +++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap index c8d3eb32f6..98735e9b5d 100644 --- a/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap +++ b/crates/ruff_linter/src/snapshots/ruff_linter__linter__tests__semantic_syntax_error_annotated_global.py_3.14.snap @@ -44,3 +44,13 @@ invalid-syntax: annotated name `d` can't be global 21 | 22 | e: int = 1 | + +invalid-syntax: annotated name `g` can't be global + --> resources/test/fixtures/semantic_errors/annotated_global.py:29:1 + | +27 | f: int = 1 # okay +28 | +29 | g: int = 1 + | ^ +30 | global g # error + | diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index 5fdf41f0c2..29bc07c47c 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -300,12 +300,15 @@ impl SemanticSyntaxChecker { visitor.visit_expr(annotation); } if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() { - if ctx.global(id.as_str()).is_some() && ctx.in_function_scope() { - Self::add_error( - ctx, - SemanticSyntaxErrorKind::AnnotatedGlobal(id.to_string()), - target.range(), - ); + if let Some(global_stmt) = ctx.global(id.as_str()) { + let global_start = global_stmt.start(); + if ctx.in_function_scope() || target.start() < global_start { + Self::add_error( + ctx, + SemanticSyntaxErrorKind::AnnotatedGlobal(id.to_string()), + target.range(), + ); + } } } }