Skip to content

Commit c0daad3

Browse files
cristianocclaude
andcommitted
Warn about an @as that does not name a record field
Every @as on a record field was marked used, which made sense when the attribute was what the compiler read. Now the one that names the field is taken out of the attributes when the field is built, so an @as still there is either a second one, which the type checker rejects, or a payload that is not a name at all. @as(42) on a record field was accepted in silence: it renamed nothing and nothing reported it. Only the attribute the compiler acts on is marked now, so that case warns and a duplicate still reports just its own error rather than both. Signed-off-by: Cristiano Calcagno <ccrisccris@gmail.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8g8qwBARAcvW9MyuKQq8H
1 parent 6c0f6bf commit c0daad3

4 files changed

Lines changed: 28 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- Fix the side-effect analysis treating bigint exponentiation and bounds-checked array and string reads as pure, which let dead-code elimination drop an unused one that throws: `let _ = 2n ** -1n` no longer raised. https://github.com/rescript-lang/rescript/pull/8617
4040
- Fix excessive parentheses and indentation in function assignments to refs, align record and array assignment formatting across refs and fields, and preserve function return-type parentheses and consistent JSX fragment layout in callbacks. https://github.com/rescript-lang/rescript/pull/8611
4141
- Report an error instead of crashing when an integer in a variant constructor's `@as` annotation exceeds the compiler's integer range. https://github.com/rescript-lang/rescript/pull/8619
42+
- Warn about an `@as` on a record field whose payload does not name the field, such as `@as(42)`. It renamed nothing and was silently accepted. https://github.com/rescript-lang/rescript/pull/8619
4243
- Fix a recursive module with an empty signature discarding its right-hand side. Lambda-to-Lam conversion rewrote `Pupdate_mod` to unit when the module's shape had no fields, dropping the primitive's arguments - one of which is the right-hand side - so `module rec M: {} = { let () = Console.log("effect") }` emitted nothing for `M`. The elision now happens where the bindings are produced, with the right-hand side still in hand. https://github.com/rescript-lang/rescript/pull/8608
4344
- Fix a compiler crash on a polymorphic variant whose numeric name exceeds the `int32` range. `#99999999999("a")` and the same name in a pattern failed with `Failure("Int32.of_string")` and no location, because the range check ran in the frontend AST pass and matched only payload-free expressions. It now runs in `Typecore`, next to the integer literal decoding whose overflow error it mirrors, and covers both label positions. A bare `type t = [#99999999999]` still compiles, since nothing decodes a row field name. https://github.com/rescript-lang/rescript/pull/8608
4445
- Object typing errors now describe fields directly: assigning to a field without `@set` reports that the field is not settable and suggests the annotation, and missing-property errors name the field instead of a phantom `"x#="` member. https://github.com/rescript-lang/rescript/pull/8597

compiler/frontend/bs_ast_invariant.ml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,17 @@ let emit_external_warnings : iterator =
9494
| _ -> super.expr self a);
9595
label_declaration =
9696
(fun self lbl ->
97-
Ext_list.iter lbl.pld_attributes (fun attr ->
98-
match attr with
99-
| {txt = "as"}, _ -> Used_attributes.mark_used_attribute attr
100-
| _ -> ());
97+
(* A field's [@as] is taken out of its attributes when it names the
98+
field, so one still here is either a second one, which the type
99+
checker rejects, or a payload that is not a name. The first is
100+
reported already; the second is reported by nothing else, so let it
101+
warn as the unused attribute it is. *)
102+
Ext_list.iter lbl.pld_attributes
103+
(fun (({txt}, payload) as attr : Parsetree.attribute) ->
104+
if
105+
txt = "as"
106+
&& Ast_payload.string_literal_of_payload payload <> None
107+
then Used_attributes.mark_used_attribute attr);
101108
super.label_declaration self lbl);
102109
constructor_declaration =
103110
(fun self ({pcd_name = {txt; loc}} as ctr) ->
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
Warning number 101 (configured as error)
3+
/.../fixtures/record_field_as_not_a_name.res:3:11-13
4+
5+
1 │ /* An @as that does not name the field renames nothing. Nothing else rep
6+
│ orts
7+
2 │ it, so it warns as the unused attribute it is. */
8+
3 │ type t = {@as(42) a: int}
9+
4 │
10+
11+
Unused attribute: @as
12+
This attribute has no effect here.
13+
For example, some attributes are only meaningful in externals.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* An @as that does not name the field renames nothing. Nothing else reports
2+
it, so it warns as the unused attribute it is. */
3+
type t = {@as(42) a: int}

0 commit comments

Comments
 (0)