From c7e2bfd7592d765df07b87ad64755d5fa9037fa4 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 17 Oct 2025 18:13:40 +0100 Subject: [PATCH] [ty] `continue` and `break` statements outside loops are syntax errors (#20944) Co-authored-by: Brent Westbrook --- .../diagnostics/semantic_syntax_errors.md | 22 ++++ ...-_`break`_and_`continu…_(3143ba0a999d644).snap | 107 ++++++++++++++++++ .../src/semantic_index/builder.rs | 2 +- 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 crates/ty_python_semantic/resources/mdtest/snapshots/semantic_syntax_erro…_-_Semantic_syntax_erro…_-_`break`_and_`continu…_(3143ba0a999d644).snap diff --git a/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md b/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md index 5e77445b07..e3c830a1e8 100644 --- a/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md +++ b/crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md @@ -354,3 +354,25 @@ def f(): x = 1 global x # error: [invalid-syntax] "name `x` is used prior to global declaration" ``` + +## `break` and `continue` outside a loop + + + +```py +break # error: [invalid-syntax] +continue # error: [invalid-syntax] + +for x in range(42): + break # fine + continue # fine + + def _(): + break # error: [invalid-syntax] + continue # error: [invalid-syntax] + + class Fine: + # this is invalid syntax despite it being in an eager-nested scope! + break # error: [invalid-syntax] + continue # error: [invalid-syntax] +``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/semantic_syntax_erro…_-_Semantic_syntax_erro…_-_`break`_and_`continu…_(3143ba0a999d644).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/semantic_syntax_erro…_-_Semantic_syntax_erro…_-_`break`_and_`continu…_(3143ba0a999d644).snap new file mode 100644 index 0000000000..cd814dd61a --- /dev/null +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/semantic_syntax_erro…_-_Semantic_syntax_erro…_-_`break`_and_`continu…_(3143ba0a999d644).snap @@ -0,0 +1,107 @@ +--- +source: crates/ty_test/src/lib.rs +expression: snapshot +--- +--- +mdtest name: semantic_syntax_errors.md - Semantic syntax error diagnostics - `break` and `continue` outside a loop +mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/semantic_syntax_errors.md +--- + +# Python source files + +## mdtest_snippet.py + +``` + 1 | break # error: [invalid-syntax] + 2 | continue # error: [invalid-syntax] + 3 | + 4 | for x in range(42): + 5 | break # fine + 6 | continue # fine + 7 | + 8 | def _(): + 9 | break # error: [invalid-syntax] +10 | continue # error: [invalid-syntax] +11 | +12 | class Fine: +13 | # this is invalid syntax despite it being in an eager-nested scope! +14 | break # error: [invalid-syntax] +15 | continue # error: [invalid-syntax] +``` + +# Diagnostics + +``` +error[invalid-syntax]: `break` outside loop + --> src/mdtest_snippet.py:1:1 + | +1 | break # error: [invalid-syntax] + | ^^^^^ +2 | continue # error: [invalid-syntax] + | + +``` + +``` +error[invalid-syntax]: `continue` outside loop + --> src/mdtest_snippet.py:2:1 + | +1 | break # error: [invalid-syntax] +2 | continue # error: [invalid-syntax] + | ^^^^^^^^ +3 | +4 | for x in range(42): + | + +``` + +``` +error[invalid-syntax]: `break` outside loop + --> src/mdtest_snippet.py:9:9 + | + 8 | def _(): + 9 | break # error: [invalid-syntax] + | ^^^^^ +10 | continue # error: [invalid-syntax] + | + +``` + +``` +error[invalid-syntax]: `continue` outside loop + --> src/mdtest_snippet.py:10:9 + | + 8 | def _(): + 9 | break # error: [invalid-syntax] +10 | continue # error: [invalid-syntax] + | ^^^^^^^^ +11 | +12 | class Fine: + | + +``` + +``` +error[invalid-syntax]: `break` outside loop + --> src/mdtest_snippet.py:14:9 + | +12 | class Fine: +13 | # this is invalid syntax despite it being in an eager-nested scope! +14 | break # error: [invalid-syntax] + | ^^^^^ +15 | continue # error: [invalid-syntax] + | + +``` + +``` +error[invalid-syntax]: `continue` outside loop + --> src/mdtest_snippet.py:15:9 + | +13 | # this is invalid syntax despite it being in an eager-nested scope! +14 | break # error: [invalid-syntax] +15 | continue # error: [invalid-syntax] + | ^^^^^^^^ + | + +``` diff --git a/crates/ty_python_semantic/src/semantic_index/builder.rs b/crates/ty_python_semantic/src/semantic_index/builder.rs index 01de16b8a6..5bf8cbb3e7 100644 --- a/crates/ty_python_semantic/src/semantic_index/builder.rs +++ b/crates/ty_python_semantic/src/semantic_index/builder.rs @@ -2787,7 +2787,7 @@ impl SemanticSyntaxContext for SemanticIndexBuilder<'_, '_> { } fn in_loop_context(&self) -> bool { - true + self.current_scope_info().current_loop.is_some() } }