Fsharp: StackOverflowException in Lexer

Created on 25 Feb 2020  路  15Comments  路  Source: dotnet/fsharp

Please provide a succinct description of the issue.

Certain files cause the Lexer to overflow the stack.

I have a log file here from FCS 34.0.1 but saw same issue with 34.1.0 and master. The log file includes runtime information about tail call optimizations.

Some analysis on 34.0.1 showed that the following call points could be problematic although we are only seeing exceptions in one place currently.

Searching for 'mono_is_not_supported_tailcall_helper token -> '...
C:\s\gistfile1.txt(1258):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> string (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1262):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> tripleQuoteString (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1266):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> verbatimString (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1270):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> singleLineComment (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1274):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> singleLineComment (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1278):2020-02-25 11:24:27.222 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> singleLineComment (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
C:\s\gistfile1.txt(1282):2020-02-25 11:24:27.223 mono[44849:4190754] mono_is_not_supported_tailcall_helper token -> singleLineComment (!mono_arch_tailcall_supported (cfg, caller_signature, callee_signature, virtual_)):1
7 occurrence(s) have been found.

I noticed that there had been some work done on the Lexer recently so I tried a master build and got the same stack overflow.

Provide the steps required to reproduce the problem:

  1. Build VSMac with latest FCS. I built using master too as I noticed that there had been some changes to the Lexer code recently
  2. Edit a file

If possible attach a zip file with the repro case. This often makes it easier for others to reproduce.
The zip file should ideally represent the situation just before the call/step that is problematic.

Expected behavior

Files are lexed.

Actual behavior

The IDE crashes with no way to prevent this.

Known workarounds

None

Related information

Provide any related information (optional):

  • Operating system
  • .NET Runtime kind (.Mono)
  • Editing Tools (VSMac)
Area-Compiler Area-External Tenet-Performance bug

Most helpful comment

this is my favorite repro

All 15 comments

Do you mean this happens in FCS itself? Or do you mean you compile FCS and then when you use it on your own project or fails with an SOE?

Edit a file

Any file in particular, or does it happen always, everywhere?

Based on the version numbers, I'd assume he's using the FCS nuget packages. 34.0.1 was released 20 days ago and contains up to d7018737c in this repo, and 34.1.0 was released 6 days ago and contains up to 9d69b49b7 in this repo.

Based on the version numbers, I'd assume he's using the FCS nuget packages. 34.0.1 was released 20 days ago and contains up to d7018737c in this repo, and 34.1.0 was released 6 days ago and contains up to 9d69b49b7 in this repo.

I have repro'd in 34.0.1, 34.1.0 and master

Any file in particular, or does it happen always, everywhere?

As far as I can tell, it's just when lexing a certain file. I'm not 100% sure which file at this point though as the crash happens in VSMac's global search function which finds types in all files in the solution.

As far as I can tell, it's just when lexing a certain file.

Which file in particular? Is there anything unusual about it? Long strings? Long comments?

I went through all the changes to src/fsharp/lex.fsl since the start of 2019 and didn't spot anything that looks problematic. At some point we did make a change to the lexer generator but that should only improve the situation here as it means turning more tailcalls into loops. Odd. It does feel like this is a change in mono

  • Can you hand-build FSHarp.COmpiler.Service.dll from this repro and still repro the problem ?

  • If so, it might be worth adding a dump of the stack here in the generated "lex.fs":

and token args skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
   match _fslex_tables.Interpret(356,lexbuf) with

Likewise here:

and ifdefSkip n m args skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
....
and endline cont args skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
...
and string sargs skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
...
and verbatimString sargs skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
...
and tripleQuoteString sargs skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
...
and singleLineComment cargs skip lexbuf =
  /// --> Print out a log or print the stack here <--- 
...

just so we know the problematic path. All I see from the stack in the gist is a long sequence of token ---> token --> token... calls but there must be intermediate frames being eliminated by tailcalls

@tihan and I took a brief look at this yesterday and our thought was also that this is a mono bug. None of the changes to parsing/lexing in a long while should have affected this. But we could probably spent some more time on it in the next sprint since it's blocking VSMac.

Can you hand-build FSHarp.COmpiler.Service.dll from this repro and still repro the problem ?

If so, it might be worth adding a dump of the stack here in the generated "lex.fs":

Yes. Will try this today.

@dsyme I added logging to each point that you mentioned and the log is here https://gist.github.com/nosami/009fe2598dc3a7b0e0e396259a36166e#file-stackoverflow-L7881

Doesn't look like it's that useful. I didn't add stack trace logging because the log would have been gigantic.

I've narrowed it down to one file that seems to crash every time. Not sure if there are others yet.

Note that there's lots of weirdness in here ... ////, /////, // /// etc. I will see if I can narrow it down some more.

//// Copyright (c) Microsoft Corporation.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

////----------------------------------------------------------------------------
//// Open up the compiler as an incremental service for parsing,
//// type checking and intellisense-like environment-reporting.
////--------------------------------------------------------------------------

namespace FSharp.Compiler.SourceCodeServices

open System
//open FSharp.Compiler.Ast
//open System.Collections.Generic
//open FSharp.Compiler
//open FSharp.Compiler.Range

//type internal ShortIdent = string
//type Idents = ShortIdent[]
//type MaybeUnresolvedIdent = { Ident: ShortIdent; Resolved: bool }
//type MaybeUnresolvedIdents = MaybeUnresolvedIdent[]
//type IsAutoOpen = bool

[<AutoOpen>]
module internal Extensions =
    [<RequireQualifiedAccess>]
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module Option =
        let inline attempt (f: unit -> 'T) = try Some (f()) with _ -> None
        let inline orElse v = function Some x -> Some x | None -> v

    [<RequireQualifiedAccess>]
    [<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
    module Array =
        /// Returns a new array with an element replaced with a given value.
        let replace index value (array: _ []) =
            if index >= array.Length then raise (IndexOutOfRangeException "index")
            let res = Array.copy array
            res.[index] <- value
            res

//        /// Optimized arrays equality. ~100x faster than `array1 = array2` on strings.
//        /// ~2x faster for floats
//        /// ~0.8x slower for ints
//        let inline areEqual (xs: 'T []) (ys: 'T []) =
//            match xs, ys with
//            | null, null -> true
//            | [||], [||] -> true
//            | null, _ | _, null -> false
//            | _ when xs.Length <> ys.Length -> false
//            | _ ->
//                let mutable break' = false
//                let mutable i = 0
//                let mutable result = true
//                while i < xs.Length && not break' do
//                    if xs.[i] <> ys.[i] then
//                        break' <- true
//                        result <- false
//                    i <- i + 1
//                result

//        /// Returns all heads of a given array.
//        /// For [|1;2;3|] it returns [|[|1; 2; 3|]; [|1; 2|]; [|1|]|]
//        let heads (array: 'T []) =
//            let res = Array.zeroCreate<'T[]> array.Length
//            for i = array.Length - 1 downto 0 do
//                res.[i] <- array.[0..i]
//            res

//        /// check if subArray is found in the wholeArray starting
//        /// at the provided index
//        let inline isSubArray (subArray: 'T []) (wholeArray:'T []) index =
//            if isNull subArray || isNull wholeArray then false
//            elif subArray.Length = 0 then true
//            elif subArray.Length > wholeArray.Length then false
//            elif subArray.Length = wholeArray.Length then areEqual subArray wholeArray else
//            let rec loop subidx idx =
//                if subidx = subArray.Length then true
//                elif subArray.[subidx] = wholeArray.[idx] then loop (subidx+1) (idx+1)
//                else false
//            loop 0 index

//        /// Returns true if one array has another as its subset from index 0.
//        let startsWith (prefix: _ []) (whole: _ []) =
//            isSubArray prefix whole 0

//        /// Returns true if one array has trailing elements equal to another's.
//        let endsWith (suffix: _ []) (whole: _ []) =
//            isSubArray suffix whole (whole.Length-suffix.Length)

//    type FSharpEntity with
//        member x.TryGetFullName() =
//            try x.TryFullName
//            with _ ->
//                try Some(String.Join(".", x.AccessPath, x.DisplayName))
//                with _ -> None

//        member x.TryGetFullDisplayName() =
//            let fullName = x.TryGetFullName() |> Option.map (fun fullName -> fullName.Split '.')
//            let res =
//                match fullName with
//                | Some fullName ->
//                    match Option.attempt (fun _ -> x.DisplayName) with
//                    | Some shortDisplayName when not (shortDisplayName.Contains ".") ->
//                        Some (fullName |> Array.replace (fullName.Length - 1) shortDisplayName)
//                    | _ -> Some fullName
//                | None -> None
//                |> Option.map (fun fullDisplayName -> String.Join (".", fullDisplayName))
//            //debug "GetFullDisplayName: FullName = %A, Result = %A" fullName res
//            res

//        member x.TryGetFullCompiledName() =
//            let fullName = x.TryGetFullName() |> Option.map (fun fullName -> fullName.Split '.')
//            let res =
//                match fullName with
//                | Some fullName ->
//                    match Option.attempt (fun _ -> x.CompiledName) with
//                    | Some shortCompiledName when not (shortCompiledName.Contains ".") ->
//                        Some (fullName |> Array.replace (fullName.Length - 1) shortCompiledName)
//                    | _ -> Some fullName
//                | None -> None
//                |> Option.map (fun fullDisplayName -> String.Join (".", fullDisplayName))
//            //debug "GetFullCompiledName: FullName = %A, Result = %A" fullName res
//            res

//        member x.PublicNestedEntities =
//            x.NestedEntities |> Seq.filter (fun entity -> entity.Accessibility.IsPublic)

//        member x.TryGetMembersFunctionsAndValues =
//            try x.MembersFunctionsAndValues with _ -> [||] :> _

//    let isOperator (name: string) =
//        name.StartsWith "( " && name.EndsWith " )" && name.Length > 4
//            && name.Substring (2, name.Length - 4)
//               |> String.forall (fun c -> c <> ' ' && not (Char.IsLetter c))

//    type FSharpMemberOrFunctionOrValue with
//        // FullType may raise exceptions (see https://github.com/fsharp/fsharp/issues/307).
//        member x.FullTypeSafe = Option.attempt (fun _ -> x.FullType)

//        member x.TryGetFullDisplayName() =
//            let fullName = Option.attempt (fun _ -> x.FullName.Split '.')
//            match fullName with
//            | Some fullName ->
//                match Option.attempt (fun _ -> x.DisplayName) with
//                | Some shortDisplayName when not (shortDisplayName.Contains ".") ->
//                    Some (fullName |> Array.replace (fullName.Length - 1) shortDisplayName)
//                | _ -> Some fullName
//            | None -> None
//            |> Option.map (fun fullDisplayName -> String.Join (".", fullDisplayName))

//        //member x.TryGetFullCompiledOperatorNameIdents() : Idents option =
//            //// For operator ++ displayName is ( ++ ) compiledName is op_PlusPlus
//            //if isOperator x.DisplayName && x.DisplayName <> x.CompiledName then
//            //    Option.attempt (fun _ -> x.EnclosingEntity)
//            //    |> Option.bind (fun e -> e.TryGetFullName())
//            //    |> Option.map (fun enclosingEntityFullName ->
//            //         Array.append (enclosingEntityFullName.Split '.') [| x.CompiledName |])
//            //else None

//    type FSharpAssemblySignature with
//        member x.TryGetEntities() = try x.Entities :> _ seq with _ -> Seq.empty

//[<AutoOpen>]
//module internal Utils =
//    let isAttribute<'T> (attribute: FSharpAttribute) =
//        // CompiledName throws exception on DataContractAttribute generated by SQLProvider
//        match (try Some attribute.AttributeType.CompiledName with _ -> None) with
//        | Some name when name = typeof<'T>.Name -> true
//        | _ -> false

//    let hasAttribute<'T> (attributes: seq<FSharpAttribute>) =
//        attributes |> Seq.exists isAttribute<'T>

//    let tryGetAttribute<'T> (attributes: seq<FSharpAttribute>) =
//        attributes |> Seq.tryFind isAttribute<'T>

//    let hasModuleSuffixAttribute (entity: FSharpEntity) =
//        entity.Attributes
//        |> tryGetAttribute<CompilationRepresentationAttribute>
//        |> Option.bind (fun a ->
//            try Some a.ConstructorArguments with _ -> None
//            |> Option.bind (fun args -> args |> Seq.tryPick (fun (_, arg) ->
//                let res =
//                    match arg with
//                    | :? int32 as arg when arg = int CompilationRepresentationFlags.ModuleSuffix ->
//                        Some()
//                    | :? CompilationRepresentationFlags as arg when arg = CompilationRepresentationFlags.ModuleSuffix ->
//                        Some()
//                    | _ ->
//                        None
//                res)))
//        |> Option.isSome

//[<RequireQualifiedAccess>]
//type internal LookupType =
//    | Fuzzy
//    | Precise

//[<NoComparison; NoEquality>]
//type internal RawEntity =
//    { /// Full entity name as it's seen in compiled code (raw FSharpEntity.FullName, FSharpValueOrFunction.FullName).
//      FullName: string
//      /// Entity name parts with removed module suffixes (Ns.M1Module.M2Module.M3.entity -> Ns.M1.M2.M3.entity)
//      /// and replaced compiled names with display names (FSharpEntity.DisplayName, FSharpValueOrFucntion.DisplayName).
//      /// Note: *all* parts are cleaned, not the last one.
//      CleanedIdents: Idents
//      Namespace: Idents option
//      IsPublic: bool
//      TopRequireQualifiedAccessParent: Idents option
//      AutoOpenParent: Idents option
//      Kind: LookupType -> EntityKind }
//    override x.ToString() = sprintf "%A" x

//type AssemblyPath = string
//type AssemblyContentType = Public | Full

//type internal Parent =
//    { Namespace: Idents option
//      RequiresQualifiedAccess: Idents option
//      AutoOpen: Idents option
//      WithModuleSuffix: Idents option }
//    static member Empty =
//        { Namespace = None
//          RequiresQualifiedAccess = None
//          AutoOpen = None
//          WithModuleSuffix = None }
//    static member RewriteParentIdents (parentIdents: Idents option) (idents: Idents) =
//        match parentIdents with
//        | Some p when p.Length <= idents.Length ->
//            for i in 0..p.Length - 1 do
//                idents.[i] <- p.[i]
//        | _ -> ()
//        idents
//    member x.FixParentModuleSuffix (idents: Idents) =
//        Parent.RewriteParentIdents x.WithModuleSuffix idents

//    member __.FormatEntityFullName (entity: FSharpEntity) =
//        // remove number of arguments from generic types
//        // e.g. System.Collections.Generic.Dictionary`2 -> System.Collections.Generic.Dictionary
//        // and System.Data.Listeners`1.Func -> System.Data.Listeners.Func
//        let removeGenericParamsCount (idents: Idents) =
//            idents
//            |> Array.map (fun ident ->
//                if ident.Length > 0 && Char.IsDigit ident.[ident.Length - 1] then
//                    let lastBacktickIndex = ident.LastIndexOf '`'
//                    if lastBacktickIndex <> -1 then
//                        ident.Substring(0, lastBacktickIndex)
//                    else ident
//                else ident)

//        let removeModuleSuffix (idents: Idents) =
//            if entity.IsFSharpModule && idents.Length > 0 && hasModuleSuffixAttribute entity then
//                let lastIdent = idents.[idents.Length - 1]
//                if lastIdent.EndsWith "Module" then
//                    idents |> Array.replace (idents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 6))
//                else idents
//            else idents

//        entity.TryGetFullName()
//        |> Option.bind (fun fullName ->
//            entity.TryGetFullDisplayName()
//            |> Option.map (fun fullDisplayName ->
//                fullName,
//                fullDisplayName.Split '.'
//                |> removeGenericParamsCount
//                |> removeModuleSuffix))

//module internal TypedAstPatterns =
//    let (|TypeWithDefinition|_|) (ty: FSharpType) =
//        if ty.HasTypeDefinition then Some ty.TypeDefinition
//        else None

//    let (|Attribute|_|) (entity: FSharpEntity) =
//        let isAttribute (entity: FSharpEntity) =
//            try entity.IsAttributeType with _ -> false
//        if isAttribute entity then Some() else None

//    let (|FSharpModule|_|) (entity: FSharpEntity) = if entity.IsFSharpModule then Some() else None

//type internal AssemblyContentCacheEntry =
//    { FileWriteTime: DateTime
//      ContentType: AssemblyContentType
//      Entities: RawEntity list }

//[<NoComparison; NoEquality>]
//type internal IAssemblyContentCache =
//    abstract TryGet: AssemblyPath -> AssemblyContentCacheEntry option
//    abstract Set: AssemblyPath -> AssemblyContentCacheEntry -> unit

//module internal AssemblyContentProvider =
//    open System.IO

//    let private createEntity ns (parent: Parent) (entity: FSharpEntity) =
//        parent.FormatEntityFullName entity
//        |> Option.map (fun (fullName, cleanIdents) ->
//            { FullName = fullName
//              CleanedIdents = cleanIdents
//              Namespace = ns
//              IsPublic = entity.Accessibility.IsPublic
//              TopRequireQualifiedAccessParent = parent.RequiresQualifiedAccess |> Option.map parent.FixParentModuleSuffix
//              AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix
//              Kind = fun lookupType ->
//                match entity, lookupType with
//                | TypedAstPatterns.FSharpModule, _ ->
//                    EntityKind.Module
//                        { IsAutoOpen = hasAttribute<AutoOpenAttribute> entity.Attributes
//                          HasModuleSuffix = hasModuleSuffixAttribute entity }
//                | _, LookupType.Fuzzy ->
//                    EntityKind.Type
//                | _, LookupType.Precise ->
//                    match entity with
//                    | TypedAstPatterns.Attribute -> EntityKind.Attribute
//                    | _ -> EntityKind.Type
//            })

//    let private traverseMemberFunctionAndValues ns (parent: Parent) (membersFunctionsAndValues: seq<FSharpMemberOrFunctionOrValue>) =
//        membersFunctionsAndValues
//        |> Seq.collect (fun func ->
//            let processIdents fullName idents =
//                { FullName = fullName
//                  CleanedIdents = parent.FixParentModuleSuffix idents
//                  Namespace = ns
//                  IsPublic = func.Accessibility.IsPublic
//                  TopRequireQualifiedAccessParent =
//                        parent.RequiresQualifiedAccess |> Option.map parent.FixParentModuleSuffix
//                  AutoOpenParent = parent.AutoOpen |> Option.map parent.FixParentModuleSuffix
//                  Kind = fun _ -> EntityKind.FunctionOrValue func.IsActivePattern }

//            [ yield! func.TryGetFullDisplayName()
//                     |> Option.map (fun fullDisplayName -> processIdents func.FullName (fullDisplayName.Split '.'))
//                     |> Option.toList
//              (* for
//                 [<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
//                 module M =
//                     let (++) x y = ()
//                 open M
//                 let _ = 1 ++ 2
//  we should return additional RawEntity { FullName = MModule.op_PlusPlus; CleanedIdents = [|"M"; "op_PlusPlus"|] ... }
//              *)
//              yield! func.TryGetFullCompiledOperatorNameIdents()
//                     |> Option.map (fun fullCompiledIdents ->
//                          processIdents (fullCompiledIdents |> String.concat ".") fullCompiledIdents)
//                     |> Option.toList ])

//    let rec private traverseEntity contentType (parent: Parent) (entity: FSharpEntity) =

//        seq { if not entity.IsProvided then
//                match contentType, entity.Accessibility.IsPublic with
//                | Full, _ | Public, true ->
//                    let ns = entity.Namespace |> Option.map (fun x -> x.Split '.') |> Option.orElse parent.Namespace
//                    let currentEntity = createEntity ns parent entity

//                    match currentEntity with
//                    | Some x -> yield x
//                    | None -> ()

//                    let currentParent =
//                        { RequiresQualifiedAccess =
//                            parent.RequiresQualifiedAccess
//                            |> Option.orElse (
//                                if entity.IsFSharp && hasAttribute<RequireQualifiedAccessAttribute> entity.Attributes then
//                                    parent.FormatEntityFullName entity |> Option.map snd
//                                else None)
//                          AutoOpen =
//                            let isAutoOpen = entity.IsFSharpModule && hasAttribute<AutoOpenAttribute> entity.Attributes
//                            match isAutoOpen, parent.AutoOpen with
//                            // if parent is also AutoOpen, then keep the parent
//                            | true, Some parent -> Some parent
//                            // if parent is not AutoOpen, but current entity is, peek the latter as a new AutoOpen module
//                            | true, None -> parent.FormatEntityFullName entity |> Option.map snd
//                            // if current entity is not AutoOpen, we discard whatever parent was
//                            | false, _ -> None

//                          WithModuleSuffix =
//                            if entity.IsFSharpModule && hasModuleSuffixAttribute entity then
//                                currentEntity |> Option.map (fun e -> e.CleanedIdents)
//                            else parent.WithModuleSuffix
//                          Namespace = ns }

//                    if entity.IsFSharpModule then
//                        match entity.TryGetMembersFunctionsAndValues with
//                        | xs when xs.Count > 0 ->
//                            yield! traverseMemberFunctionAndValues ns currentParent xs
//                        | _ -> ()

//                    for e in (try entity.NestedEntities :> _ seq with _ -> Seq.empty) do
//                        yield! traverseEntity contentType currentParent e
//                | _ -> () }

//    let getAssemblySignatureContent contentType (signature: FSharpAssemblySignature) =
//            signature.TryGetEntities()
//            |> Seq.collect (traverseEntity contentType Parent.Empty)
//            |> Seq.distinctBy (fun {FullName = fullName; CleanedIdents = cleanIdents} -> (fullName, cleanIdents))

//    let private getAssemblySignaturesContent contentType (assemblies: FSharpAssembly list) =
//        assemblies
//        |> Seq.collect (fun asm -> getAssemblySignatureContent contentType asm.Contents)
//        |> Seq.toList

//    let getAssemblyContent (withCache: (IAssemblyContentCache -> _) -> _)
//                           contentType (fileName: string option) (assemblies: FSharpAssembly list) =
//        match assemblies |> List.filter (fun x -> not x.IsProviderGenerated), fileName with
//        | [], _ -> []
//        | assemblies, Some fileName ->
//            let fileWriteTime = FileInfo(fileName).LastWriteTime
//            withCache <| fun cache ->
//                match contentType, cache.TryGet fileName with
//                | _, Some entry
//                | Public, Some entry when entry.FileWriteTime = fileWriteTime -> entry.Entities
//                | _ ->
//                    let entities = getAssemblySignaturesContent contentType assemblies
//                    cache.Set fileName { FileWriteTime = fileWriteTime; ContentType = contentType; Entities = entities }
//                    entities
//        | assemblies, None ->
//            getAssemblySignaturesContent contentType assemblies
//        |> List.filter (fun entity ->
//            match contentType, entity.IsPublic with
//            | Full, _ | Public, true -> true
//            | _ -> false)

//type internal EntityCache() =
//    let dic = Dictionary<AssemblyPath, AssemblyContentCacheEntry>()
//    interface IAssemblyContentCache with
//        member __.TryGet assembly =
//            match dic.TryGetValue assembly with
//            | true, entry -> Some entry
//            | _ -> None
//        member __.Set assembly entry = dic.[assembly] <- entry

//    member __.Clear() = dic.Clear()
//    member x.Locking f = lock dic <| fun _ -> f (x :> IAssemblyContentCache)

//type internal LongIdent = string

//type internal Entity =
//    { FullRelativeName: LongIdent
//      Qualifier: LongIdent
//      Namespace: LongIdent option
//      Name: LongIdent }
//    override x.ToString() = sprintf "%A" x

//[<CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
//module internal Entity =
//    let getRelativeNamespace (targetNs: Idents) (sourceNs: Idents) =
//        let rec loop index =
//            if index > targetNs.Length - 1 then sourceNs.[index..]
//            // target namespace is not a full parent of source namespace, keep the source ns as is
//            elif index > sourceNs.Length - 1 then sourceNs
//            elif targetNs.[index] = sourceNs.[index] then loop (index + 1)
//            else sourceNs.[index..]
//        if sourceNs.Length = 0 || targetNs.Length = 0 then sourceNs
//        else loop 0

//    let cutAutoOpenModules (autoOpenParent: Idents option) (candidateNs: Idents) =
//        let nsCount =
//            match autoOpenParent with
//            | Some parent when parent.Length > 0 ->
//                min (parent.Length - 1) candidateNs.Length
//            | _ -> candidateNs.Length
//        candidateNs.[0..nsCount - 1]

//    let tryCreate (targetNamespace: Idents option, targetScope: Idents, partiallyQualifiedName: MaybeUnresolvedIdents,
//                   requiresQualifiedAccessParent: Idents option, autoOpenParent: Idents option, candidateNamespace: Idents option, candidate: Idents) =
//        match candidate with
//        | [||] -> [||]
//        | _ ->
//            partiallyQualifiedName
//            |> Array.heads
//            // long ident must contain an unresolved part, otherwise we show false positive suggestions like
//            // "open System" for `let _ = System.DateTime.Naaaw`. Here only "Naaw" is unresolved.
//            |> Array.filter (fun x -> x |> Array.exists (fun x -> not x.Resolved))
//            |> Array.choose (fun parts ->
//                let parts = parts |> Array.map (fun x -> x.Ident)
//                if not (candidate |> Array.endsWith parts) then None
//                else
//                  let identCount = parts.Length
//                  let fullOpenableNs, restIdents =
//                      let openableNsCount =
//                          match requiresQualifiedAccessParent with
//                          | Some parent -> min parent.Length candidate.Length
//                          | None -> candidate.Length
//                      candidate.[0..openableNsCount - 2], candidate.[openableNsCount - 1..]

//                  let openableNs = cutAutoOpenModules autoOpenParent fullOpenableNs

//                  let getRelativeNs ns =
//                      match targetNamespace, candidateNamespace with
//                      | Some targetNs, Some candidateNs when candidateNs = targetNs ->
//                          getRelativeNamespace targetScope ns
//                      | None, _ -> getRelativeNamespace targetScope ns
//                      | _ -> ns

//                  let relativeNs = getRelativeNs openableNs

//                  match relativeNs, restIdents with
//                  | [||], [||] -> None
//                  | [||], [|_|] -> None
//                  | _ ->
//                      let fullRelativeName = Array.append (getRelativeNs fullOpenableNs) restIdents
//                      let ns =
//                          match relativeNs with
//                          | [||] -> None
//                          | _ when identCount > 1 && relativeNs.Length >= identCount ->
//                              Some (relativeNs.[0..relativeNs.Length - identCount] |> String.concat ".")
//                          | _ -> Some (relativeNs |> String.concat ".")
//                      let qualifier =
//                          if fullRelativeName.Length > 1 && fullRelativeName.Length >= identCount then
//                              fullRelativeName.[0..fullRelativeName.Length - identCount]
//                          else fullRelativeName
//                      Some
//                          { FullRelativeName = String.concat "." fullRelativeName //.[0..fullRelativeName.Length - identCount - 1]
//                            Qualifier = String.concat "." qualifier
//                            Namespace = ns
//                            Name = match restIdents with [|_|] -> "" | _ -> String.concat "." restIdents })

//type internal ScopeKind =
//    | Namespace
//    | TopModule
//    | NestedModule
//    | OpenDeclaration
//    | HashDirective
//    override x.ToString() = sprintf "%A" x

//[<Measure>] type internal FCS

//type internal Point<[<Measure>]'t> = { Line : int; Column : int }

//[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
//module internal Point =
//    let make line column : Point<'t> = { Line = line; Column = column }

//type internal InsertContext =
//    { ScopeKind: ScopeKind
//      Pos: Point<FCS> }

//module internal ParsedInput =
    //open FSharp.Compiler
    //open FSharp.Compiler.Ast

    //type private EndLine = int

    ///// An recursive pattern that collect all sequential expressions to avoid StackOverflowException
    //let rec (|Sequentials|_|) = function
    //    | SynExpr.Sequential(_, _, e, Sequentials es, _) ->
    //        Some(e::es)
    //    | SynExpr.Sequential(_, _, e1, e2, _) ->
    //        Some [e1; e2]
    //    | _ -> None

    //let (|ConstructorPats|) = function
    //    | SynConstructorArgs.Pats ps -> ps
    //    | SynConstructorArgs.NamePatPairs(xs, _) -> List.map snd xs

    ///// Returns all `Ident`s and `LongIdent`s found in an untyped AST.
    //let internal getLongIdents (input: ParsedInput option) : IDictionary<Range.pos, LongIdent> =
    //    let identsByEndPos = Dictionary<Range.pos, LongIdent>()

    //    let addLongIdent (longIdent: LongIdent) =
    //        for ident in longIdent do
    //            identsByEndPos.[ident.idRange.End] <- longIdent

    //    let addLongIdentWithDots (LongIdentWithDots (longIdent, lids) as value) =
    //        match longIdent with
    //        | [] -> ()
    //        | [_] as idents -> identsByEndPos.[value.Range.End] <- idents
    //        | idents ->
    //            for dotRange in lids do
    //                identsByEndPos.[Range.mkPos dotRange.EndLine (dotRange.EndColumn - 1)] <- idents
    //            identsByEndPos.[value.Range.End] <- idents

    //    let addIdent (ident: Ident) =
    //        identsByEndPos.[ident.idRange.End] <- [ident]

    //    let rec walkImplFileInput (ParsedImplFileInput(_, _, _, _, _, moduleOrNamespaceList, _)) =
    //        List.iter walkSynModuleOrNamespace moduleOrNamespaceList

    //    and walkSynModuleOrNamespace (SynModuleOrNamespace(_, _, _, decls, _, attrs, _, _)) =
    //        List.iter walkAttribute attrs
    //        List.iter walkSynModuleDecl decls

    //    and walkAttribute (attr: SynAttribute) =
    //        addLongIdentWithDots attr.TypeName
    //        walkExpr attr.ArgExpr

    //    and walkTyparDecl (SynTyparDecl.TyparDecl (attrs, typar)) =
    //        List.iter walkAttribute attrs
    //        walkTypar typar

    //    and walkTypeConstraint = function
    //        | SynTypeConstraint.WhereTyparIsValueType (t, _)
    //        | SynTypeConstraint.WhereTyparIsReferenceType (t, _)
    //        | SynTypeConstraint.WhereTyparIsUnmanaged (t, _)
    //        | SynTypeConstraint.WhereTyparSupportsNull (t, _)
    //        | SynTypeConstraint.WhereTyparIsComparable (t, _)
    //        | SynTypeConstraint.WhereTyparIsEquatable (t, _) -> walkTypar t
    //        | SynTypeConstraint.WhereTyparDefaultsToType (t, ty, _)
    //        | SynTypeConstraint.WhereTyparSubtypeOfType (t, ty, _) -> walkTypar t; walkType ty
    //        | SynTypeConstraint.WhereTyparIsEnum (t, ts, _)
    //        | SynTypeConstraint.WhereTyparIsDelegate (t, ts, _) -> walkTypar t; List.iter walkType ts
    //        | SynTypeConstraint.WhereTyparSupportsMember (ts, sign, _) -> List.iter walkType ts; walkMemberSig sign

    //    and walkPat = function
    //        | SynPat.Tuple (_, pats, _)
    //        | SynPat.ArrayOrList (_, pats, _)
    //        | SynPat.Ands (pats, _) -> List.iter walkPat pats
    //        | SynPat.Named (pat, ident, _, _, _) ->
    //            walkPat pat
    //            addIdent ident
    //        | SynPat.Typed (pat, t, _) ->
    //            walkPat pat
    //            walkType t
    //        | SynPat.Attrib (pat, attrs, _) ->
    //            walkPat pat
    //            List.iter walkAttribute attrs
    //        | SynPat.Or (pat1, pat2, _) -> List.iter walkPat [pat1; pat2]
    //        | SynPat.LongIdent (ident, _, typars, ConstructorPats pats, _, _) ->
    //            addLongIdentWithDots ident
    //            typars
    //            |> Option.iter (fun (SynValTyparDecls (typars, _, constraints)) ->
    //                 List.iter walkTyparDecl typars
    //                 List.iter walkTypeConstraint constraints)
    //            List.iter walkPat pats
    //        | SynPat.Paren (pat, _) -> walkPat pat
    //        | SynPat.IsInst (t, _) -> walkType t
    //        | SynPat.QuoteExpr(e, _) -> walkExpr e
    //        | _ -> ()

    //    and walkTypar (Typar (_, _, _)) = ()

    //    and walkBinding (SynBinding.Binding (_, _, _, _, attrs, _, _, pat, returnInfo, e, _, _)) =
    //        List.iter walkAttribute attrs
    //        walkPat pat
    //        walkExpr e
    //        returnInfo |> Option.iter (fun (SynBindingReturnInfo (t, _, _)) -> walkType t)

    //    and walkInterfaceImpl (InterfaceImpl(_, bindings, _)) = List.iter walkBinding bindings

    //    and walkIndexerArg = function
    //        | SynIndexerArg.One e -> walkExpr e
    //        | SynIndexerArg.Two (e1, e2) -> List.iter walkExpr [e1; e2]

    //    and walkType = function
    //        | SynType.Array (_, t, _)
    //        | SynType.HashConstraint (t, _)
    //        | SynType.MeasurePower (t, _, _) -> walkType t
    //        | SynType.Fun (t1, t2, _)
    //        | SynType.MeasureDivide (t1, t2, _) -> walkType t1; walkType t2
    //        | SynType.LongIdent ident -> addLongIdentWithDots ident
    //        | SynType.App (ty, _, types, _, _, _, _) -> walkType ty; List.iter walkType types
    //        | SynType.LongIdentApp (_, _, _, types, _, _, _) -> List.iter walkType types
    //        | SynType.Tuple (_, ts, _) -> ts |> List.iter (fun (_, t) -> walkType t)
    //        | SynType.WithGlobalConstraints (t, typeConstraints, _) ->
    //            walkType t; List.iter walkTypeConstraint typeConstraints
    //        | _ -> ()

    //    and walkClause (Clause (pat, e1, e2, _, _)) =
    //        walkPat pat
    //        walkExpr e2
    //        e1 |> Option.iter walkExpr

    //    and walkSimplePats = function
    //        | SynSimplePats.SimplePats (pats, _) -> List.iter walkSimplePat pats
    //        | SynSimplePats.Typed (pats, ty, _) ->
    //            walkSimplePats pats
    //            walkType ty

    //    and walkExpr = function
    //        | SynExpr.Paren (e, _, _, _)
    //        | SynExpr.Quote (_, _, e, _, _)
    //        | SynExpr.Typed (e, _, _)
    //        | SynExpr.InferredUpcast (e, _)
    //        | SynExpr.InferredDowncast (e, _)
    //        | SynExpr.AddressOf (_, e, _, _)
    //        | SynExpr.DoBang (e, _)
    //        | SynExpr.YieldOrReturn (_, e, _)
    //        | SynExpr.ArrayOrListOfSeqExpr (_, e, _)
    //        | SynExpr.CompExpr (_, _, e, _)
    //        | SynExpr.Do (e, _)
    //        | SynExpr.Assert (e, _)
    //        | SynExpr.Lazy (e, _)
    //        | SynExpr.YieldOrReturnFrom (_, e, _) -> walkExpr e
    //        | SynExpr.Lambda (_, _, pats, e, _) ->
    //            walkSimplePats pats
    //            walkExpr e
    //        | SynExpr.New (_, t, e, _)
    //        | SynExpr.TypeTest (e, t, _)
    //        | SynExpr.Upcast (e, t, _)
    //        | SynExpr.Downcast (e, t, _) -> walkExpr e; walkType t
    //        | SynExpr.Tuple (_, es, _, _)
    //        | Sequentials es
    //        | SynExpr.ArrayOrList (_, es, _) -> List.iter walkExpr es
    //        | SynExpr.App (_, _, e1, e2, _)
    //        | SynExpr.TryFinally (e1, e2, _, _, _)
    //        | SynExpr.While (_, e1, e2, _) -> List.iter walkExpr [e1; e2]
    //        | SynExpr.Record (_, _, fields, _) ->
    //            fields |> List.iter (fun ((ident, _), e, _) ->
    //                        addLongIdentWithDots ident
    //                        e |> Option.iter walkExpr)
    //        | SynExpr.Ident ident -> addIdent ident
    //        | SynExpr.ObjExpr(ty, argOpt, bindings, ifaces, _, _) ->
    //            argOpt |> Option.iter (fun (e, ident) ->
    //                walkExpr e
    //                ident |> Option.iter addIdent)
    //            walkType ty
    //            List.iter walkBinding bindings
    //            List.iter walkInterfaceImpl ifaces
    //        | SynExpr.LongIdent (_, ident, _, _) -> addLongIdentWithDots ident
    //        | SynExpr.For (_, ident, e1, _, e2, e3, _) ->
    //            addIdent ident
    //            List.iter walkExpr [e1; e2; e3]
    //        | SynExpr.ForEach (_, _, _, pat, e1, e2, _) ->
    //            walkPat pat
    //            List.iter walkExpr [e1; e2]
    //        | SynExpr.MatchLambda (_, _, synMatchClauseList, _, _) ->
    //            List.iter walkClause synMatchClauseList
    //        | SynExpr.Match (_, e, synMatchClauseList, _) ->
    //            walkExpr e
    //            List.iter walkClause synMatchClauseList
    //        | SynExpr.TypeApp (e, _, tys, _, _, _, _) ->
    //            List.iter walkType tys; walkExpr e
    //        | SynExpr.LetOrUse (_, _, bindings, e, _) ->
    //            List.iter walkBinding bindings; walkExpr e
    //        | SynExpr.TryWith (e, _, clauses, _, _, _, _) ->
    //            List.iter walkClause clauses;  walkExpr e
    //        | SynExpr.IfThenElse (e1, e2, e3, _, _, _, _) ->
    //            List.iter walkExpr [e1; e2]
    //            e3 |> Option.iter walkExpr
    //        | SynExpr.LongIdentSet (ident, e, _)
    //        | SynExpr.DotGet (e, _, ident, _) ->
    //            addLongIdentWithDots ident
    //            walkExpr e
    //        | SynExpr.DotSet (e1, idents, e2, _) ->
    //            walkExpr e1
    //            addLongIdentWithDots idents
    //            walkExpr e2
    //        | SynExpr.DotIndexedGet (e, args, _, _) ->
    //            walkExpr e
    //            List.iter walkIndexerArg args
    //        | SynExpr.DotIndexedSet (e1, args, e2, _, _, _) ->
    //            walkExpr e1
    //            List.iter walkIndexerArg args
    //            walkExpr e2
    //        | SynExpr.NamedIndexedPropertySet (ident, e1, e2, _) ->
    //            addLongIdentWithDots ident
    //            List.iter walkExpr [e1; e2]
    //        | SynExpr.DotNamedIndexedPropertySet (e1, ident, e2, e3, _) ->
    //            addLongIdentWithDots ident
    //            List.iter walkExpr [e1; e2; e3]
    //        | SynExpr.JoinIn (e1, _, e2, _) -> List.iter walkExpr [e1; e2]
    //        | SynExpr.LetOrUseBang (_, _, _, pat, e1, e2, _) ->
    //            walkPat pat
    //            List.iter walkExpr [e1; e2]
    //        | SynExpr.TraitCall (ts, sign, e, _) ->
    //            List.iter walkTypar ts
    //            walkMemberSig sign
    //            walkExpr e
    //        | SynExpr.Const (SynConst.Measure(_, m), _) -> walkMeasure m
    //        | _ -> ()

    //    and walkMeasure = function
    //        | SynMeasure.Product (m1, m2, _)
    //        | SynMeasure.Divide (m1, m2, _) -> walkMeasure m1; walkMeasure m2
    //        | SynMeasure.Named (longIdent, _) -> addLongIdent longIdent
    //        | SynMeasure.Seq (ms, _) -> List.iter walkMeasure ms
    //        | SynMeasure.Power (m, _, _) -> walkMeasure m
    //        | SynMeasure.Var (ty, _) -> walkTypar ty
    //        | SynMeasure.One
    //        | SynMeasure.Anon _ -> ()

    //    and walkSimplePat = function
    //        | SynSimplePat.Attrib (pat, attrs, _) ->
    //            walkSimplePat pat
    //            List.iter walkAttribute attrs
    //        | SynSimplePat.Typed(pat, t, _) ->
    //            walkSimplePat pat
    //            walkType t
    //        | _ -> ()

    //    and walkField (SynField.Field(attrs, _, _, t, _, _, _, _)) =
    //        List.iter walkAttribute attrs
    //        walkType t

    //    and walkValSig (SynValSig.ValSpfn(attrs, _, _, t, SynValInfo(argInfos, argInfo), _, _, _, _, _, _)) =
    //        List.iter walkAttribute attrs
    //        walkType t
    //        argInfo :: (argInfos |> List.concat)
    //        |> List.map (fun (SynArgInfo(attrs, _, _)) -> attrs)
    //        |> List.concat
    //        |> List.iter walkAttribute

    //    and walkMemberSig = function
    //        | SynMemberSig.Inherit (t, _)
    //        | SynMemberSig.Interface(t, _) -> walkType t
    //        | SynMemberSig.Member(vs, _, _) -> walkValSig vs
    //        | SynMemberSig.ValField(f, _) -> walkField f
    //        | SynMemberSig.NestedType(SynTypeDefnSig.TypeDefnSig (info, repr, memberSigs, _), _) ->
    //            let isTypeExtensionOrAlias =
    //                match repr with
    //                | SynTypeDefnSigRepr.Simple(SynTypeDefnSimpleRepr.TypeAbbrev _, _)
    //                | SynTypeDefnSigRepr.ObjectModel(SynTypeDefnKind.TyconAbbrev, _, _)
    //                | SynTypeDefnSigRepr.ObjectModel(SynTypeDefnKind.TyconAugmentation, _, _) -> true
    //                | _ -> false
    //            walkComponentInfo isTypeExtensionOrAlias info
    //            walkTypeDefnSigRepr repr
    //            List.iter walkMemberSig memberSigs

    //    and walkMember = function
    //        | SynMemberDefn.AbstractSlot (valSig, _, _) -> walkValSig valSig
    //        | SynMemberDefn.Member (binding, _) -> walkBinding binding
    //        | SynMemberDefn.ImplicitCtor (_, attrs, pats, _, _) ->
    //            List.iter walkAttribute attrs
    //            List.iter walkSimplePat pats
    //        | SynMemberDefn.ImplicitInherit (t, e, _, _) -> walkType t; walkExpr e
    //        | SynMemberDefn.LetBindings (bindings, _, _, _) -> List.iter walkBinding bindings
    //        | SynMemberDefn.Interface (t, members, _) ->
    //            walkType t
    //            members |> Option.iter (List.iter walkMember)
    //        | SynMemberDefn.Inherit (t, _, _) -> walkType t
    //        | SynMemberDefn.ValField (field, _) -> walkField field
    //        | SynMemberDefn.NestedType (tdef, _, _) -> walkTypeDefn tdef
    //        | SynMemberDefn.AutoProperty (attrs, _, _, t, _, _, _, _, e, _, _) ->
    //            List.iter walkAttribute attrs
    //            Option.iter walkType t
    //            walkExpr e
    //        | _ -> ()

    //    and walkEnumCase (EnumCase(attrs, _, _, _, _)) = List.iter walkAttribute attrs

    //    and walkUnionCaseType = function
    //        | SynUnionCaseType.UnionCaseFields fields -> List.iter walkField fields
    //        | SynUnionCaseType.UnionCaseFullType (t, _) -> walkType t

    //    and walkUnionCase (SynUnionCase.UnionCase (attrs, _, t, _, _, _)) =
    //        List.iter walkAttribute attrs
    //        walkUnionCaseType t

    //    and walkTypeDefnSimple = function
    //        | SynTypeDefnSimpleRepr.Enum (cases, _) -> List.iter walkEnumCase cases
    //        | SynTypeDefnSimpleRepr.Union (_, cases, _) -> List.iter walkUnionCase cases
    //        | SynTypeDefnSimpleRepr.Record (_, fields, _) -> List.iter walkField fields
    //        | SynTypeDefnSimpleRepr.TypeAbbrev (_, t, _) -> walkType t
    //        | _ -> ()

    //    and walkComponentInfo isTypeExtensionOrAlias (ComponentInfo(attrs, typars, constraints, longIdent, _, _, _, _)) =
    //        List.iter walkAttribute attrs
    //        List.iter walkTyparDecl typars
    //        List.iter walkTypeConstraint constraints
    //        if isTypeExtensionOrAlias then
    //            addLongIdent longIdent

    //    and walkTypeDefnRepr = function
    //        | SynTypeDefnRepr.ObjectModel (_, defns, _) -> List.iter walkMember defns
    //        | SynTypeDefnRepr.Simple(defn, _) -> walkTypeDefnSimple defn
    //        | SynTypeDefnRepr.Exception _ -> ()

    //    and walkTypeDefnSigRepr = function
    //        | SynTypeDefnSigRepr.ObjectModel (_, defns, _) -> List.iter walkMemberSig defns
    //        | SynTypeDefnSigRepr.Simple(defn, _) -> walkTypeDefnSimple defn
    //        | SynTypeDefnSigRepr.Exception _ -> ()

    //    and walkTypeDefn (TypeDefn (info, repr, members, _)) =
    //        let isTypeExtensionOrAlias =
    //            match repr with
    //            | SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.TyconAugmentation, _, _)
    //            | SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.TyconAbbrev, _, _)
    //            | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.TypeAbbrev _, _) -> true
    //            | _ -> false
    //        walkComponentInfo isTypeExtensionOrAlias info
    //        walkTypeDefnRepr repr
    //        List.iter walkMember members

    //    and walkSynModuleDecl (decl: SynModuleDecl) =
    //        match decl with
    //        | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace fragment
    //        | SynModuleDecl.NestedModule (info, _, modules, _, _) ->
    //            walkComponentInfo false info
    //            List.iter walkSynModuleDecl modules
    //        | SynModuleDecl.Let (_, bindings, _) -> List.iter walkBinding bindings
    //        | SynModuleDecl.DoExpr (_, expr, _) -> walkExpr expr
    //        | SynModuleDecl.Types (types, _) -> List.iter walkTypeDefn types
    //        | SynModuleDecl.Attributes (attrs, _) -> List.iter walkAttribute attrs
    //        | _ -> ()

    //    match input with
    //    | Some (ParsedInput.ImplFile input) ->
    //         walkImplFileInput input
    //    | _ -> ()
    //    //debug "%A" idents
    //    upcast identsByEndPos

    //let getLongIdentAt ast pos =
    //    let idents = getLongIdents (Some ast)

    //    match idents.TryGetValue pos with
    //    | true, idents -> Some idents
    //    | _ -> None

    //type Col = int

    //type Scope =
    //    { Idents: Idents
    //      Kind: ScopeKind }

    //let tryFindInsertionContext (currentLine: int) (ast: ParsedInput) =
        //let result: (Scope * Point<FCS>) option ref = ref None
        //let ns: string[] option ref = ref None
        //let modules = ResizeArray<Module>

        //let inline longIdentToIdents ident = ident |> Seq.map (fun x -> string x) |> Seq.toArray

        //let addModule (longIdent: LongIdent, range: range) =
        //    modules.Add
        //        { Idents = longIdent |> List.map string |> List.toArray
        //          Range = range }

        //let doRange kind (scope: LongIdent) line col =
        //    if line <= currentLine then
        //        match !result with
        //        | None ->
        //            result := Some ({ Idents = longIdentToIdents scope; Kind = kind }, Point.make line col)
        //        | Some (oldScope, oldPos) ->
        //            match kind, oldScope.Kind with
        //            | (Namespace | NestedModule | TopModule), OpenDeclaration
        //            | _ when oldPos.Line <= line ->
        //                result :=
        //                    Some ({ Idents =
        //                                match scope with
        //                                | [] -> oldScope.Idents
        //                                | _ -> longIdentToIdents scope
        //                            Kind = kind },
        //                          Point.make line col)
        //            | _ -> ()

        //let getMinColumn (decls: SynModuleDecls) =
        //    match decls with
        //    | [] -> None
        //    | firstDecl :: _ ->
        //        match firstDecl with
        //        | SynModuleDecl.NestedModule (_, _, _, _, r)
        //        | SynModuleDecl.Let (_, _, r)
        //        | SynModuleDecl.DoExpr (_, _, r)
        //        | SynModuleDecl.Types (_, r)
        //        | SynModuleDecl.Exception (_, r)
        //        | SynModuleDecl.Open (_, r)
        //        | SynModuleDecl.HashDirective (_, r) -> Some r
        //        | _ -> None
        //        |> Option.map (fun r -> r.StartColumn)


        //let rec walkImplFileInput (ParsedImplFileInput(_, _, _, _, _, moduleOrNamespaceList, _)) =
        //    List.iter (walkSynModuleOrNamespace []) moduleOrNamespaceList

        //and walkSynModuleOrNamespace (parent: LongIdent) (SynModuleOrNamespace(ident, _, kind, decls, _, _, _, range)) =
        //    if range.EndLine >= currentLine then
        //        let isModule = kind.IsModule
        //        match isModule, parent, ident with
        //        | false, _, _ -> ns := Some (longIdentToIdents ident)
        //        // top level module with "inlined" namespace like Ns1.Ns2.TopModule
        //        | true, [], _f :: _s :: _ ->
        //            let ident = longIdentToIdents ident
        //            ns := Some (ident.[0..ident.Length - 2])
        //        | _ -> ()

        //        let fullIdent = parent @ ident

        //        let startLine =
        //            if isModule then range.StartLine
        //            else range.StartLine - 1

        //        let scopeKind =
        //            match isModule, parent with
        //            | true, [] -> TopModule
        //            | true, _ -> NestedModule
        //            | _ -> Namespace

        //        doRange scopeKind fullIdent startLine range.StartColumn
        //        addModule (fullIdent, range)
        //        List.iter (walkSynModuleDecl fullIdent) decls

        //and walkSynModuleDecl (parent: LongIdent) (decl: SynModuleDecl) =
        //    match decl with
        //    | SynModuleDecl.NamespaceFragment fragment -> walkSynModuleOrNamespace parent fragment
        //    | SynModuleDecl.NestedModule(ComponentInfo(_, _, _, ident, _, _, _, _), _, decls, _, range) ->
        //        let fullIdent = parent @ ident
        //        addModule fullIdent range.EndLine range.StartColumn
        //        if range.EndLine >= currentLine then
        //            let moduleBodyIdentation = getMinColumn decls |> Option.defaultValue (range.StartColumn + 4)
        //            doRange NestedModule fullIdent range.StartLine moduleBodyIdentation
        //            List.iter (walkSynModuleDecl fullIdent) decls
        //    | SynModuleDecl.Open (_, range) -> doRange OpenDeclaration [] range.EndLine (range.StartColumn - 5)
        //    | SynModuleDecl.HashDirective (_, range) -> doRange HashDirective [] range.EndLine range.StartColumn
        //    | _ -> ()

        //match ast with
        //| ParsedInput.SigFile _ -> ()
        //| ParsedInput.ImplFile input -> walkImplFileInput input

        //let res =
        //    !result
        //    |> Option.map (fun (scope, pos) ->
        //        let ns = !ns |> Option.map longIdentToIdents
        //        scope, ns, { pos with Line = pos.Line + 1 })

        //let modules =
        //    modules
        //    |> Seq.filter (fun (_, endLine, _) -> endLine < currentLine)
        //    |> Seq.sortBy (fun (m, _, _) -> -m.Length)
        //    |> Seq.toList

        //fun (partiallyQualifiedName: MaybeUnresolvedIdents)
            //(requiresQualifiedAccessParent: Idents option, autoOpenParent: Idents option, entityNamespace: Idents option, entity: Idents) ->
            //match res with
            //| None -> [||]
            //| Some (scope, ns, pos) ->
                //let results =
                //    Entity.tryCreate(ns, scope.Idents, partiallyQualifiedName, requiresQualifiedAccessParent, autoOpenParent, entityNamespace, entity)
                //    |> Array.map (fun e ->
                //        e,
                //        match modules |> List.filter (fun (m, _, _) -> entity |> Array.startsWith m ) with
                //        | [] -> { ScopeKind = scope.Kind; Pos = pos }
                //        | (_, endLine, startCol) :: _ ->
                //            //printfn "All modules: %A, Win module: %A" modules m
                //            let scopeKind =
                //                match scope.Kind with
                //                | TopModule -> NestedModule
                //                | x -> x
                //            { ScopeKind = scopeKind; Pos = Point.make (endLine + 1) startCol })
                //results

OK... It's crashing just because of the number of commented lines.

This file also crashes with a StackOverflowException.

//1
//2
//3
//4
//5
//6
//7
//8
//9
//10
//11
//12
//13
//14
//15
//16
//17
//18
//19
//20
//21
//22
//23
//24
//25
//26
//27
//28
//29
//30
//31
//32
//33
//34
//35
//36
//37
//38
//39
//40
//41
//42
//43
//44
//45
//46
//47
//48
//49
//50
//51
//52
//53
//54
//55
//56
//57
//58
//59
//60
//61
//62
//63
//64
//65
//66
//67
//68
//69
//70
//71
//72
//73
//74
//75
//76
//77
//78
//79
//80
//81
//82
//83
//84
//85
//86
//87
//88
//89
//90
//91
//92
//93
//94
//95
//96
//97
//98
//99
//100
//101
//102
//103
//104
//105
//106
//107
//108
//109
//110
//111
//112
//113
//114
//115
//116
//117
//118
//119
//120
//121
//122
//123
//124
//125
//126
//127
//128
//129
//130
//131
//132
//133
//134
//135
//136
//137
//138
//139
//140
//141
//142
//143
//144
//145
//146
//147
//148
//149
//150
//151
//152
//153
//154
//155
//156
//157
//158
//159
//160
//161
//162
//163
//164
//165
//166
//167
//168
//169
//170
//171
//172
//173
//174
//175
//176
//177
//178
//179
//180
//181
//182
//183
//184
//185
//186
//187
//188
//189
//190
//191
//192
//193
//194
//195
//196
//197
//198
//199
//200
//201
//202
//203
//204
//205
//206
//207
//208
//209
//210
//211
//212
//213
//214
//215
//216
//217
//218
//219
//220
//221
//222
//223
//224
//225
//226
//227
//228
//229
//230
//231
//232
//233
//234
//235
//236
//237
//238
//239
//240
//241
//242
//243
//244
//245
//246
//247
//248
//249
//250
//251
//252
//253
//254
//255
//256
//257
//258
//259
//260
//261
//262
//263
//264
//265
//266
//267
//268
//269
//270
//271
//272
//273
//274
//275
//276
//277
//278
//279
//280
//281
//282
//283
//284
//285
//286
//287
//288
//289
//290
//291
//292
//293
//294
//295
//296
//297
//298
//299
//300
//301
//302
//303
//304
//305
//306
//307
//308
//309
//310
//311
//312
//313
//314
//315
//316
//317
//318
//319
//320
//321
//322
//323
//324
//325
//326
//327
//328
//329
//330
//331
//332
//333
//334
//335
//336
//337
//338
//339
//340
//341
//342
//343
//344
//345
//346
//347
//348
//349
//350
//351
//352
//353
//354
//355
//356
//357
//358
//359
//360
//361
//362
//363
//364
//365
//366
//367
//368
//369
//370
//371
//372
//373
//374
//375
//376
//377
//378
//379
//380
//381
//382
//383
//384
//385
//386
//387
//388
//389
//390
//391
//392
//393
//394
//395
//396
//397
//398
//399
//400
//401
//402
//403
//404
//405
//406
//407
//408
//409
//410
//411
//412
//413
//414
//415
//416
//417
//418
//419
//420
//421
//422
//423
//424
//425
//426
//427
//428
//429
//430
//431
//432
//433
//434
//435
//436
//437
//438
//439
//440
//441
//442
//443
//444
//445
//446
//447
//448
//449
//450
//451
//452
//453
//454
//455
//456
//457
//458
//459
//460
//461
//462
//463
//464
//465
//466
//467
//468
//469
//470
//471
//472
//473
//474
//475
//476
//477
//478
//479
//480
//481
//482
//483
//484
//485
//486
//487
//488
//489
//490
//491
//492
//493
//494
//495
//496
//497
//498
//499
//500
//501
//502
//503
//504
//505
//506
//507
//508
//509
//510
//511
//512
//513
//514
//515
//516
//517
//518
//519
//520
//521
//522
//523
//524
//525
//526
//527
//528
//529
//530
//531
//532
//533
//534
//535
//536
//537
//538
//539
//540
//541
//542
//543
//544
//545
//546
//547
//548
//549
//550
//551
//552
//553
//554
//555
//556
//557
//558
//559
//560
//561
//562
//563
//564
//565
//566
//567
//568
//569
//570
//571
//572
//573
//574
//575
//576
//577
//578
//579
//580
//581
//582
//583
//584
//585
//586
//587
//588
//589
//590
//591
//592
//593
//594
//595
//596
//597
//598
//599
//600
//601
//602
//603
//604
//605
//606
//607
//608
//609
//610
//611
//612
//613
//614
//615
//616
//617
//618
//619
//620
//621
//622
//623
//624
//625
//626
//627
//628
//629
//630
//631
//632
//633
//634
//635
//636
//637
//638
//639
//640
//641
//642
//643
//644
//645
//646
//647
//648
//649
//650
//651
//652
//653
//654
//655
//656
//657
//658
//659
//660
//661
//662
//663
//664
//665
//666
//667
//668
//669
//670
//671
//672
//673
//674
//675
//676
//677
//678
//679
//680
//681
//682
//683
//684
//685
//686
//687
//688
//689
//690
//691
//692
//693
//694
//695
//696
//697
//698
//699
//700
//701
//702
//703
//704
//705
//706
//707
//708
//709
//710
//711
//712
//713
//714
//715
//716
//717
//718
//719
//720
//721
//722
//723
//724
//725
//726
//727
//728
//729
//730
//731
//732
//733
//734
//735
//736
//737
//738
//739
//740
//741
//742
//743
//744
//745
//746
//747
//748
//749
//750
//751
//752
//753
//754
//755
//756
//757
//758
//759
//760
//761
//762
//763
//764
//765
//766
//767
//768
//769
//770
//771
//772
//773
//774
//775
//776
//777
//778
//779
//780
//781
//782
//783
//784
//785
//786
//787
//788
//789
//790
//791
//792
//793
//794
//795
//796
//797
//798
//799
//800
//801
//802
//803
//804
//805
//806
//807
//808
//809
//810
//811
//812
//813
//814
//815
//816
//817
//818
//819
//820
//821
//822
//823
//824
//825
//826
//827
//828
//829
//830
//831
//832
//833
//834
//835
//836
//837
//838
//839
//840
//841
//842
//843
//844
//845
//846
//847
//848
//849
//850
//851
//852
//853
//854
//855
//856
//857
//858
//859
//860
//861
//862
//863
//864
//865
//866
//867
//868
//869
//870
//871
//872
//873
//874
//875
//876
//877
//878
//879
//880
//881
//882
//883
//884
//885
//886
//887
//888
//889
//890
//891
//892
//893
//894
//895
//896
//897
//898
//899
//900
//901
//902
//903
//904
//905
//906
//907
//908
//909
//910
//911
//912
//913
//914
//915
//916
//917
//918
//919
//920
//921
//922
//923
//924
//925
//926
//927
//928
//929
//930
//931
//932
//933
//934
//935
//936
//937
//938
//939
//940
//941
//942
//943
//944
//945
//946
//947
//948
//949
//950
//951
//952
//953
//954
//955
//956
//957
//958
//959
//960
//961
//962
//963
//964
//965
//966
//967
//968
//969
//970
//971
//972
//973
//974
//975
//976
//977
//978
//979
//980
//981
//982
//983
//984
//985
//986
//987
//988
//989
//990
//991
//992
//993
//994
//995
//996
//997
//998
//999
//1000

this is my favorite repro

@TIHan had mentioned a while ago that we had a constant folding issue within the typechecker that resulted in stack overflows. Perhaps this is related?

@dsyme this one gets repro'd by the VSMac team too if they use global search now that we have FSharp.Editor. The search will try to parse a file, and it will hit this file with all the comments leading to the exception.

Given that a few folks have hit this issue and they haven't been using F# at all, I'd say this is more severe and we should probably take a look at it in addition to the other more severe bugs. The file is not that large, yet the commenting out is somehow leading to this crash.

Note that this will ultimately affect all environments on macOS, not just VSMac.

String constant folding doesn't take place here. That's an optimization before emitting IL.

My gut feeling is that mono isn't supporting tail call instructions properly. After looking at how we handle comments, we rely on recursion and tail calls. Could we ask someone from the mono team to look at this?

Was this page helpful?
0 / 5 - 0 ratings