diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0aa152dca..965f3fd0955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,22 @@ > - :nail_care: [Polish] > - :house: [Internal] +# 12.3.1 (Unreleased) + +#### :bug: Bug fix + +- Fix rewatch warning replay after early compile errors. https://github.com/rescript-lang/rescript/pull/8408 +- Fix formatting of trailing comments before `=` in let bindings. https://github.com/rescript-lang/rescript/pull/8444 +- Fix namespaced reference lookup in editor analysis. https://github.com/rescript-lang/rescript/pull/8455 +- Fix build crash when the compiler emits output that is not valid UTF-8, such as a truncated multibyte character in a code frame. https://github.com/rescript-lang/rescript/pull/8482 +- Preserve multibyte characters when wrapping long source lines in compiler code frames. https://github.com/rescript-lang/rescript/pull/8520 +- Preserve parentheses around multiplication, division, and modulo expressions used as exponents. https://github.com/rescript-lang/rescript/pull/8550 +- Enforce function arity in interface/module inclusion, type equality, and coercion. https://github.com/rescript-lang/rescript/pull/8559 +- Fix bare labeled arrow types (`~x: int => string`) getting no arity and failing to unify with their parenthesized form. https://github.com/rescript-lang/rescript/pull/8563 +- Fix argument evaluation order when a function call is inlined: the beta reducer could evaluate non-substitutable arguments from last to first. https://github.com/rescript-lang/rescript/pull/8572 +- Compute optional-parameter defaults when their own curried function group is applied instead of deferring outer defaults until an inner function is called. https://github.com/rescript-lang/rescript/pull/8568 +- Fix termination-analysis false positives when progress flows through helper functions. https://github.com/rescript-lang/rescript/pull/8568 + # 12.3.0 No changes compared to 12.3.0-beta.1. diff --git a/analysis/reanalyze/src/Arnold.ml b/analysis/reanalyze/src/Arnold.ml index cc917725a92..3295f2928ab 100644 --- a/analysis/reanalyze/src/Arnold.ml +++ b/analysis/reanalyze/src/Arnold.ml @@ -545,7 +545,7 @@ module FindFunctionsCalled = struct let findCallees (expression : Typedtree.expression) = let isFunction = match expression.exp_desc with - | Texp_function {arity = None} -> true + | Texp_function _ -> true | _ -> false in let callees = ref StringSet.empty in diff --git a/analysis/src/Cmt.ml b/analysis/src/Cmt.ml index ac1d5ae595f..72753b3040a 100644 --- a/analysis/src/Cmt.ml +++ b/analysis/src/Cmt.ml @@ -8,6 +8,32 @@ let fullForCmt ~moduleName ~package ~uri cmt = let extra = ProcessExtra.getExtra ~file ~infos in Some {file; extra; package} +let fullForIncrementalCmt ~package ~moduleName ~uri = + if !Cfg.inIncrementalTypecheckingMode then + let path = Uri.toPath uri in + let incrementalCmtPath = + package.rootPath ^ "/lib/bs/___incremental" ^ "/" ^ moduleName + ^ + match Files.classifySourceFile path with + | Resi -> ".cmti" + | _ -> ".cmt" + in + match fullForCmt ~moduleName ~package ~uri incrementalCmtPath with + | Some cmtInfo -> + if Debug.verbose () then + Printf.printf "[cmt] Found incremental cmt: %s\n" + (Filename.basename incrementalCmtPath); + Some cmtInfo + | None -> None + else None + +let fullFromModuleUri ~package ~moduleName ~uri ~paths = + match fullForIncrementalCmt ~package ~moduleName ~uri with + | Some cmtInfo -> Some cmtInfo + | None -> + let cmt = getCmtPath ~uri paths in + fullForCmt ~moduleName ~package ~uri cmt + let fullFromUri ~uri = let path = Uri.toPath uri in match Packages.getPackage ~uri with @@ -16,22 +42,8 @@ let fullFromUri ~uri = let moduleName = BuildSystem.namespacedName package.namespace (FindFiles.getName path) in - let incremental = - if !Cfg.inIncrementalTypecheckingMode then - let incrementalCmtPath = - package.rootPath ^ "/lib/bs/___incremental" ^ "/" ^ moduleName - ^ - match Files.classifySourceFile path with - | Resi -> ".cmti" - | _ -> ".cmt" - in - fullForCmt ~moduleName ~package ~uri incrementalCmtPath - else None - in - match incremental with - | Some cmtInfo -> - if Debug.verbose () then Printf.printf "[cmt] Found incremental cmt\n"; - Some cmtInfo + match fullForIncrementalCmt ~package ~moduleName ~uri with + | Some cmtInfo -> Some cmtInfo | None -> ( match Hashtbl.find_opt package.pathsForModule moduleName with | Some paths -> @@ -41,12 +53,20 @@ let fullFromUri ~uri = prerr_endline ("can't find module " ^ moduleName); None)) +let fullFromModule ~package ~moduleName = + Option.bind (Hashtbl.find_opt package.pathsForModule moduleName) + @@ fun paths -> + let uri = getUri paths in + fullFromModuleUri ~package ~moduleName ~uri ~paths + let fullsFromModule ~package ~moduleName = - if Hashtbl.mem package.pathsForModule moduleName then - let paths = Hashtbl.find package.pathsForModule moduleName in + match Hashtbl.find_opt package.pathsForModule moduleName with + | None -> [] + | Some paths -> let uris = getUris paths in - uris |> List.filter_map (fun uri -> fullFromUri ~uri) - else [] + uris + |> List.filter_map (fun uri -> + fullFromModuleUri ~package ~moduleName ~uri ~paths) let loadFullCmtFromPath ~path = let uri = Uri.fromPath path in diff --git a/analysis/src/References.ml b/analysis/src/References.ml index e047a2ba182..142cbfa2f89 100644 --- a/analysis/src/References.ml +++ b/analysis/src/References.ml @@ -18,6 +18,20 @@ let locItemsForPos ~extra pos = let lineColToCmtLoc ~pos:(line, col) = (line + 1, col) +(** External references in namespaced projects are indexed by the public + * namespace path, e.g. MyNamespace.MyModule1.myFunc1, while definitions live + * in hidden compiled modules like MyModule1-MyNamespace. + * We return the lookup key pair used by the external reference index. + *) +let normalizeExternalReferenceKey ~namespace ~moduleName ~path = + match namespace with + | Some namespace when Utils.endsWith moduleName ("-" ^ namespace) -> + let suffixLen = String.length namespace + 1 in + let sourceModuleLen = String.length moduleName - suffixLen in + let sourceModule = String.sub moduleName 0 sourceModuleLen in + (namespace, sourceModule :: path) + | _ -> (moduleName, path) + let getLocItem ~full ~pos ~debug = let log n msg = if debug then Printf.printf "getLocItem #%d: %s\n" n msg in let pos = lineColToCmtLoc ~pos in @@ -485,6 +499,10 @@ let forLocalStamp ~full:{file; extra; package} stamp (tip : Tip.t) = in maybeLog ("Now checking path " ^ pathToString path); let thisModuleName = file.moduleName in + let normalizedModuleName, normalizedPath = + normalizeExternalReferenceKey ~namespace:package.namespace + ~moduleName:thisModuleName ~path + in let externals = package.projectFiles |> FileSet.elements |> List.filter (fun name -> name <> file.moduleName) @@ -493,14 +511,15 @@ let forLocalStamp ~full:{file; extra; package} stamp (tip : Tip.t) = |> List.map (fun {file; extra} -> match Hashtbl.find_opt extra.externalReferences - thisModuleName + normalizedModuleName with | None -> [] | Some refs -> let locs = refs |> Utils.filterMap (fun (p, t, locs) -> - if p = path && t = tip then Some locs + if p = normalizedPath && t = tip then + Some locs else None) in locs @@ -522,10 +541,8 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = | TopLevelModule moduleName -> let otherModulesReferences = package.projectFiles |> FileSet.elements - |> Utils.filterMap (fun name -> - match ProcessCmt.fileForModule ~package name with - | None -> None - | Some file -> Cmt.fullFromUri ~uri:file.uri) + |> Utils.filterMap (fun moduleName -> + Cmt.fullFromModule ~package ~moduleName) |> List.map (fun full -> match Hashtbl.find_opt full.extra.fileReferences moduleName with | None -> [] @@ -563,7 +580,7 @@ let allReferencesForLocItem ~full:({file; package} as full) locItem = match exportedForTip ~env ~path ~package ~tip with | None -> [] | Some (env, _name, stamp) -> ( - match Cmt.fullFromUri ~uri:env.file.uri with + match Cmt.fullFromModule ~package ~moduleName:env.file.moduleName with | None -> [] | Some full -> maybeLog diff --git a/analysis/src/SignatureHelp.ml b/analysis/src/SignatureHelp.ml index e4c9cb11ae1..0ba81d7d1eb 100644 --- a/analysis/src/SignatureHelp.ml +++ b/analysis/src/SignatureHelp.ml @@ -101,24 +101,28 @@ let findFunctionType ~currentFile ~debug ~path ~pos = Some (args, docstring, type_expr, package, env, file) | _ -> None)) -(* Extracts all parameters from a parsed function signature *) +(* Extracts the parameters from the outermost parsed function signature. A + returned function's parameters belong to a different call site. *) let extractParameters ~signature ~typeStrForParser ~labelPrefixLen = match signature with - | [{Parsetree.psig_desc = Psig_value {pval_type = expr}}] - when match expr.ptyp_desc with - | Ptyp_arrow _ -> true - | _ -> false -> - let rec extractParams expr params = + | [ + { + Parsetree.psig_desc = + Psig_value + {pval_type = {ptyp_desc = Ptyp_arrow {arity = outerArity}} as expr}; + }; + ] -> + let rec extractParams expr params remaining = match expr with - | { - (* Gotcha: functions with multiple arugments are modelled as a series of single argument functions. *) - Parsetree.ptyp_desc = Ptyp_arrow {arg; ret = nextFunctionExpr}; - ptyp_loc; - } -> + | {Parsetree.ptyp_desc = Ptyp_arrow {arg; ret = nextFunctionExpr}} + when remaining > 0 -> + let startLoc = + match arg.lbl with + | Asttypes.Labelled {loc} | Optional {loc} -> loc |> Loc.start + | Nolabel -> arg.typ.ptyp_loc |> Loc.start + in let startOffset = - ptyp_loc |> Loc.start - |> Pos.positionToOffset typeStrForParser - |> Option.get + startLoc |> Pos.positionToOffset typeStrForParser |> Option.get in let endOffset = arg.typ.ptyp_loc |> Loc.end_ @@ -140,9 +144,10 @@ let extractParameters ~signature ~typeStrForParser ~labelPrefixLen = startOffset - labelPrefixLen, endOffset - labelPrefixLen ); ]) + (remaining - 1) | _ -> params in - extractParams expr [] + extractParams expr [] (Option.value outerArity ~default:max_int) | _ -> [] (* Finds what parameter is active, if any *) diff --git a/compiler/common/bs_version.ml b/compiler/common/bs_version.ml index 9a7ea1c30fd..ef3033dbef5 100644 --- a/compiler/common/bs_version.ml +++ b/compiler/common/bs_version.ml @@ -21,5 +21,5 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *) -let version = "12.3.0" +let version = "12.3.1" let header = "// Generated by ReScript, PLEASE EDIT WITH CARE" diff --git a/compiler/core/js_op_util.ml b/compiler/core/js_op_util.ml index 8d2d8636396..1ac99090215 100644 --- a/compiler/core/js_op_util.ml +++ b/compiler/core/js_op_util.ml @@ -41,7 +41,7 @@ let op_prec (op : Js_op.binop) = | Lsl | Lsr | Asr -> (10, 10, 11) | Bnot | Plus | Minus -> (11, 11, 12) | Mul | Div | Mod -> (12, 12, 13) - | Pow -> (13, 14, 12) + | Pow -> (13, 14, 13) let op_int_prec (op : Js_op.int_op) = match op with diff --git a/compiler/core/lam_beta_reduce.ml b/compiler/core/lam_beta_reduce.ml index e7f9842bbc7..10ddae76b68 100644 --- a/compiler/core/lam_beta_reduce.ml +++ b/compiler/core/lam_beta_reduce.ml @@ -63,7 +63,10 @@ let propagate_beta_reduce (meta : Lam_stats.t) (params : Ident.t list) (Hash_ident.of_list2 (List.rev params) rev_new_params) body in - Ext_list.fold_right rest_bindings new_body (fun (param, arg) l -> + (* [rest_bindings] is in reverse parameter order; folding left makes the + first parameter's binding outermost, so arguments evaluate in call + order. *) + Ext_list.fold_left rest_bindings new_body (fun l (param, arg) -> (match arg with | Lprim {primitive = Pmakeblock (_, _, Immutable); args; _} -> Hash_ident.replace meta.ident_tbl param @@ -104,7 +107,8 @@ let propagate_beta_reduce_with_map (meta : Lam_stats.t) (Hash_ident.of_list2 (List.rev params) rev_new_params) body in - Ext_list.fold_right rest_bindings new_body (fun (param, (arg : Lam.t)) l -> + (* See above: fold left so arguments evaluate in call order. *) + Ext_list.fold_left rest_bindings new_body (fun l (param, (arg : Lam.t)) -> (match arg with | Lprim {primitive = Pmakeblock (_, _, Immutable); args} -> Hash_ident.replace meta.ident_tbl param diff --git a/compiler/ml/code_frame.ml b/compiler/ml/code_frame.ml index 9f75c765ac4..b14b7007af9 100644 --- a/compiler/ml/code_frame.ml +++ b/compiler/ml/code_frame.ml @@ -37,14 +37,23 @@ let leading_space_count str = loop 0 0 let break_long_line max_width line = + let line_length = String.length line in + let rec find_chunk_end pos remaining_width = + if pos = line_length || remaining_width = 0 then pos + else + let char_length = + String.get_utf_8_uchar line pos |> Uchar.utf_decode_length + in + find_chunk_end (pos + char_length) (remaining_width - 1) + in let rec loop pos accum = - if pos = String.length line then accum + if pos = line_length then List.rev accum else - let chunk_length = min max_width (String.length line - pos) in - let chunk = String.sub line pos chunk_length in - loop (pos + chunk_length) (chunk :: accum) + let chunk_end = find_chunk_end pos max_width in + let chunk = String.sub line pos (chunk_end - pos) in + loop chunk_end (chunk :: accum) in - loop 0 [] |> List.rev + loop 0 [] let filter_mapi f l = let rec loop f l i accum = diff --git a/compiler/ml/ctype.ml b/compiler/ml/ctype.ml index e15adf31b43..23c95d03394 100644 --- a/compiler/ml/ctype.ml +++ b/compiler/ml/ctype.ml @@ -2914,8 +2914,8 @@ let rec moregen inst_nongen type_pairs env t1 t2 = | Tvar _, _ when may_instantiate inst_nongen t1' -> moregen_occur env t1'.level t2; link_type t1' t2 - | Tarrow (arg1, ret1, _, _), Tarrow (arg2, ret2, _, _) - when Asttypes.same_arg_label arg1.lbl arg2.lbl -> + | Tarrow (arg1, ret1, _, a1), Tarrow (arg2, ret2, _, a2) + when a1 = a2 && Asttypes.same_arg_label arg1.lbl arg2.lbl -> moregen inst_nongen type_pairs env arg1.typ arg2.typ; moregen inst_nongen type_pairs env ret1 ret2 | Ttuple tl1, Ttuple tl2 -> @@ -3184,8 +3184,8 @@ let rec eqtype rename type_pairs subst env t1 t2 = if List.exists (fun (_, t) -> t == t2') !subst then raise (Unify []); subst := (t1', t2') :: !subst) - | Tarrow (arg1, ret1, _, _), Tarrow (arg2, ret2, _, _) - when Asttypes.same_arg_label arg1.lbl arg2.lbl -> + | Tarrow (arg1, ret1, _, a1), Tarrow (arg2, ret2, _, a2) + when a1 = a2 && Asttypes.same_arg_label arg1.lbl arg2.lbl -> eqtype rename type_pairs subst env arg1.typ arg2.typ; eqtype rename type_pairs subst env ret1 ret2 | Ttuple tl1, Ttuple tl2 -> @@ -3597,8 +3597,8 @@ let rec subtype_rec env trace t1 t2 cstrs = TypePairs.add subtypes (t1, t2) (); match (t1.desc, t2.desc) with | Tvar _, _ | _, Tvar _ -> (trace, t1, t2, !univar_pairs, None) :: cstrs - | Tarrow (arg1, ret1, _, _), Tarrow (arg2, ret2, _, _) - when Asttypes.same_arg_label arg1.lbl arg2.lbl -> + | Tarrow (arg1, ret1, _, a1), Tarrow (arg2, ret2, _, a2) + when a1 = a2 && Asttypes.same_arg_label arg1.lbl arg2.lbl -> let cstrs = subtype_rec env ((arg2.typ, arg1.typ) :: trace) diff --git a/compiler/ml/translcore.ml b/compiler/ml/translcore.ml index 078cbf133a0..199c90ea718 100644 --- a/compiler/ml/translcore.ml +++ b/compiler/ml/translcore.ml @@ -547,8 +547,10 @@ let rec push_defaults loc bindings case partial = c_lhs = pat; c_guard = None; c_rhs = - {exp_desc = Texp_function {arg_label; arity; param; case; partial; async}} - as exp; + { + exp_desc = + Texp_function {arg_label; arity = None; param; case; partial; async}; + } as exp; } -> let case = push_defaults exp.exp_loc bindings case partial in @@ -559,7 +561,7 @@ let rec push_defaults loc bindings case partial = { exp with exp_desc = - Texp_function {arg_label; arity; param; case; partial; async}; + Texp_function {arg_label; arity = None; param; case; partial; async}; }; } | { diff --git a/compiler/syntax/src/res_core.ml b/compiler/syntax/src/res_core.ml index 0d7ef55e960..419a2180ea7 100644 --- a/compiler/syntax/src/res_core.ml +++ b/compiler/syntax/src/res_core.ml @@ -4726,7 +4726,7 @@ and parse_es6_arrow_type ~attrs p = Parser.expect EqualGreater p; let return_type = parse_typ_expr ~alias:false p in let loc = mk_loc start_pos p.prev_end_pos in - Ast_helper.Typ.arrow ~loc ~arity:None {attrs; lbl; typ} return_type + Ast_helper.Typ.arrow ~loc ~arity:(Some 1) {attrs; lbl; typ} return_type | DocComment _ -> assert false | _ -> let parameters = parse_type_parameters p in diff --git a/compiler/syntax/src/res_printer.ml b/compiler/syntax/src/res_printer.ml index 2010d23f6dd..40c2b33fcf7 100644 --- a/compiler/syntax/src/res_printer.ml +++ b/compiler/syntax/src/res_printer.ml @@ -70,6 +70,11 @@ let has_trailing_single_line_comment tbl loc = | Some (comment :: _) -> Comment.is_single_line_comment comment | _ -> false +let has_any_trailing_line_comment tbl loc = + match Hashtbl.find_opt tbl.CommentTable.trailing loc with + | Some comments -> List.exists Comment.is_single_line_comment comments + | None -> false + let has_comment_below tbl loc = match Hashtbl.find tbl.CommentTable.trailing loc with | comment :: _ -> @@ -2230,7 +2235,15 @@ and print_value_binding ~state ~rec_flag (vb : Parsetree.value_binding) cmt_tbl | Braced braces -> print_braces doc expr braces | Nothing -> doc in + let pattern_has_trailing_line_comment = + has_any_trailing_line_comment cmt_tbl vb.pvb_pat.ppat_loc + in let pattern_doc = print_pattern ~state vb.pvb_pat cmt_tbl in + let equal_doc = + if pattern_has_trailing_line_comment then + Doc.indent (Doc.concat [Doc.hard_line; Doc.equal]) + else Doc.text " =" + in (* * we want to optimize the layout of one pipe: * let tbl = data->Js.Array2.reduce((map, curr) => { @@ -2248,21 +2261,14 @@ and print_value_binding ~state ~rec_flag (vb : Parsetree.value_binding) cmt_tbl [ Doc.group (Doc.concat - [ - attrs; - header; - pattern_doc; - Doc.text " ="; - Doc.space; - printed_expr; - ]); + [attrs; header; pattern_doc; equal_doc; Doc.space; printed_expr]); Doc.group (Doc.concat [ attrs; header; pattern_doc; - Doc.text " ="; + equal_doc; Doc.indent (Doc.concat [Doc.line; printed_expr]); ]); ] @@ -2294,7 +2300,7 @@ and print_value_binding ~state ~rec_flag (vb : Parsetree.value_binding) cmt_tbl attrs; header; pattern_doc; - Doc.text " ="; + equal_doc; (if should_indent then Doc.indent (Doc.concat [Doc.line; printed_expr]) else Doc.concat [Doc.space; printed_expr]); diff --git a/package.json b/package.json index 84d4ac69d9f..1148b49edb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rescript", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript toolchain", "type": "module", "keywords": [ diff --git a/packages/@rescript/darwin-arm64/package.json b/packages/@rescript/darwin-arm64/package.json index f76e3b499c0..b649eb29348 100644 --- a/packages/@rescript/darwin-arm64/package.json +++ b/packages/@rescript/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/darwin-arm64", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript binaries for MacOS ARM64", "type": "module", "license": "(LGPL-3.0-or-later AND MIT)", diff --git a/packages/@rescript/darwin-x64/package.json b/packages/@rescript/darwin-x64/package.json index 34848583cb3..7a1187151fd 100644 --- a/packages/@rescript/darwin-x64/package.json +++ b/packages/@rescript/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/darwin-x64", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript binaries for MacOS x86_64", "type": "module", "license": "(LGPL-3.0-or-later AND MIT)", diff --git a/packages/@rescript/linux-arm64/package.json b/packages/@rescript/linux-arm64/package.json index 3486e6144f1..e2bae4c28f0 100644 --- a/packages/@rescript/linux-arm64/package.json +++ b/packages/@rescript/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/linux-arm64", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript binaries for Linux ARM64", "type": "module", "license": "(LGPL-3.0-or-later AND MIT)", diff --git a/packages/@rescript/linux-x64/package.json b/packages/@rescript/linux-x64/package.json index efcbcdb344a..8b336059459 100644 --- a/packages/@rescript/linux-x64/package.json +++ b/packages/@rescript/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/linux-x64", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript binaries for Linux x86_64", "type": "module", "license": "(LGPL-3.0-or-later AND MIT)", diff --git a/packages/@rescript/runtime/package.json b/packages/@rescript/runtime/package.json index 6020ea0d00d..d2a74bfcf22 100644 --- a/packages/@rescript/runtime/package.json +++ b/packages/@rescript/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/runtime", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript runtime modules", "type": "module", "license": "MIT", diff --git a/packages/@rescript/win32-x64/package.json b/packages/@rescript/win32-x64/package.json index 8a9857c672e..f7fa1c09414 100644 --- a/packages/@rescript/win32-x64/package.json +++ b/packages/@rescript/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@rescript/win32-x64", - "version": "12.3.0", + "version": "12.3.1", "description": "ReScript binaries for Windows x86_64", "type": "module", "license": "(LGPL-3.0-or-later AND MIT)", diff --git a/rewatch/Cargo.lock b/rewatch/Cargo.lock index 1397372d1c5..a81293a385d 100644 --- a/rewatch/Cargo.lock +++ b/rewatch/Cargo.lock @@ -768,7 +768,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rescript" -version = "12.3.0" +version = "12.3.1" dependencies = [ "ahash", "anyhow", diff --git a/rewatch/Cargo.toml b/rewatch/Cargo.toml index 35576d57196..c05014cb728 100644 --- a/rewatch/Cargo.toml +++ b/rewatch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rescript" -version = "12.3.0" +version = "12.3.1" edition = "2024" rust-version = "1.91" diff --git a/rewatch/src/build/compile.rs b/rewatch/src/build/compile.rs index 4c735df6218..6052d0c0dbf 100644 --- a/rewatch/src/build/compile.rs +++ b/rewatch/src/build/compile.rs @@ -21,6 +21,12 @@ use std::process::Command; use std::sync::OnceLock; use std::time::SystemTime; +/// Decode captured compiler output without crashing if a code frame truncates a +/// multi-byte character. +fn compiler_output_to_string(bytes: &[u8]) -> String { + String::from_utf8_lossy(bytes).to_string() +} + /// Execute js-post-build command for a compiled JavaScript file. /// The command runs in the directory containing the rescript.json that defines it. /// The absolute path to the JS file is passed as an argument. @@ -113,6 +119,7 @@ pub fn compile( let mut compile_errors = "".to_string(); let mut compile_warnings = "".to_string(); let mut num_compiled_modules = 0; + let mut recompiled_modules = AHashSet::::new(); let mut sorted_modules = build_state.module_names.iter().collect::>(); sorted_modules.sort(); @@ -271,6 +278,7 @@ pub fn compile( if *is_compiled { num_compiled_modules += 1; + recompiled_modules.insert(module_name.to_string()); } files_current_loop_count += 1; @@ -443,11 +451,11 @@ pub fn compile( }; } - // Collect warnings from modules that were not recompiled in this build - // but still have stored warnings from a previous compilation. - // This ensures warnings are not lost during incremental builds in watch mode. + // Collect warnings from modules that were not recompiled in this build but still have stored + // warnings from a previous compilation. This includes modules in the compile universe that + // were never reached because an earlier module failed. for (module_name, module) in build_state.modules.iter() { - if compile_universe.contains(module_name) { + if recompiled_modules.contains(module_name) { continue; } if let SourceType::SourceFile(ref source_file) = module.source_type { @@ -787,9 +795,7 @@ fn compile_file( "Could not compile file. Error: {e}. Path to AST: {ast_path:?}" )), Ok(x) => { - let err = std::str::from_utf8(&x.stderr) - .expect("stdout should be non-null") - .to_string(); + let err = compiler_output_to_string(&x.stderr); let dir = Path::new(implementation_file_path).parent().unwrap(); @@ -1048,3 +1054,16 @@ pub fn mark_modules_with_expired_deps_dirty(build_state: &mut BuildCommandState) } }); } + +#[cfg(test)] +mod tests { + use super::compiler_output_to_string; + + #[test] + fn compiler_output_to_string_handles_invalid_utf8() { + // Start of an em dash (U+2014), with its third byte missing. + let truncated = [b'W', b'a', b'r', b'n', b'i', b'n', b'g', b' ', 0xe2, 0x80]; + let decoded = compiler_output_to_string(&truncated); + assert!(decoded.starts_with("Warning ")); + } +} diff --git a/rewatch/tests/watch.sh b/rewatch/tests/watch.sh index bcd0158956e..f5898a4e47e 100755 --- a/rewatch/tests/watch.sh +++ b/rewatch/tests/watch.sh @@ -39,6 +39,40 @@ else exit 1 fi +bold "Test: Stored warnings are replayed after an early compile error" +warning_count=$(grep -c "unusedValue" rewatch.log || true) +echo 'B.world()' >> ./packages/watch-warnings/src/ModuleA.res +timeout=20 +while [ "$(grep -c "unusedValue" rewatch.log || true)" -le "$warning_count" ] && [ "$timeout" -gt 0 ]; do + sleep 1 + timeout=$((timeout - 1)) +done +if [ "$timeout" -eq 0 ]; then + error "Expected warning was not emitted before the error test" + git checkout -- ./packages/watch-warnings/src/ModuleA.res + exit_watcher + exit 1 +fi + +error_log_start=$(($(wc -l < rewatch.log) + 1)) +echo 'let broken: int = "broken"' >> ./packages/watch-warnings/src/B.res +timeout=20 +while ! tail -n +"$error_log_start" rewatch.log | grep -q 'let broken' && [ "$timeout" -gt 0 ]; do + sleep 1 + timeout=$((timeout - 1)) +done +warning_replay_output=$(tail -n +"$error_log_start" rewatch.log) +if [[ "$warning_replay_output" == *"unusedValue"* ]]; then + success "Stored warning was replayed" +else + error "Stored warning was not replayed" + printf "%s\n" "$warning_replay_output" >&2 + git checkout -- ./packages/watch-warnings/src/ModuleA.res ./packages/watch-warnings/src/B.res + exit_watcher + exit 1 +fi +git checkout -- ./packages/watch-warnings/src/ModuleA.res ./packages/watch-warnings/src/B.res + sleep 1 replace '/Js.log("added-by-test")/d' ./packages/main/src/Main.res; @@ -54,4 +88,4 @@ else exit 1 fi -exit_watcher \ No newline at end of file +exit_watcher diff --git a/tests/analysis_tests/Makefile b/tests/analysis_tests/Makefile index 0fd0226e6a0..741c90403ec 100644 --- a/tests/analysis_tests/Makefile +++ b/tests/analysis_tests/Makefile @@ -4,6 +4,7 @@ test-analysis-binary: make -C tests test make -C tests-generic-jsx-transform test make -C tests-incremental-typechecking test + make -C tests-namespaced-references test make -C tests-sourcedirs-dependency test test-reanalyze: @@ -15,6 +16,7 @@ clean: make -C tests clean make -C tests-generic-jsx-transform clean make -C tests-incremental-typechecking clean + make -C tests-namespaced-references clean make -C tests-sourcedirs-dependency clean make -C tests-reanalyze clean diff --git a/tests/analysis_tests/tests-namespaced-references/.gitignore b/tests/analysis_tests/tests-namespaced-references/.gitignore new file mode 100644 index 00000000000..3b4e8dcf930 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/.gitignore @@ -0,0 +1,2 @@ +lib/ +node_modules/ diff --git a/tests/analysis_tests/tests-namespaced-references/Makefile b/tests/analysis_tests/tests-namespaced-references/Makefile new file mode 100644 index 00000000000..2b663dd3475 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/Makefile @@ -0,0 +1,20 @@ +SHELL = /bin/bash + +build: + yarn build + + # Simulate editor-written incremental CMTs + mkdir -p lib/bs/___incremental + cp lib/bs/src/MyModule1-MyNamespace.cmt lib/bs/___incremental/MyModule1-MyNamespace.cmt + cp lib/bs/src/MyModule2-MyNamespace.cmt lib/bs/___incremental/MyModule2-MyNamespace.cmt + cp lib/bs/MyNamespace.cmt lib/bs/___incremental/MyNamespace.cmt + +test: build + ./test.sh + +clean: + yarn clean + +.DEFAULT_GOAL := test + +.PHONY: build clean test diff --git a/tests/analysis_tests/tests-namespaced-references/package.json b/tests/analysis_tests/tests-namespaced-references/package.json new file mode 100644 index 00000000000..596b324a429 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/package.json @@ -0,0 +1,11 @@ +{ + "name": "@tests/namespaced-references", + "private": true, + "scripts": { + "build": "rescript build", + "clean": "rescript clean" + }, + "dependencies": { + "rescript": "workspace:^" + } +} diff --git a/tests/analysis_tests/tests-namespaced-references/rescript.json b/tests/analysis_tests/tests-namespaced-references/rescript.json new file mode 100644 index 00000000000..283f6934730 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/rescript.json @@ -0,0 +1,17 @@ +{ + "name": "@tests/namespaced-references", + "namespace": "my-namespace", + "sources": [ + { + "dir": "src", + "subdirs": true + } + ], + "package-specs": [ + { + "module": "commonjs", + "in-source": false + } + ], + "suffix": ".res.js" +} diff --git a/tests/analysis_tests/tests-namespaced-references/src/MyModule1.res b/tests/analysis_tests/tests-namespaced-references/src/MyModule1.res new file mode 100644 index 00000000000..d51b53b56f4 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/src/MyModule1.res @@ -0,0 +1,6 @@ +// ^in+ +// ^dv+ +let myFunc1 = () => MyModule2.myFunc2() +// ^ref +// ^dv- +// ^in- diff --git a/tests/analysis_tests/tests-namespaced-references/src/MyModule2.res b/tests/analysis_tests/tests-namespaced-references/src/MyModule2.res new file mode 100644 index 00000000000..4e7d3930e1e --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/src/MyModule2.res @@ -0,0 +1,2 @@ +let myFunc2 = () => 42 +// ^ref diff --git a/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule1.res.txt b/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule1.res.txt new file mode 100644 index 00000000000..ba415fdf9d3 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule1.res.txt @@ -0,0 +1,9 @@ +References src/MyModule1.res 2:30 +[cmt] Found incremental cmt: MyModule1-MyNamespace.cmt +[cmt] Found incremental cmt: MyModule2-MyNamespace.cmt +[cmt] Found incremental cmt: MyModule1-MyNamespace.cmt +[cmt] Found incremental cmt: MyNamespace.cmt +[ +{"uri": "MyModule1.res", "range": {"start": {"line": 2, "character": 30}, "end": {"line": 2, "character": 37}}}, +{"uri": "MyModule2.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 11}}} +] diff --git a/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule2.res.txt b/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule2.res.txt new file mode 100644 index 00000000000..9a42ce08712 --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/src/expected/MyModule2.res.txt @@ -0,0 +1,5 @@ +References src/MyModule2.res 0:4 +[ +{"uri": "MyModule1.res", "range": {"start": {"line": 2, "character": 30}, "end": {"line": 2, "character": 37}}}, +{"uri": "MyModule2.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 11}}} +] diff --git a/tests/analysis_tests/tests-namespaced-references/test.sh b/tests/analysis_tests/tests-namespaced-references/test.sh new file mode 100755 index 00000000000..32801fb411f --- /dev/null +++ b/tests/analysis_tests/tests-namespaced-references/test.sh @@ -0,0 +1,30 @@ +for file in src/*.res; do + output="$(dirname $file)/expected/$(basename $file).txt" + ../../../_build/install/default/bin/rescript-editor-analysis test $file &> $output + # CI. We use LF, and the CI OCaml fork prints CRLF. Convert. + if [ "$RUNNER_OS" == "Windows" ]; then + perl -pi -e 's/\r\n/\n/g' -- $output + fi + + # Remove unwanted output (machine-specific path) + perl -ni -e 'print unless /^\[getRuntimeDir\]/' -- $output + + # Strip leading newlines caused by ^in+ and ^dv+ marker usage. + perl -0pi -e 's/\A\n+//' -- $output + + # Strip trailing newlines caused by ^dv- and ^in- marker usage. + perl -0pi -e 's/\n+\z/\n/' -- $output +done + +warningYellow='\033[0;33m' +successGreen='\033[0;32m' +reset='\033[0m' + +diff=$(git ls-files --modified src/expected) +if [[ $diff = "" ]]; then + printf "${successGreen}✅ No unstaged tests difference.${reset}\n" +else + printf "${warningYellow}⚠️ There are unstaged differences in tests/! Did you break a test?\n${diff}\n${reset}" + git --no-pager diff src/expected + exit 1 +fi diff --git a/tests/analysis_tests/tests-reanalyze/termination/expected/termination.txt b/tests/analysis_tests/tests-reanalyze/termination/expected/termination.txt index 63e15f81349..5852d6ee99e 100644 --- a/tests/analysis_tests/tests-reanalyze/termination/expected/termination.txt +++ b/tests/analysis_tests/tests-reanalyze/termination/expected/termination.txt @@ -70,15 +70,16 @@ Termination Analysis for butSecondArgumentIsAlwaysEvaluated Function Table - 1 parseExpression: [_ || _]; [+Parser.next; parseExpression; parseExpression; _ || _] - 2 parseList: parseList$loop - 3 parseList$loop: [_ || f; parseList$loop; _] - 4 parseListExpression: _ - 5 parseListExpression2: parseExpression; parseList - 6 parseListInt: _ - 7 parseListIntTailRecursive: parseListIntTailRecursive$loop - 8 parseListIntTailRecursive$loop: [_ || parseListIntTailRecursive$loop] - 9 parseListListInt: parseList + 1 parseExpression: [_ || _]; [+Parser.next; parseExpression; parseExpression; _ || parseInt] + 2 parseInt: [_ || _]; +Parser.next; _ + 3 parseList: parseList$loop + 4 parseList$loop: [_ || f; parseList$loop; _] + 5 parseListExpression: _ + 6 parseListExpression2: parseExpression; parseList + 7 parseListInt: parseList + 8 parseListIntTailRecursive: parseListIntTailRecursive$loop + 9 parseListIntTailRecursive$loop: [_ || parseInt; parseListIntTailRecursive$loop] + 10 parseListListInt: parseList Termination Analysis for parseListInt @@ -111,10 +112,13 @@ Function Table 1 alwaysReturnNone: [+Parser.next; alwaysReturnNone || None] - 2 parseIntOWrapper: _ - 3 parseListIntO: _ - 4 testAlwaysReturnNone: alwaysReturnNone - 5 thisMakesNoProgress: None; [_ || +Parser.next; Some] + 2 parseIntO: [+Parser.next; Some || None] + 3 parseIntOWrapper: parseIntO + 4 parseListIntO: parseListO + 5 parseListO: parseListO$loop + 6 parseListO$loop: [+Parser.next; _ || switch f {some: parseListO$loop, none: _}] + 7 testAlwaysReturnNone: alwaysReturnNone + 8 thisMakesNoProgress: None; [_ || +Parser.next; Some] Termination Analysis for parseListIntO @@ -149,10 +153,10 @@ Termination Analysis Stats Files:1 Recursive Blocks:21 - Functions:45 - Infinite Loops:12 - Hygiene Errors:3 - Cache Hits:4/21 + Functions:49 + Infinite Loops:10 + Hygiene Errors:2 + Cache Hits:7/30 Error Termination @@ -207,10 +211,6 @@ TestCyberTruck.res:217:32-73 Call must have named argument f - Error Hygiene - TestCyberTruck.res:198:29-53 - Named argument f must be passed a recursive function - Error Termination TestCyberTruck.res:180:15-21 Possible infinite loop when calling parseList$loop which is parseList$loop @@ -219,21 +219,6 @@ 2 parseList (TestCyberTruck.res 201) 1 parseListListInt (TestCyberTruck.res 201) - Error Termination - TestCyberTruck.res:180:15-21 - Possible infinite loop when calling parseList$loop which is parseList$loop - CallStack: - 3 parseList$loop (TestCyberTruck.res 183) - 2 parseList (TestCyberTruck.res 220) - 1 parseListExpression2 (TestCyberTruck.res 220) - - Error Termination - TestCyberTruck.res:228:7-38 - Possible infinite loop when calling parseListIntTailRecursive$loop - CallStack: - 2 parseListIntTailRecursive$loop (TestCyberTruck.res 230) - 1 parseListIntTailRecursive (TestCyberTruck.res 223) - Error Termination TestCyberTruck.res:238:31-49 Possible infinite loop when calling loopAfterProgress @@ -246,4 +231,4 @@ CallStack: 1 countRendersCompiled (TestCyberTruck.res 283) - Analysis reported 15 issues (Error Hygiene:3, Error Termination:12) + Analysis reported 12 issues (Error Hygiene:2, Error Termination:10) diff --git a/tests/analysis_tests/tests/src/SignatureHelp.res b/tests/analysis_tests/tests/src/SignatureHelp.res index 72108e295ff..b2b351680be 100644 --- a/tests/analysis_tests/tests/src/SignatureHelp.res +++ b/tests/analysis_tests/tests/src/SignatureHelp.res @@ -157,3 +157,13 @@ let _bbb = Error("err") let _cc = Some(true) // ^she + +let makeAdder = (base: int, offset: int): (int => int) => { + let _ = offset + x => x + base + offset +} + +// Only makeAdder's own two parameters may be listed; the returned +// function's parameter belongs to a different call site. +// let _ = makeAdder(1, 2) +// ^she diff --git a/tests/analysis_tests/tests/src/expected/SignatureHelp.res.txt b/tests/analysis_tests/tests/src/expected/SignatureHelp.res.txt index cdb8f9a2047..6b38618481e 100644 --- a/tests/analysis_tests/tests/src/expected/SignatureHelp.res.txt +++ b/tests/analysis_tests/tests/src/expected/SignatureHelp.res.txt @@ -10,12 +10,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: unlabelled<0> extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -34,12 +33,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: unlabelled<0> extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -58,12 +56,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: ~two extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -82,12 +79,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: ~two extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -106,12 +102,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: ~four extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -130,12 +125,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: ~four extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -154,11 +148,11 @@ ContextPath Value[otherFunc] Path otherFunc argAtCursor: unlabelled<0> extracted params: -[(string, int, float] +[string, int, float] { "signatures": [{ "label": "(string, int, float) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 0 @@ -176,11 +170,11 @@ ContextPath Value[otherFunc] Path otherFunc argAtCursor: unlabelled<0> extracted params: -[(string, int, float] +[string, int, float] { "signatures": [{ "label": "(string, int, float) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 0 @@ -198,11 +192,11 @@ ContextPath Value[otherFunc] Path otherFunc argAtCursor: unlabelled<2> extracted params: -[(string, int, float] +[string, int, float] { "signatures": [{ "label": "(string, int, float) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 2 @@ -220,11 +214,11 @@ ContextPath Value[Completion, Lib, foo] Path Completion.Lib.foo argAtCursor: ~age extracted params: -[(~age: int, ~name: string] +[age: int, name: string] { "signatures": [{ "label": "(~age: int, ~name: string) => string", - "parameters": [{"label": [0, 10], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [2, 10], "documentation": {"kind": "markdown", "value": ""}}, {"label": [13, 25], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 0 @@ -265,11 +259,11 @@ ContextPath Value[otherFunc] Path otherFunc argAtCursor: unlabelled<1> extracted params: -[(string, int, float] +[string, int, float] { "signatures": [{ "label": "(string, int, float) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [9, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 19], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 1 @@ -287,11 +281,11 @@ ContextPath Value[fn] Path fn argAtCursor: unlabelled<1> extracted params: -[(int, string, int] +[int, string, int] { "signatures": [{ "label": "(int, string, int) => unit", - "parameters": [{"label": [0, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 1 @@ -309,11 +303,11 @@ ContextPath Value[fn] Path fn argAtCursor: unlabelled<1> extracted params: -[(int, string, int] +[int, string, int] { "signatures": [{ "label": "(int, string, int) => unit", - "parameters": [{"label": [0, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 1 @@ -331,11 +325,11 @@ ContextPath Value[fn] Path fn argAtCursor: unlabelled<2> extracted params: -[(int, string, int] +[int, string, int] { "signatures": [{ "label": "(int, string, int) => unit", - "parameters": [{"label": [0, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] + "parameters": [{"label": [1, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 12], "documentation": {"kind": "markdown", "value": ""}}, {"label": [14, 17], "documentation": {"kind": "markdown", "value": ""}}] }], "activeSignature": 0, "activeParameter": 2 @@ -381,12 +375,11 @@ ContextPath Value[someFunc] Path someFunc argAtCursor: unlabelled<0> extracted params: -[( - int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] +[int, two: string=?, three: unit => unit, four: someVariant, unit] { "signatures": [{ "label": "(\n int,\n ~two: string=?,\n ~three: unit => unit,\n ~four: someVariant,\n unit,\n) => unit", - "parameters": [{"label": [0, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [11, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [29, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [53, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [4, 7], "documentation": {"kind": "markdown", "value": ""}}, {"label": [12, 25], "documentation": {"kind": "markdown", "value": ""}}, {"label": [30, 49], "documentation": {"kind": "markdown", "value": ""}}, {"label": [54, 71], "documentation": {"kind": "markdown", "value": "```rescript\ntype someVariant = One | Two | Three\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C0%2C0%5D)"}}, {"label": [75, 79], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, @@ -473,11 +466,11 @@ Signature help src/SignatureHelp.res 105:9 Signature help src/SignatureHelp.res 113:42 argAtCursor: unlabelled<1> extracted params: -[(array, int => int] +[array, int => int] { "signatures": [{ "label": "(array, int => int) => array", - "parameters": [{"label": [0, 11], "documentation": {"kind": "markdown", "value": ""}}, {"label": [13, 23], "documentation": {"kind": "markdown", "value": ""}}], + "parameters": [{"label": [1, 11], "documentation": {"kind": "markdown", "value": ""}}, {"label": [13, 23], "documentation": {"kind": "markdown", "value": ""}}], "documentation": {"kind": "markdown", "value": "\n`map(array, fn)` returns a new array with all elements from `array`, each element transformed using the provided `fn`.\n\nSee [`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on MDN.\n\n## Examples\n\n```rescript\nlet array = [\"Hello\", \"Hi\", \"Good bye\"]\nlet mappedArray = array->Array.map(greeting => greeting ++ \" to you\")\n\nmappedArray == [\"Hello to you\", \"Hi to you\", \"Good bye to you\"]\n```\n"} }], "activeSignature": 0, @@ -487,11 +480,11 @@ extracted params: Signature help src/SignatureHelp.res 132:18 argAtCursor: unlabelled<0> extracted params: -[(x, tt] +[x, tt] { "signatures": [{ "label": "(x, tt) => string", - "parameters": [{"label": [0, 2], "documentation": {"kind": "markdown", "value": "```rescript\ntype x = {age?: int, name?: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C117%2C0%5D)"}}, {"label": [4, 6], "documentation": {"kind": "markdown", "value": "```rescript\ntype tt = One\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C123%2C0%5D)"}}], + "parameters": [{"label": [1, 2], "documentation": {"kind": "markdown", "value": "```rescript\ntype x = {age?: int, name?: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C117%2C0%5D)"}}, {"label": [4, 6], "documentation": {"kind": "markdown", "value": "```rescript\ntype tt = One\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C123%2C0%5D)"}}], "documentation": {"kind": "markdown", "value": " Some stuff "} }], "activeSignature": 0, @@ -501,11 +494,11 @@ extracted params: Signature help src/SignatureHelp.res 135:22 argAtCursor: unlabelled<1> extracted params: -[(x, tt] +[x, tt] { "signatures": [{ "label": "(x, tt) => string", - "parameters": [{"label": [0, 2], "documentation": {"kind": "markdown", "value": "```rescript\ntype x = {age?: int, name?: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C117%2C0%5D)"}}, {"label": [4, 6], "documentation": {"kind": "markdown", "value": "```rescript\ntype tt = One\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C123%2C0%5D)"}}], + "parameters": [{"label": [1, 2], "documentation": {"kind": "markdown", "value": "```rescript\ntype x = {age?: int, name?: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C117%2C0%5D)"}}, {"label": [4, 6], "documentation": {"kind": "markdown", "value": "```rescript\ntype tt = One\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22SignatureHelp.res%22%2C123%2C0%5D)"}}], "documentation": {"kind": "markdown", "value": " Some stuff "} }], "activeSignature": 0, @@ -600,3 +593,25 @@ Signature help src/SignatureHelp.res 157:16 "activeParameter": 0 } +Signature help src/SignatureHelp.res 167:25 +posCursor:[167:20] posNoWhite:[167:19] Found expr:[167:11->167:26] +Pexp_apply ...[167:11->167:20] (...[167:21->167:22], ...[167:24->167:25]) +posCursor:[167:20] posNoWhite:[167:19] Found expr:[167:11->167:20] +Pexp_ident makeAdder:[167:11->167:20] +Completable: Cpath Value[makeAdder] +Package opens Stdlib.place holder Pervasives.JsxModules.place holder +Resolved opens 1 Stdlib +ContextPath Value[makeAdder] +Path makeAdder +argAtCursor: unlabelled<1> +extracted params: +[int, int] +{ + "signatures": [{ + "label": "(int, int) => int => int", + "parameters": [{"label": [1, 4], "documentation": {"kind": "markdown", "value": ""}}, {"label": [6, 9], "documentation": {"kind": "markdown", "value": ""}}] + }], + "activeSignature": 0, + "activeParameter": 1 +} + diff --git a/tests/build_tests/super_errors/expected/coercion_arity_mismatch.res.expected b/tests/build_tests/super_errors/expected/coercion_arity_mismatch.res.expected new file mode 100644 index 00000000000..6fd77c58aa4 --- /dev/null +++ b/tests/build_tests/super_errors/expected/coercion_arity_mismatch.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/coercion_arity_mismatch.res:2:10-31 + + 1 │ let f = (x: int) => (y: int) => x + y + 2 │ let g = (f :> (int, int) => int) + 3 │ + + Type int => int => int is not a subtype of (int, int) => int \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/module_sig_value_arity_mismatch.res.expected b/tests/build_tests/super_errors/expected/module_sig_value_arity_mismatch.res.expected new file mode 100644 index 00000000000..f491e37f771 --- /dev/null +++ b/tests/build_tests/super_errors/expected/module_sig_value_arity_mismatch.res.expected @@ -0,0 +1,28 @@ + + We've found a bug for you! + /.../fixtures/module_sig_value_arity_mismatch.res:3:5-5:1 + + 1 │ module M: { + 2 │ let f: (int, int) => int + 3 │ } = { + 4 │  let f = (x: int) => (y: int) => x + y + 5 │ } + 6 │ + + Signature mismatch: + Modules do not match: + { + let f: int => int => int +} + is not included in + { + let f: (int, int) => int +} + Values do not match: + let f: int => int => int + is not included in + let f: (int, int) => int + /.../fixtures/module_sig_value_arity_mismatch.res:2:3-26: + Expected declaration + /.../fixtures/module_sig_value_arity_mismatch.res:4:7: + Actual declaration \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/type_decl_function_arity_mismatch.res.expected b/tests/build_tests/super_errors/expected/type_decl_function_arity_mismatch.res.expected new file mode 100644 index 00000000000..14e91bcf247 --- /dev/null +++ b/tests/build_tests/super_errors/expected/type_decl_function_arity_mismatch.res.expected @@ -0,0 +1,28 @@ + + We've found a bug for you! + /.../fixtures/type_decl_function_arity_mismatch.res:3:5-5:1 + + 1 │ module M: { + 2 │ type t = (int, int) => int + 3 │ } = { + 4 │  type t = int => int => int + 5 │ } + 6 │ + + Signature mismatch: + Modules do not match: + { + type t = int => int => int +} + is not included in + { + type t = (int, int) => int +} + Type declarations do not match: + type t = int => int => int + is not included in + type t = (int, int) => int + /.../fixtures/type_decl_function_arity_mismatch.res:2:3-28: + Expected declaration + /.../fixtures/type_decl_function_arity_mismatch.res:4:3-28: + Actual declaration \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/coercion_arity_mismatch.res b/tests/build_tests/super_errors/fixtures/coercion_arity_mismatch.res new file mode 100644 index 00000000000..93d9111360e --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/coercion_arity_mismatch.res @@ -0,0 +1,2 @@ +let f = (x: int) => (y: int) => x + y +let g = (f :> (int, int) => int) diff --git a/tests/build_tests/super_errors/fixtures/module_sig_value_arity_mismatch.res b/tests/build_tests/super_errors/fixtures/module_sig_value_arity_mismatch.res new file mode 100644 index 00000000000..a335585ac0a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/module_sig_value_arity_mismatch.res @@ -0,0 +1,5 @@ +module M: { + let f: (int, int) => int +} = { + let f = (x: int) => (y: int) => x + y +} diff --git a/tests/build_tests/super_errors/fixtures/type_decl_function_arity_mismatch.res b/tests/build_tests/super_errors/fixtures/type_decl_function_arity_mismatch.res new file mode 100644 index 00000000000..4a831f446dd --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/type_decl_function_arity_mismatch.res @@ -0,0 +1,5 @@ +module M: { + type t = (int, int) => int +} = { + type t = int => int => int +} diff --git a/tests/dependencies/rescript-react/package.json b/tests/dependencies/rescript-react/package.json index 6271f397f34..8a863b8ad51 100644 --- a/tests/dependencies/rescript-react/package.json +++ b/tests/dependencies/rescript-react/package.json @@ -1,7 +1,7 @@ { "name": "@rescript/react", "private": true, - "version": "12.3.0", + "version": "12.3.1", "homepage": "https://rescript-lang.org", "bugs": "https://github.com/rescript-lang/rescript/issues", "repository": { diff --git a/tests/ounit_tests/ounit_analysis_references_tests.ml b/tests/ounit_tests/ounit_analysis_references_tests.ml new file mode 100644 index 00000000000..a81e69764e1 --- /dev/null +++ b/tests/ounit_tests/ounit_analysis_references_tests.ml @@ -0,0 +1,25 @@ +open OUnit + +let show_reference (module_name, path) = + Printf.sprintf "%s:%s" module_name (String.concat "." path) + +let assert_ref_key_eq ~expected ~actual = + assert_equal ~printer:show_reference expected actual + +let suites = + __FILE__ + >::: [ + ( "without namespace, external reference is unchanged" >:: fun _ -> + assert_ref_key_eq ~expected:("MyModule2", ["myFunc2"]) + ~actual: + (Analysis.References.normalizeExternalReferenceKey + ~namespace:None ~moduleName:"MyModule2" ~path:["myFunc2"]) ); + ( "with namespace, hidden module resolves to public namespace path" + >:: fun _ -> + assert_ref_key_eq + ~expected:("MyNamespace", ["MyModule2"; "myFunc2"]) + ~actual: + (Analysis.References.normalizeExternalReferenceKey + ~namespace:(Some "MyNamespace") + ~moduleName:"MyModule2-MyNamespace" ~path:["myFunc2"]) ); + ] diff --git a/tests/ounit_tests/ounit_tests_main.ml b/tests/ounit_tests/ounit_tests_main.ml index ee39b4a37f3..f5a676e5fa8 100644 --- a/tests/ounit_tests/ounit_tests_main.ml +++ b/tests/ounit_tests/ounit_tests_main.ml @@ -22,6 +22,7 @@ let suites = Ounit_bsb_pkg_tests.suites; Ounit_util_tests.suites; Ounit_jsx_loc_tests.suites; + Ounit_analysis_references_tests.suites; ] let _ = OUnit.run_test_tt_main suites diff --git a/tests/ounit_tests/ounit_utf8_test.ml b/tests/ounit_tests/ounit_utf8_test.ml index 42846b6d049..b94babc4ea1 100644 --- a/tests/ounit_tests/ounit_utf8_test.ml +++ b/tests/ounit_tests/ounit_utf8_test.ml @@ -4,6 +4,7 @@ let ( >:: ), ( >::: ) = OUnit.(( >:: ), ( >::: )) let ( =~ ) = OUnit.assert_equal + let suites = __FILE__ >::: [ @@ -29,4 +30,6 @@ let suites = 105; ] ); (__LOC__ >:: fun _ -> Ext_utf8.decode_utf8_string "" =~ []); + ( __LOC__ >:: fun _ -> + Code_frame.break_long_line 4 "abc—def" =~ ["abc—"; "def"] ); ] diff --git a/tests/syntax_tests/data/parsing/errors/typexpr/expected/arrow.res.txt b/tests/syntax_tests/data/parsing/errors/typexpr/expected/arrow.res.txt index c621d68f7f0..31e172542b1 100644 --- a/tests/syntax_tests/data/parsing/errors/typexpr/expected/arrow.res.txt +++ b/tests/syntax_tests/data/parsing/errors/typexpr/expected/arrow.res.txt @@ -46,5 +46,5 @@ module Error3 = type nonrec observation = { observed: int ; - onStep: currentValue:unit -> [%rescript.typehole ] } + onStep: currentValue:unit -> [%rescript.typehole ] (a:1) } end \ No newline at end of file diff --git a/tests/syntax_tests/data/parsing/grammar/typexpr/expected/es6Arrow.res.txt b/tests/syntax_tests/data/parsing/grammar/typexpr/expected/es6Arrow.res.txt index 47f23df4998..f302e57ce11 100644 --- a/tests/syntax_tests/data/parsing/grammar/typexpr/expected/es6Arrow.res.txt +++ b/tests/syntax_tests/data/parsing/grammar/typexpr/expected/es6Arrow.res.txt @@ -12,15 +12,15 @@ let (t : a:int -> b:int -> int (a:2)) = xf let (t : ?a:int -> ?b:int -> int (a:2)) = xf let (t : int -> int -> int -> int (a:1) (a:1) (a:1)) = xf let (t : a:int -> b:int -> c:int -> int (a:1) (a:1) (a:1)) = xf -type nonrec t = f:int -> string -type nonrec t = ?f:int -> string -let (f : f:int -> string) = fx -let (f : ?f:int -> string) = fx type nonrec t = f:int -> string (a:1) -type nonrec t = f:int -> string +type nonrec t = ?f:int -> string (a:1) +let (f : f:int -> string (a:1)) = fx +let (f : ?f:int -> string (a:1)) = fx +type nonrec t = f:int -> string (a:1) +type nonrec t = f:int -> string (a:1) +type nonrec t = f:(int -> string (a:1)) -> float (a:1) type nonrec t = f:(int -> string (a:1)) -> float (a:1) -type nonrec t = f:(int -> string (a:1)) -> float -type nonrec t = f:int -> string -> float (a:1) +type nonrec t = f:int -> string -> float (a:1) (a:1) type nonrec t = a:int[@attrBeforeLblA ] -> b:int[@attrBeforeLblB ] -> ((float)[@attr ]) -> unit (a:3) @@ -28,7 +28,7 @@ type nonrec t = ((a:int -> ((b:int -> ((float)[@attr ]) -> unit (a:1) (a:1))[@attrBeforeLblB ]) (a:1)) [@attrBeforeLblA ]) -type nonrec t = a:int[@attr ] -> unit +type nonrec t = a:int[@attr ] -> unit (a:1) type nonrec 'a getInitialPropsFn = < query: string dict ;req: 'a Js.t Js.Nullable.t > -> 'a Js.t Js.Promise.t (a:1) \ No newline at end of file diff --git a/tests/syntax_tests/data/printer/comments/expected/valueBindings.res.txt b/tests/syntax_tests/data/printer/comments/expected/valueBindings.res.txt index c87a9530b1e..e5cac613d77 100644 --- a/tests/syntax_tests/data/printer/comments/expected/valueBindings.res.txt +++ b/tests/syntax_tests/data/printer/comments/expected/valueBindings.res.txt @@ -10,6 +10,14 @@ let walkList: 'node. unit = comments => { let x /* comment */ = 0 } +let x // comment before equals + = 1 + +let multilineString // comment before equals + = " +multiline +" + let walkList: 'node. ( ~prevLoc: Location.t=?, ~getLoc: 'node => Location.t, diff --git a/tests/syntax_tests/data/printer/comments/valueBindings.res b/tests/syntax_tests/data/printer/comments/valueBindings.res index c47f0276e6b..b575aee5f4b 100644 --- a/tests/syntax_tests/data/printer/comments/valueBindings.res +++ b/tests/syntax_tests/data/printer/comments/valueBindings.res @@ -10,6 +10,14 @@ let walkList: 'node. unit = comments => { let x /* comment */ = 0 } +let x // comment before equals += 1 + +let multilineString // comment before equals += " +multiline +" + let walkList: 'node. ( ~prevLoc: Location.t=?, ~getLoc: 'node => Location.t, diff --git a/tests/tests/src/bs_set_int_test.mjs b/tests/tests/src/bs_set_int_test.mjs index 06d988fff24..ea3c52b4863 100644 --- a/tests/tests/src/bs_set_int_test.mjs +++ b/tests/tests/src/bs_set_int_test.mjs @@ -61,23 +61,23 @@ Mocha.describe("Bs_set_int_test", () => { let nr = r; Test_utils.ok("File \"bs_set_int_test.res\", line 40, characters 7-14", Belt_SetInt.eq(match[0], nl)); Test_utils.ok("File \"bs_set_int_test.res\", line 41, characters 7-14", Belt_SetInt.eq(match[1], nr)); - let i$2 = range(50, 100); let s = Belt_SetInt.intersect(Belt_SetInt.fromArray(range(1, 100)), Belt_SetInt.fromArray(range(50, 200))); + let i$2 = range(50, 100); Test_utils.ok("File \"bs_set_int_test.res\", line 44, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$2), s)); - let i$3 = range(1, 200); let s$1 = Belt_SetInt.union(Belt_SetInt.fromArray(range(1, 100)), Belt_SetInt.fromArray(range(50, 200))); + let i$3 = range(1, 200); Test_utils.ok("File \"bs_set_int_test.res\", line 55, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$3), s$1)); - let i$4 = range(1, 49); let s$2 = Belt_SetInt.diff(Belt_SetInt.fromArray(range(1, 100)), Belt_SetInt.fromArray(range(50, 200))); + let i$4 = range(1, 49); Test_utils.ok("File \"bs_set_int_test.res\", line 66, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$4), s$2)); - let i$5 = revRange(50, 100); let s$3 = Belt_SetInt.intersect(Belt_SetInt.fromArray(revRange(1, 100)), Belt_SetInt.fromArray(revRange(50, 200))); + let i$5 = revRange(50, 100); Test_utils.ok("File \"bs_set_int_test.res\", line 77, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$5), s$3)); - let i$6 = revRange(1, 200); let s$4 = Belt_SetInt.union(Belt_SetInt.fromArray(revRange(1, 100)), Belt_SetInt.fromArray(revRange(50, 200))); + let i$6 = revRange(1, 200); Test_utils.ok("File \"bs_set_int_test.res\", line 88, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$6), s$4)); - let i$7 = revRange(1, 49); let s$5 = Belt_SetInt.diff(Belt_SetInt.fromArray(revRange(1, 100)), Belt_SetInt.fromArray(revRange(50, 200))); + let i$7 = revRange(1, 49); Test_utils.ok("File \"bs_set_int_test.res\", line 99, characters 6-13", Belt_SetInt.eq(Belt_SetInt.fromArray(i$7), s$5)); let ss = [ 1, diff --git a/tests/tests/src/exponentiation_test.mjs b/tests/tests/src/exponentiation_test.mjs index c39ea9465db..debb6adbc00 100644 --- a/tests/tests/src/exponentiation_test.mjs +++ b/tests/tests/src/exponentiation_test.mjs @@ -7,25 +7,49 @@ let intPow = ((a, b) => Math.pow(a, b) | 0); let four = 4; +function floatPowDiv(base, numerator, denominator) { + return base ** (numerator / denominator); +} + +function floatPowMul(base, left, right) { + return base ** (left * right); +} + +function floatPowMod(base, value, modulus) { + return base ** (value % modulus); +} + +function bigintPowMul(base, left, right) { + return base ** (left * right); +} + Mocha.describe("Exponentiation_test", () => { Mocha.test("exponentiation operations", () => { - Test_utils.eq("File \"exponentiation_test.res\", line 11, characters 7-14", 2 ** 3 ** 2, Math.pow(2, Math.pow(3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 12, characters 7-14", 2 ** (-3) ** 2, Math.pow(2, Math.pow(-3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 13, characters 7-14", (2 ** 3) ** 2, Math.pow(Math.pow(2, 3), 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 14, characters 7-14", (-2) ** 2, Math.pow(-2, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 16, characters 7-14", 512, intPow(2, intPow(3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 17, characters 7-14", 512, intPow(2, intPow(-3, 2))); - Test_utils.eq("File \"exponentiation_test.res\", line 18, characters 7-14", 64, intPow(intPow(2, 3), 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 19, characters 7-14", -2147483648, intPow(-2, 31)); - Test_utils.eq("File \"exponentiation_test.res\", line 20, characters 7-14", 0, intPow(2, 32)); - Test_utils.eq("File \"exponentiation_test.res\", line 21, characters 7-14", 0, intPow(2147483647, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 22, characters 7-14", 0, intPow(-2147483648, 2)); - Test_utils.eq("File \"exponentiation_test.res\", line 24, characters 7-14", 256, four ** four | 0); + Test_utils.eq("File \"exponentiation_test.res\", line 17, characters 7-14", 2 ** 3 ** 2, Math.pow(2, Math.pow(3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 18, characters 7-14", 2 ** (-3) ** 2, Math.pow(2, Math.pow(-3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 19, characters 7-14", (2 ** 3) ** 2, Math.pow(Math.pow(2, 3), 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 20, characters 7-14", (-2) ** 2, Math.pow(-2, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 22, characters 7-14", 512, intPow(2, intPow(3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 23, characters 7-14", 512, intPow(2, intPow(-3, 2))); + Test_utils.eq("File \"exponentiation_test.res\", line 24, characters 7-14", 64, intPow(intPow(2, 3), 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 25, characters 7-14", -2147483648, intPow(-2, 31)); + Test_utils.eq("File \"exponentiation_test.res\", line 26, characters 7-14", 0, intPow(2, 32)); + Test_utils.eq("File \"exponentiation_test.res\", line 27, characters 7-14", 0, intPow(2147483647, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 28, characters 7-14", 0, intPow(-2147483648, 2)); + Test_utils.eq("File \"exponentiation_test.res\", line 30, characters 7-14", 256, four ** four | 0); + Test_utils.eq("File \"exponentiation_test.res\", line 32, characters 7-14", 2 ** (0 / 10000), 1); + Test_utils.eq("File \"exponentiation_test.res\", line 33, characters 7-14", 2 ** (3 * 4), 4096); + Test_utils.eq("File \"exponentiation_test.res\", line 34, characters 7-14", 2 ** (5 % 3), 4); + Test_utils.eq("File \"exponentiation_test.res\", line 35, characters 7-14", 2n ** (3n * 2n), 64n); }); }); export { intPow, four, + floatPowDiv, + floatPowMul, + floatPowMod, + bigintPowMul, } /* Not a pure module */ diff --git a/tests/tests/src/exponentiation_test.res b/tests/tests/src/exponentiation_test.res index 19fb9cb7b44..216de749954 100644 --- a/tests/tests/src/exponentiation_test.res +++ b/tests/tests/src/exponentiation_test.res @@ -6,6 +6,12 @@ external jsPow: (float, float) => float = "Math.pow" let intPow: (int, int) => int = %raw(`(a, b) => Math.pow(a, b) | 0`) let four: int = %raw(`4`) +let floatPowDiv = (base: float, numerator: float, denominator: float) => + base ** (numerator /. denominator) +let floatPowMul = (base: float, left: float, right: float) => base ** (left *. right) +let floatPowMod = (base: float, value: float, modulus: float) => base ** (value % modulus) +let bigintPowMul = (base: bigint, left: bigint, right: bigint) => base ** (left * right) + describe(__MODULE__, () => { test("exponentiation operations", () => { eq(__LOC__, 2. ** 3. ** 2., jsPow(2., jsPow(3., 2.))) @@ -22,5 +28,10 @@ describe(__MODULE__, () => { eq(__LOC__, -2147483648 ** 2, intPow(-2147483648, 2)) eq(__LOC__, 4 ** 4, four ** four) + + eq(__LOC__, floatPowDiv(2., 0., 10000.), 1.) + eq(__LOC__, floatPowMul(2., 3., 4.), 4096.) + eq(__LOC__, floatPowMod(2., 5., 3.), 4.) + eq(__LOC__, bigintPowMul(2n, 3n, 2n), 64n) }) }) diff --git a/tests/tests/src/inline_arg_order_test.mjs b/tests/tests/src/inline_arg_order_test.mjs new file mode 100644 index 00000000000..708cad8ba0e --- /dev/null +++ b/tests/tests/src/inline_arg_order_test.mjs @@ -0,0 +1,55 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js"; + +let recorded = []; + +let equalish = Primitive_object.equal; + +function copy(a) { + return a.slice(); +} + +function helper(x, y) { + return Primitive_object.equal(x, y.slice()); +} + +function effA(_n) { + while (true) { + let n = _n; + recorded.push("a"); + if (n <= 0) { + return [n]; + } + _n = n - 1 | 0; + continue; + }; +} + +function effB(_n) { + while (true) { + let n = _n; + recorded.push("b"); + if (n <= 0) { + return [n]; + } + _n = n - 1 | 0; + continue; + }; +} + +let x = effA(0); + +let y = effB(0); + +Primitive_object.equal(x, y.slice()); + +export { + recorded, + equalish, + copy, + helper, + effA, + effB, +} +/* x Not a pure module */ diff --git a/tests/tests/src/inline_arg_order_test.res b/tests/tests/src/inline_arg_order_test.res new file mode 100644 index 00000000000..89818997914 --- /dev/null +++ b/tests/tests/src/inline_arg_order_test.res @@ -0,0 +1,30 @@ +// The beta reducer used to stack inlined-call argument bindings in reverse +// parameter order, so the last argument was evaluated first. The checked-in +// JS pins evaluation to source order: effA runs before effB. +let recorded: array = [] + +let equalish = (a: array, b: array) => a == b +let copy = (a: array) => Array.copy(a) +let helper = (x, y) => equalish(x, copy(y)) + +let rec effA = n => { + recorded->Array.push("a") + if n > 0 { + effA(n - 1) + } else { + [n] + } +} + +let rec effB = n => { + recorded->Array.push("b") + if n > 0 { + effB(n - 1) + } else { + [n] + } +} + +let _ = { + helper(effA(0), effB(0)) +} diff --git a/tests/tests/src/uncurried_default.args.mjs b/tests/tests/src/uncurried_default.args.mjs index 18f639c123e..e86b8db99ed 100644 --- a/tests/tests/src/uncurried_default.args.mjs +++ b/tests/tests/src/uncurried_default.args.mjs @@ -1,10 +1,10 @@ // Generated by ReScript, PLEASE EDIT WITH CARE -function withOpt($staropt$star, y) { - return ($staropt$star$1, w) => { - let x = $staropt$star !== undefined ? $staropt$star : 1; - let z = $staropt$star$1 !== undefined ? $staropt$star$1 : 1; +function withOpt(xOpt, y) { + let x = xOpt !== undefined ? xOpt : 1; + return (zOpt, w) => { + let z = zOpt !== undefined ? zOpt : 1; return ((x + y | 0) + z | 0) + w | 0; }; } @@ -53,10 +53,10 @@ let StandardNotation = { r3: r3 }; -function withOpt$1($staropt$star, y) { - return ($staropt$star$1, w) => { - let x = $staropt$star !== undefined ? $staropt$star : 1; - let z = $staropt$star$1 !== undefined ? $staropt$star$1 : 1; +function withOpt$1(xOpt, y) { + let x = xOpt !== undefined ? xOpt : 1; + return (zOpt, w) => { + let z = zOpt !== undefined ? zOpt : 1; return ((x + y | 0) + z | 0) + w | 0; }; } @@ -98,6 +98,22 @@ let M = { foo: foo }; +let outerScope = "outer"; + +function shadowedDefault(xOpt, outerScope$1) { + let x = xOpt !== undefined ? xOpt : outerScope; + return [ + x, + outerScope$1 + ]; +} + +function laterUsesEarlier(xOpt, yOpt, param) { + let x = xOpt !== undefined ? xOpt : 1; + let y = yOpt !== undefined ? yOpt : x + 1 | 0; + return x + y | 0; +} + export { StandardNotation, withOpt$1 as withOpt, @@ -109,5 +125,8 @@ export { r2$1 as r2, foo3$1 as foo3, M, + outerScope, + shadowedDefault, + laterUsesEarlier, } /* testWithOpt Not a pure module */ diff --git a/tests/tests/src/uncurried_default.args.res b/tests/tests/src/uncurried_default.args.res index c776b567349..38760adf020 100644 --- a/tests/tests/src/uncurried_default.args.res +++ b/tests/tests/src/uncurried_default.args.res @@ -1,3 +1,7 @@ +// The generated JS also pins *where* optional-parameter defaults are +// computed: each default must be evaluated when its own parameter group is +// applied (x's in the outer function, z's in the inner closure below), not +// pushed into the innermost body. module StandardNotation = { let withOpt = (~x=1, y) => (~z=1, w) => x + y + z + w let testWithOpt = withOpt(3)(4) @@ -33,3 +37,12 @@ module M: { } = { let foo = func => func() + 1 } + +// Scoping of defaults: a default sees only the parameters to its left — +// never a later parameter, and never a same-group shadow. `outerScope` in +// the default below is the top-level binding, not the parameter. +let outerScope = "outer" +let shadowedDefault = (~x=outerScope, outerScope: int) => (x, outerScope) + +// A default may use parameters to its left. +let laterUsesEarlier = (~x=1, ~y=x + 1, ()) => x + y diff --git a/yarn.config.cjs b/yarn.config.cjs index d0a45b60606..631382a105f 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -14,7 +14,7 @@ const execPromise = util.promisify(exec); * @param {Yarn.Constraints.Context} ctx */ async function enforceCompilerMeta({ Yarn }) { - const EXPECTED_VERSION = "12.3.0"; + const EXPECTED_VERSION = "12.3.1"; for (const workspace of Yarn.workspaces()) { const { ident } = workspace.pkg; diff --git a/yarn.lock b/yarn.lock index 64582a26b57..2f447e924d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -773,6 +773,14 @@ __metadata: languageName: unknown linkType: soft +"@tests/namespaced-references@workspace:tests/analysis_tests/tests-namespaced-references": + version: 0.0.0-use.local + resolution: "@tests/namespaced-references@workspace:tests/analysis_tests/tests-namespaced-references" + dependencies: + rescript: "workspace:^" + languageName: unknown + linkType: soft + "@tests/reanalyze-benchmark@workspace:tests/analysis_tests/tests-reanalyze/deadcode-benchmark": version: 0.0.0-use.local resolution: "@tests/reanalyze-benchmark@workspace:tests/analysis_tests/tests-reanalyze/deadcode-benchmark"