Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#### :bug: Bug fix

- Preserve `@as(...)` decorator on record fields when creating interface. https://github.com/rescript-lang/rescript/pull/7779

#### :memo: Documentation

#### :nail_care: Polish
Expand Down
23 changes: 19 additions & 4 deletions analysis/src/CreateInterface.ml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module AttributesUtils : sig

val contains : string -> t -> bool

val isEmpty : t -> bool

val toString : t -> string
end = struct
type attribute = {line: int; offset: int; name: string}
Expand Down Expand Up @@ -76,6 +78,8 @@ end = struct
let contains attributeForSearch t =
t |> List.exists (fun {name} -> name = attributeForSearch)

let isEmpty t = t = []

let toString t =
match t with
| [] -> ""
Expand Down Expand Up @@ -250,11 +254,22 @@ let printSignature ~extractor ~signature =
Buffer.add_string buf (indent ^ newItemStr ^ "\n");
processSignature ~indent items
| Sig_type (id, typeDecl, resStatus) :: items ->
let newItemStr =
sigItemToString
(Printtyp.tree_of_type_declaration id typeDecl resStatus)
let lines =
let posStart, posEnd = Loc.range typeDecl.type_loc in
extractor |> SourceFileExtractor.extract ~posStart ~posEnd
in
Buffer.add_string buf (indent ^ newItemStr ^ "\n");
let attributes = AttributesUtils.make lines in

(if not (AttributesUtils.isEmpty attributes) then
(* Copy the type declaration verbatim to preserve attributes *)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work in the else branch too? Can we always copy verbatim? Or what's the tradeoff here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed the else branch since every type declaration should be valid in an interface file when copied verbatim 👍 The only tradeoff I can think of is that we'll be copying any non-doc comments within the declaration

Buffer.add_string buf ((lines |> String.concat "\n") ^ "\n")
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String concatenation with ^ creates intermediate strings. Consider using Buffer.add_string for each line separately or String.concat "\n" without the additional concatenation.

Suggested change
Buffer.add_string buf ((lines |> String.concat "\n") ^ "\n")
Array.iter (fun line -> Buffer.add_string buf line; Buffer.add_char buf '\n') lines

Copilot uses AI. Check for mistakes.

else
let newItemStr =
sigItemToString
(Printtyp.tree_of_type_declaration id typeDecl resStatus)
in
Buffer.add_string buf (indent ^ newItemStr ^ "\n"));
Copy link

Copilot AI Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The conditional logic should be inverted to reduce nesting. Consider using early return pattern: if AttributesUtils.isEmpty attributes then (* normal processing *) else (* verbatim copy *).

Copilot uses AI. Check for mistakes.


processSignature ~indent items
| Sig_typext (id, extConstr, extStatus) :: items ->
let newItemStr =
Expand Down
7 changes: 7 additions & 0 deletions tests/analysis_tests/tests/src/CreateInterface.res
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,10 @@ module Memo = {

let make = React.memo(make)
}

external x: (@as("bar") ~foo: int) => unit = "myexternal"

type record = {
@as("foo_bar")
fooBar: int
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,9 @@ module Memo: {
@react.component
let make: (~name: string) => React.element
}
external x: (@as("bar") ~foo: int) => unit = "myexternal"
type record = {
@as("foo_bar")
fooBar: int
}

Loading