diff --git a/CHANGELOG.md b/CHANGELOG.md index efcfa340a..dab1057f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Proper default for `"uncurried"` in V11 projects. https://github.com/rescript-lang/rescript-vscode/pull/867 - Treat `result` type as a proper built in type. https://github.com/rescript-lang/rescript-vscode/pull/860 +- Fix infinite loop when resolving inferred completions when several values in scope has the same name. https://github.com/rescript-lang/rescript-vscode/pull/869 #### :nail_care: Polish diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 04a0634bf..9fdf52fd2 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -263,7 +263,7 @@ let findAllCompletions ~(env : QueryEnv.t) ~prefix ~exact ~namesUsed completionForExportedFields ~env ~prefix ~exact ~namesUsed @ completionForExportedModules ~env ~prefix ~exact ~namesUsed -let processLocalValue name loc contextPath ~prefix ~exact ~env +let processLocalValue name loc contextPath scope ~prefix ~exact ~env ~(localTables : LocalTables.t) = if Utils.checkName name ~prefix ~exact then match Hashtbl.find_opt localTables.valueTable (name, Loc.start loc) with @@ -279,14 +279,14 @@ let processLocalValue name loc contextPath ~prefix ~exact ~env } :: localTables.resultRev) | None -> - Log.log - (Printf.sprintf "Completion Value Not Found %s loc:%s\n" name - (Loc.toString loc)); + if !Cfg.debugFollowCtxPath then + Printf.printf "Completion Value Not Found %s loc:%s\n" name + (Loc.toString loc); localTables.resultRev <- Completion.create name ~env ~kind: (match contextPath with - | Some contextPath -> FollowContextPath contextPath + | Some contextPath -> FollowContextPath (contextPath, scope) | None -> Value (Ctype.newconstr @@ -622,17 +622,17 @@ let completionsGetCompletionType ~full = function | {Completion.kind = ExtractedType (typ, _); env} :: _ -> Some (typ, env) | _ -> None -let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos ~scope - = function +let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos = + function | {Completion.kind = Value typ; env} :: _ | {Completion.kind = ObjLabel typ; env} :: _ | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (TypeExpr typ, env) - | {Completion.kind = FollowContextPath ctxPath; env} :: _ -> + | {Completion.kind = FollowContextPath (ctxPath, scope); env} :: _ -> ctxPath |> getCompletionsForContextPath ~debug ~full ~env ~exact:true ~opens ~rawOpens ~pos ~scope - |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos | {Completion.kind = Type typ; env} :: _ -> ( match TypeUtils.extractTypeFromResolvedType typ ~env ~full with | None -> None @@ -642,16 +642,16 @@ let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos ~scope | _ -> None and completionsGetTypeEnv2 ~debug (completions : Completion.t list) ~full ~opens - ~rawOpens ~pos ~scope = + ~rawOpens ~pos = match completions with | {Completion.kind = Value typ; env} :: _ -> Some (typ, env) | {Completion.kind = ObjLabel typ; env} :: _ -> Some (typ, env) | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) - | {Completion.kind = FollowContextPath ctxPath; env} :: _ -> + | {Completion.kind = FollowContextPath (ctxPath, scope); env} :: _ -> ctxPath |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos | _ -> None and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact @@ -754,7 +754,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact cp |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos with | Some ((TypeExpr typ | ExtractedType (Tfunction {typ})), env) -> ( let rec reconstructFunctionType args tRet = @@ -805,7 +805,6 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact match completionsForCtxPath |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos - ~scope with | Some (TypeExpr typ, env) -> ( match typ |> TypeUtils.extractRecordType ~env ~package with @@ -839,7 +838,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact cp |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos with | Some (typ, env) -> ( match typ |> TypeUtils.extractObjectType ~env ~package with @@ -866,7 +865,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact cp |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope ~mode:Pipe - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos with | None -> [] | Some (typ, envFromCompletionItem) -> ( @@ -1031,7 +1030,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact path |> getCompletionsForPath ~debug ~completionContext:Value ~exact:true ~package ~opens ~full ~pos ~env ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos in let lowercaseComponent = match pathToComponent with @@ -1078,7 +1077,6 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos - ~scope with | Some ((TypeExpr typ | ExtractedType (Tfunction {typ})), env) -> (typ |> TypeUtils.getArgs ~full ~env, env) @@ -1115,7 +1113,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact rootCtxPath |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos with | Some (typ, env) -> ( match typ |> TypeUtils.resolveNestedPatternPath ~env ~full ~nested with @@ -1528,7 +1526,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = path |> getCompletionsForPath ~debug ~completionContext:Value ~exact:true ~package ~opens ~full ~pos ~env ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos in match completable with | Cnone -> [] @@ -1603,7 +1601,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = cp |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos with | Some (typ, _env) -> if debug then @@ -1639,7 +1637,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = contextPath |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact:true ~scope - |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos ~scope + |> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos with | Some (typ, env) -> ( match diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index 8c8163bc6..1b76a283a 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -141,7 +141,7 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover let opens = CompletionBackEnd.getOpens ~debug ~rawOpens ~package ~env in match CompletionBackEnd.completionsGetTypeEnv2 ~debug ~full ~rawOpens ~opens - ~pos ~scope completions + ~pos completions with | Some (typ, _env) -> let typeString = @@ -154,7 +154,7 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover let opens = CompletionBackEnd.getOpens ~debug ~rawOpens ~package ~env in match CompletionBackEnd.completionsGetTypeEnv2 ~debug ~full ~rawOpens ~opens - ~pos ~scope completions + ~pos completions with | Some (typ, _env) -> let typeString = diff --git a/analysis/src/Scope.ml b/analysis/src/Scope.ml index 5251486f8..54a6de558 100644 --- a/analysis/src/Scope.ml +++ b/analysis/src/Scope.ml @@ -1,13 +1,9 @@ -type item = - | Constructor of string * Location.t - | Field of string * Location.t - | Module of string * Location.t - | Open of string list - | Type of string * Location.t - | Value of string * Location.t * SharedTypes.Completable.contextPath option +type item = SharedTypes.ScopeTypes.item type t = item list +open SharedTypes.ScopeTypes + let itemToString item = let str s = if s = "" then "\"\"" else s in let list l = "[" ^ (l |> List.map str |> String.concat ", ") ^ "]" in @@ -16,7 +12,7 @@ let itemToString item = | Field (s, loc) -> "Field " ^ s ^ " " ^ Loc.toString loc | Open sl -> "Open " ^ list sl | Module (s, loc) -> "Module " ^ s ^ " " ^ Loc.toString loc - | Value (s, loc, _) -> "Value " ^ s ^ " " ^ Loc.toString loc + | Value (s, loc, _, _) -> "Value " ^ s ^ " " ^ Loc.toString loc | Type (s, loc) -> "Type " ^ s ^ " " ^ Loc.toString loc [@@live] @@ -34,14 +30,14 @@ let addValue ~name ~loc ?contextPath x = if showDebug then Printf.printf "adding value '%s' with ctxPath: %s\n" name (SharedTypes.Completable.contextPathToString contextPath)); - Value (name, loc, contextPath) :: x + Value (name, loc, contextPath, x) :: x let addType ~name ~loc x = Type (name, loc) :: x let iterValuesBeforeFirstOpen f x = let rec loop items = match items with - | Value (s, loc, contextPath) :: rest -> - f s loc contextPath; + | Value (s, loc, contextPath, scope) :: rest -> + f s loc contextPath scope; loop rest | Open _ :: _ -> () | _ :: rest -> loop rest @@ -52,8 +48,8 @@ let iterValuesBeforeFirstOpen f x = let iterValuesAfterFirstOpen f x = let rec loop foundOpen items = match items with - | Value (s, loc, contextPath) :: rest -> - if foundOpen then f s loc contextPath; + | Value (s, loc, contextPath, scope) :: rest -> + if foundOpen then f s loc contextPath scope; loop foundOpen rest | Open _ :: rest -> loop true rest | _ :: rest -> loop foundOpen rest diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 7917509f0..efd1fbd82 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -733,6 +733,16 @@ module Completable = struct | ChtmlElement {prefix} -> "ChtmlElement <" ^ prefix end +module ScopeTypes = struct + type item = + | Constructor of string * Location.t + | Field of string * Location.t + | Module of string * Location.t + | Open of string list + | Type of string * Location.t + | Value of string * Location.t * Completable.contextPath option * item list +end + module Completion = struct type kind = | Module of Module.t @@ -746,7 +756,7 @@ module Completion = struct | FileModule of string | Snippet of string | ExtractedType of completionType * [`Value | `Type] - | FollowContextPath of Completable.contextPath + | FollowContextPath of Completable.contextPath * ScopeTypes.item list type t = { name: string; diff --git a/analysis/src/Xform.ml b/analysis/src/Xform.ml index 74de3fe3e..4eb3e667c 100644 --- a/analysis/src/Xform.ml +++ b/analysis/src/Xform.ml @@ -291,7 +291,7 @@ module ExhaustiveSwitch = struct in match CompletionBackEnd.completionsGetCompletionType2 ~debug ~full - ~rawOpens ~opens ~pos ~scope completions + ~rawOpens ~opens ~pos completions with | Some (typ, _env) -> let extractedType = diff --git a/analysis/tests/src/CompletionInferValues.res b/analysis/tests/src/CompletionInferValues.res index 829c26032..f0edbfe8c 100644 --- a/analysis/tests/src/CompletionInferValues.res +++ b/analysis/tests/src/CompletionInferValues.res @@ -162,3 +162,8 @@ let fn3 = (~cb: sameFileRecord => unit) => { // But pipe completion gets the wrong completion path. Should be `ReactDOM.Client.Root.t`, but ends up being `CompletionSupport2.ReactDOM.Client.Root.t`. // let renderer = CompletionSupport2.makeRenderer(~prepare=() => "hello",~render=({support:{root}}) => {root->},()) // ^com + +// Handles reusing the same name already in scope for bindings +let res = 1 +// switch res { | res => res } +// ^hov \ No newline at end of file diff --git a/analysis/tests/src/expected/CompletionInferValues.res.txt b/analysis/tests/src/expected/CompletionInferValues.res.txt index 55b527da6..d9e28dd9c 100644 --- a/analysis/tests/src/expected/CompletionInferValues.res.txt +++ b/analysis/tests/src/expected/CompletionInferValues.res.txt @@ -876,3 +876,18 @@ Path ReactDOM.Client.Root. "documentation": null }] +Hover src/CompletionInferValues.res 167:27 +Nothing at that position. Now trying to use completion. +posCursor:[167:27] posNoWhite:[167:26] Found expr:[167:25->167:28] +Pexp_ident res:[167:25->167:28] +Completable: Cpath Value[res] +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +{"contents": {"kind": "markdown", "value": "```rescript\nint\n```"}} +