about summary refs log tree commit diff
path: root/tests/ui/conditional-compilation
diff options
context:
space:
mode:
authorNicholas Nethercote <n.nethercote@gmail.com>2024-08-20 17:47:53 +1000
committerNicholas Nethercote <n.nethercote@gmail.com>2024-08-23 14:40:08 +1000
commit1ae521e9d57ba3a67b1007204da2836d8b19b4a2 (patch)
tree5ea26dfc9754198bb0b6861a95fef8dd5b976fef /tests/ui/conditional-compilation
parent312ecdb2ed37249ad539bc0732278b80b4f3c7f8 (diff)
downloadrust-1ae521e9d57ba3a67b1007204da2836d8b19b4a2.tar.gz
rust-1ae521e9d57ba3a67b1007204da2836d8b19b4a2.zip
Return earlier in some cases in `collect_token`.
This example triggers an assertion failure:
```
fn f() -> u32 {
    #[cfg_eval] #[cfg(not(FALSE))] 0
}
```
The sequence of events:
- `configure_annotatable` calls `parse_expr_force_collect`, which calls
  `collect_tokens`.
- Within that, we end up in `parse_expr_dot_or_call`, which again calls
  `collect_tokens`.
  - The return value of the `f` call is the expression `0`.
  - This inner call collects tokens for `0` (parser range 10..11) and
    creates a replacement covering `#[cfg(not(FALSE))] 0` (parser range
    0..11).
- We return to the outer `collect_tokens` call. The return value of the
  `f` call is *again* the expression `0`, again with the range 10..11,
  but the replacement from earlier covers the range 0..11. The code
  mistakenly assumes that any attributes from an inner `collect_tokens`
  call fit entirely within the body of the result of an outer
  `collect_tokens` call. So it adjusts the replacement parser range
  0..11 to a node range by subtracting 10, resulting in -10..1. This is
  an invalid range and triggers an assertion failure.

It's tricky to follow, but basically things get complicated when an AST
node is returned from an inner `collect_tokens` call and then returned
again from an outer `collect_token` node without being wrapped in any
kind of additional layer.

This commit changes `collect_tokens` to return early in some extra cases,
avoiding the construction of lazy tokens. In the example above, the
outer `collect_tokens` returns earlier because the `0` token already has
tokens and `self.capture_state.capturing` is `Capturing::No`. This early
return avoids the creation of the invalid range and the assertion
failure.

Fixes #129166. Note: these invalid ranges have been happening for a long
time. #128725 looks like it's at fault only because it introduced the
assertion that catches the invalid ranges.
Diffstat (limited to 'tests/ui/conditional-compilation')
-rw-r--r--tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs11
-rw-r--r--tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr8
2 files changed, 19 insertions, 0 deletions
diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs
new file mode 100644
index 00000000000..794e6fad3fc
--- /dev/null
+++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs
@@ -0,0 +1,11 @@
+// This was triggering an assertion failure in `NodeRange::new`.
+
+#![feature(cfg_eval)]
+#![feature(stmt_expr_attributes)]
+
+fn f() -> u32 {
+    #[cfg_eval] #[cfg(not(FALSE))] 0
+    //~^ ERROR removing an expression is not supported in this position
+}
+
+fn main() {}
diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr
new file mode 100644
index 00000000000..0699e182bd5
--- /dev/null
+++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.stderr
@@ -0,0 +1,8 @@
+error: removing an expression is not supported in this position
+  --> $DIR/invalid-node-range-issue-129166.rs:7:17
+   |
+LL |     #[cfg_eval] #[cfg(not(FALSE))] 0
+   |                 ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+