Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -14,6 +14,7 @@

#### :boom: Breaking Change

- `\"_"` is no longer allowed as a quoted identifier, as it couldn't be told apart from the `_` wildcard and placeholder. https://github.com/rescript-lang/rescript/pull/8795
- Record syntax no longer creates or matches dicts, e.g. `let d: dict<int> = {foo: 1}`; it's an error pointing to `dict{...}`. https://github.com/rescript-lang/rescript/pull/8751
- Remove the call-site `@inlined` attribute, which was parsed but never affected code generation. It is now reported as a misplaced attribute (warning 53). https://github.com/rescript-lang/rescript/pull/8734
- Remove `@deriving(abstract)` and `@deriving(jsConverter)`. Use record types (with optional fields, mutable fields and `@as` renaming) and polymorphic variants directly instead. https://github.com/rescript-lang/rescript/pull/8729
Expand All @@ -30,6 +31,7 @@

- Fix parser recovery and editor completion when a record or dict value is missing before a comma, e.g. `{a: , b: 1}`. https://github.com/rescript-lang/rescript/pull/8779
- Derive module names from file names consistently, fixing files like `Button.test.res` that could not reference `Button`, and `.res`/`.resi` pairs with dotted or non-ASCII names. https://github.com/rescript-lang/rescript/pull/8797
- Fix the formatter printing escaped identifiers starting with `_`, like `\"_'"`, in a form that doesn't parse. https://github.com/rescript-lang/rescript/pull/8795
- Fix references to a `@val` global reading a binding with the same name, e.g. a toplevel `let` declared before them. https://github.com/rescript-lang/rescript/pull/8785
- Evaluate the unused arguments of an inlined function call in parameter order. https://github.com/rescript-lang/rescript/pull/8786
- Fix shadowed variables in generated JS colliding with identifiers or external names containing `$`. https://github.com/rescript-lang/rescript/pull/8778
Expand Down
8 changes: 7 additions & 1 deletion analysis/src/utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,13 @@ let print_maybe_exotic_ident ?(allow_uident = false) txt =
else if i == 0 then
match String.unsafe_get txt i with
| 'A' .. 'Z' when allow_uident -> loop (i + 1)
| 'a' .. 'z' | '_' | '$' -> loop (i + 1)
| 'a' .. 'z' | '$' -> loop (i + 1)
(* Same as Res_printer.classify_ident_content: a lone [_] or one
followed by another character is not scanned as an identifier. *)
| '_' when len > 1 -> (
match String.unsafe_get txt 1 with
| 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '$' -> loop (i + 1)
| _ -> "\"" ^ txt ^ "\"")
| _ -> "\"" ^ txt ^ "\""
else
match String.unsafe_get txt i with
Expand Down
12 changes: 11 additions & 1 deletion compiler/syntax/src/res_printer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,13 @@ let classify_ident_content ?(allow_uident = false) ?(allow_hyphen = false) txt =
match String.unsafe_get txt i with
| '\\' -> UppercaseExoticIdent
| 'A' .. 'Z' when allow_uident -> loop (i + 1)
| 'a' .. 'z' | '_' | '$' -> loop (i + 1)
| 'a' .. 'z' | '$' -> loop (i + 1)
(* The scanner reads [_] as an identifier only when one of these
follows; otherwise it is a wildcard. *)
| '_' when len > 1 -> (
match String.unsafe_get txt 1 with
| 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '_' | '$' -> loop (i + 1)
| _ -> ExoticIdent)
| _ -> ExoticIdent
else
match String.unsafe_get txt i with
Expand Down Expand Up @@ -3338,6 +3344,10 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl =
let constr = print_longident_location longident_loc cmt_tbl in
let args = print_expression_args ~state args cmt_tbl in
Doc.group (Doc.concat [constr; args])
| Pexp_ident ({txt = Longident.Lident "_"} as path) ->
(* The `_` placeholder of underscore apply sugar, see
[Parsetree_viewer.rewrite_underscore_apply]. *)
print_comments (Doc.text "_") cmt_tbl path.loc
| Pexp_ident path -> print_lident_path path cmt_tbl
| Pexp_tuple exprs ->
Doc.group
Expand Down
7 changes: 7 additions & 0 deletions compiler/syntax/src/res_scanner.ml
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,13 @@ let scan_exotic_identifier scanner =
scanner.err ~start_pos ~end_pos
(Diagnostics.message "A quoted identifier can't be empty string.");
Token.Lident ident)
else if name = "_" then (
(* `_` is the wildcard and the placeholder of underscore apply sugar;
a quoted `_` couldn't be told apart from them. *)
let end_pos = position scanner in
scanner.err ~start_pos ~end_pos
(Diagnostics.message "`_` can't be used as a quoted identifier.");
Token.Lident name)
else if Ext_ident.is_uident name then Token.Lident ident
(* Exotic ident with uppercase letter should be encoded to avoid confusing in OCaml parsetree *)
else Token.Lident name
Expand Down
2 changes: 1 addition & 1 deletion tests/analysis_tests/tests/src/CompletionExpressions.res
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ let arr = ["hello"]
// arr->Belt.Array.map()
// ^com

type exoticPolyvariant = [#"some exotic" | #dollar$]
type exoticPolyvariant = [#"some exotic" | #dollar$ | #"_"]

let takesExotic = (e: exoticPolyvariant) => {
ignore(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,19 +1330,31 @@ Path takesExotic
"detail": "#\"some exotic\"",
"documentation": {
"kind": "markdown",
"value": "```rescript\n#\"some exotic\"\n```\n\n```rescript\n[#dollar$ | #\"some exotic\"]\n```"
"value": "```rescript\n#\"some exotic\"\n```\n\n```rescript\n[#\"_\" | #dollar$ | #\"some exotic\"]\n```"
},
"insertText": "#\"some exotic\"",
"insertTextFormat": 2,
"kind": 4,
"label": "#\"some exotic\"",
"tags": []
},
{
"detail": "#\"_\"",
"documentation": {
"kind": "markdown",
"value": "```rescript\n#\"_\"\n```\n\n```rescript\n[#\"_\" | #dollar$ | #\"some exotic\"]\n```"
},
"insertText": "#\"_\"",
"insertTextFormat": 2,
"kind": 4,
"label": "#\"_\"",
"tags": []
},
{
"detail": "#dollar$",
"documentation": {
"kind": "markdown",
"value": "```rescript\n#dollar$\n```\n\n```rescript\n[#dollar$ | #\"some exotic\"]\n```"
"value": "```rescript\n#dollar$\n```\n\n```rescript\n[#\"_\" | #dollar$ | #\"some exotic\"]\n```"
},
"insertText": "#dollar$",
"insertTextFormat": 2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Syntax error!
syntax_tests/data/parsing/errors/scanner/quotedUnderscore.res:1:5-8

1 │ let \"_" = 1
2 │ let x = f(\"_")
3 │

`_` can't be used as a quoted identifier.


Syntax error!
syntax_tests/data/parsing/errors/scanner/quotedUnderscore.res:2:11-14

1 │ let \"_" = 1
2 │ let x = f(\"_")
3 │

`_` can't be used as a quoted identifier.

let _ = 1
let x [arity:1]__x = f __x
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let \"_" = 1
let x = f(\"_")
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
let \"_'" = 2
let \"_''" = 3
let __' = 4
let _a' = 5
let f = (~\"_'") => \"_'"
let a = #"_'"
let b = #"_"
type r = {\"_'": int}
let r = {\"_'": 1}
let g = r.\"_'"
let x = f(a, _, c)
11 changes: 11 additions & 0 deletions tests/syntax_tests/data/printer/expr/underscoreExoticIdent.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
let \"_'" = 2
let \"_''" = 3
let __' = 4
let _a' = 5
let f = (~\"_'") => \"_'"
let a = #"_'"
let b = #"_"
type r = {\"_'": int}
let r = {\"_'": 1}
let g = r.\"_'"
let x = f(a, _, c)
Loading