Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\netfx.props" />
<Import Project="..\..\eng\Versions.props"/> <!-- keep our test deps in line with the overall compiler -->
<PropertyGroup>
Expand All @@ -19,6 +19,9 @@
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\Common.fs">
<Link>Common.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\RangeScopedCollectionTests.fs">
<Link>RangeScopedCollectionTests.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)\..\tests\service\AssemblyReaderShim.fs">
<Link>AssemblyReaderShim.fs</Link>
</Compile>
Expand Down
3 changes: 3 additions & 0 deletions fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@
<Compile Include="$(FSharpSourcesRoot)/fsharp/range.fs">
<Link>ErrorLogging/range.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/RangeScopedCollection.fs">
<Link>ErrorLogging/RangeScopedCollection.fs</Link>
</Compile>
<Compile Include="$(FSharpSourcesRoot)/fsharp/ErrorLogger.fs">
<Link>ErrorLogging/ErrorLogger.fs</Link>
</Compile>
Expand Down
2 changes: 2 additions & 0 deletions src/absil/illib.fs
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,8 @@ type EventuallyBuilder() =

member x.TryWith(e, handler) = Eventually.tryWith e handler

member x.Using(resource, e) = Eventually.tryFinally (e resource) (fun () -> (resource :> IDisposable).Dispose())

member x.TryFinally(e, compensation) = Eventually.tryFinally e compensation

member x.Delay f = Eventually.delay f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@
<Compile Include="..\range.fs">
<Link>ErrorLogging\range.fs</Link>
</Compile>
<Compile Include="..\RangeScopedCollection.fs">
<Link>ErrorLogging\RangeScopedCollection.fs</Link>
</Compile>
<Compile Include="..\ErrorLogger.fs">
<Link>ErrorLogging\ErrorLogger.fs</Link>
</Compile>
Expand Down
30 changes: 26 additions & 4 deletions src/fsharp/NameResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// Name environment and name resolution
module internal FSharp.Compiler.NameResolution

open System
open System.Collections.Generic

open Internal.Utilities
Expand Down Expand Up @@ -1451,6 +1452,7 @@ type ITypecheckResultsSink =
abstract NotifyMethodGroupNameResolution : pos * item: Item * itemMethodGroup: Item * TyparInst * ItemOccurence * NameResolutionEnv * AccessorDomain * range * replace: bool -> unit
abstract NotifyFormatSpecifierLocation: range * int -> unit
abstract NotifyOpenDeclaration: OpenDeclaration -> unit
abstract CreateScope: range -> IDisposable
abstract CurrentSourceText: ISourceText option
abstract FormatStringCheckContext: FormatStringCheckContext option

Expand Down Expand Up @@ -1671,14 +1673,26 @@ type CapturedNameResolution(i: Item, tpinst, io: ItemOccurence, nre: NameResolut
member this.DebugToString() =
sprintf "%A: %+A" (this.Pos.Line, this.Pos.Column) i

interface IRangeOwner with
member this.Range = m


type CapturedExpressionType =
| CET of ty: TType * nenv: NameResolutionEnv * ad: AccessorDomain * m: range

interface IRangeOwner with
member this.Range =
let (CET (m = range)) = this in range


/// Represents container for all name resolutions that were met so far when typechecking some particular file
type TcResolutions
(capturedEnvs: ResizeArray<range * NameResolutionEnv * AccessorDomain>,
capturedExprTypes: ResizeArray<TType * NameResolutionEnv * AccessorDomain * range>,
capturedExprTypes: RangeScopedCollection<CapturedExpressionType>,
capturedNameResolutions: ResizeArray<CapturedNameResolution>,
capturedMethodGroupResolutions: ResizeArray<CapturedNameResolution>) =

static let empty = TcResolutions(ResizeArray 0, ResizeArray 0, ResizeArray 0, ResizeArray 0)
static let empty = TcResolutions(ResizeArray 0, RangeScopedCollection (), ResizeArray 0, ResizeArray 0)

member this.CapturedEnvs = capturedEnvs
member this.CapturedExpressionTypings = capturedExprTypes
Expand Down Expand Up @@ -1727,7 +1741,7 @@ type TcSymbolUses(g, capturedNameResolutions: ResizeArray<CapturedNameResolution
/// An accumulator for the results being emitted into the tcSink.
type TcResultsSinkImpl(g, ?sourceText: ISourceText) =
let capturedEnvs = ResizeArray<_>()
let capturedExprTypings = ResizeArray<_>()
let capturedExprTypings = RangeScopedCollection<CapturedExpressionType>()
let capturedNameResolutions = ResizeArray<CapturedNameResolution>()
let capturedMethodGroupResolutions = ResizeArray<CapturedNameResolution>()
let capturedOpenDeclarations = ResizeArray<OpenDeclaration>()
Expand Down Expand Up @@ -1807,7 +1821,7 @@ type TcResultsSinkImpl(g, ?sourceText: ISourceText) =

member sink.NotifyExprHasType(ty, nenv, ad, m) =
if allowedRange m then
capturedExprTypings.Add((ty, nenv, ad, m))
capturedExprTypings.AddItem(CET(ty, nenv, ad, m))

member sink.NotifyNameResolution(endPos, item, tpinst, occurenceType, nenv, ad, m, replace) =
if allowedRange m then
Expand All @@ -1830,6 +1844,9 @@ type TcResultsSinkImpl(g, ?sourceText: ISourceText) =
member sink.NotifyOpenDeclaration openDeclaration =
capturedOpenDeclarations.Add openDeclaration

member sink.CreateScope m =
capturedExprTypings.CreateScope(m)

member sink.CurrentSourceText = sourceText

member sink.FormatStringCheckContext = formatStringCheckContext.Value
Expand Down Expand Up @@ -1887,6 +1904,11 @@ let CallOpenDeclarationSink (sink: TcResultsSink) (openDeclaration: OpenDeclarat
| None -> ()
| Some sink -> sink.NotifyOpenDeclaration openDeclaration

let CreateScope (sink: TcResultsSink) (m: range) =
match sink.CurrentSink with
| None -> Disposable.Empty
| Some sink -> sink.CreateScope(m)

//-------------------------------------------------------------------------
// Check inferability of type parameters in resolved items.
//-------------------------------------------------------------------------
Expand Down
15 changes: 14 additions & 1 deletion src/fsharp/NameResolution.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

module internal FSharp.Compiler.NameResolution

open System
open FSharp.Compiler
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.AbstractSyntax
Expand Down Expand Up @@ -325,6 +326,14 @@ type internal CapturedNameResolution =
/// The starting and ending position
member Range : range

interface IRangeOwner


type CapturedExpressionType =
| CET of ty: TType * nenv: NameResolutionEnv * ad: AccessorDomain * m: range
interface IRangeOwner


[<Class>]
type internal TcResolutions =

Expand All @@ -334,7 +343,7 @@ type internal TcResolutions =

/// Information of exact types found for expressions, that can be to the left of a dot.
/// typ - the inferred type for an expression
member CapturedExpressionTypings : ResizeArray<TType * NameResolutionEnv * AccessorDomain * range>
member CapturedExpressionTypings : RangeScopedCollection<CapturedExpressionType>

/// Exact name resolutions
member CapturedNameResolutions : ResizeArray<CapturedNameResolution>
Expand Down Expand Up @@ -419,6 +428,8 @@ type ITypecheckResultsSink =
/// Record that an open declaration occured in a given scope range
abstract NotifyOpenDeclaration : OpenDeclaration -> unit

abstract CreateScope: range -> IDisposable

/// Get the current source
abstract CurrentSourceText : ISourceText option

Expand Down Expand Up @@ -483,6 +494,8 @@ val internal CallExprHasTypeSink : TcResultsSink -> range * NameResolutio
/// Report an open declaration
val internal CallOpenDeclarationSink : TcResultsSink -> OpenDeclaration -> unit

val internal CreateScope: TcResultsSink -> range -> IDisposable

/// Get all the available properties of a type (both intrinsic and extension)
val internal AllPropInfosOfTypeInScope : ResultCollectionSettings -> InfoReader -> NameResolutionEnv -> string option -> AccessorDomain -> FindMemberFlag -> range -> TType -> PropInfo list

Expand Down
101 changes: 101 additions & 0 deletions src/fsharp/RangeScopedCollection.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
namespace FSharp.Compiler

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

type IRangeOwner =
abstract Range: range

type Disposable() =
static member val Empty: IDisposable = new Disposable() :> _

interface IDisposable with
member _.Dispose() = ()


type ScopeData<'T when 'T :> IRangeOwner>() =
member val Items = ResizeArray<'T>()
member val NestedScopes = ResizeArray<RangedScope<'T>>()

and RangedScope<'T when 'T :> IRangeOwner> =
private
| Global of ScopeData<'T>
| Nested of range * ScopeData<'T>

member private scope.Data =
match scope with
| Global data
| Nested (_, data) -> data

member scope.NestedScopes =
scope.Data.NestedScopes

member scope.Items =
scope.Data.Items

member scope.Range =
match scope with
| Global _ -> None
| Nested (scopeRange, _) -> Some scopeRange

member scope.Contains(m: range) =
match scope with
| Global _ -> true
| Nested (scopeRange, _) -> rangeContainsRange scopeRange m

member scope.Contains(p: pos) =
match scope with
| Global _ -> true
| Nested (scopeRange, _) -> rangeContainsPos scopeRange p

member x.CreateNestedScope(m: range) =
match x with
| Global _ -> ()
| Nested (currentScopeRange, _) ->
assert rangeContainsRange currentScopeRange m

let scope = Nested(m, ScopeData())
x.NestedScopes.Add(scope)

scope

static member CreateGlobalScope<'T>() =
RangedScope.Global(ScopeData<'T>())

type RangeScopedCollection<'T when 'T :> IRangeOwner>() =
let globalScope = RangedScope.CreateGlobalScope()
let mutable currentScope = globalScope

member _.AddItem(item: 'T) =
currentScope.Items.Add(item)

member _.CreateScope(m: range) =
let newScope = currentScope.CreateNestedScope(m)

let parentScope = currentScope
currentScope <- newScope

{ new IDisposable with
member x.Dispose() =
// todo: remove scopes without items
currentScope <- parentScope }

member x.GlobalScope = globalScope

[<RequireQualifiedAccess>]
module RangeScopedCollection =
let itemsByEndPos (endPos: pos) (rsc: RangeScopedCollection<_>): IList<_> =
let rec loop (result: ResizeArray<_>) endPos (scope: RangedScope<_>) =
for item in scope.Items do
if rangeEndsAtPos endPos item.Range then
result.Add(item)

for scope in scope.NestedScopes do
if scope.Contains(endPos) then
loop result endPos scope

let result = ResizeArray()
loop result endPos rsc.GlobalScope

result :> _
5 changes: 4 additions & 1 deletion src/fsharp/TypeChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,7 @@ let MakeAndPublishSimpleValsForMergedScope cenv env m (names: NameMap<_>) =
member this.NotifyExprHasType(_, _, _, _) = assert false // no expr typings in MakeAndPublishSimpleVals
member this.NotifyFormatSpecifierLocation(_, _) = ()
member this.NotifyOpenDeclaration(_) = ()
member this.CreateScope _ = Disposable.Empty
member this.CurrentSourceText = None
member this.FormatStringCheckContext = None }

Expand Down Expand Up @@ -17509,8 +17510,10 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem

//printfn "----------\nCHECKING, e = %+A\n------------------\n" e
try
match ElimModuleDoBinding synDecl with
let synDecl = ElimModuleDoBinding synDecl
use _ = CreateScope cenv.tcSink synDecl.Range

match synDecl with
| SynModuleDecl.ModuleAbbrev (id, p, m) ->
let env = MutRecBindingChecking.TcModuleAbbrevDecl cenv scopem env (id, p, m)
return ((fun e -> e), []), env, env
Expand Down
3 changes: 3 additions & 0 deletions src/fsharp/range.fs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ let rangeContainsPos (m1:range) p =
let rangeBeforePos (m1:range) p =
posGeq p m1.End

let rangeEndsAtPos (pos: pos) (m: range) =
m.EndLine = pos.Line && m.EndColumn = pos.Column

let rangeN filename line = mkRange filename (mkPos line 0) (mkPos line 0)

let pos0 = mkPos 1 0
Expand Down
2 changes: 2 additions & 0 deletions src/fsharp/range.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ val rangeContainsPos : range -> pos -> bool
/// Test to see if a range occurs fully before a position
val rangeBeforePos : range -> pos -> bool

val rangeEndsAtPos: pos: pos -> m: range -> bool

/// Make a dummy range for a file
val rangeN : string -> int -> range

Expand Down
34 changes: 16 additions & 18 deletions src/fsharp/service/FSharpCheckerResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -359,26 +359,24 @@ type internal TypeCheckInfo
| Some (denv, m, items) ->
let items = List.map ItemWithNoInst items
ReturnItemsOfType items g denv m TypeNameResolutionFlag.ResolveTypeNamesToTypeRefs hasTextChangedSinceLastTypecheck

/// finds captured typing for the given position
let GetExprTypingForPosition(endOfExprPos) =
let quals =
sResolutions.CapturedExpressionTypings
|> Seq.filter (fun (ty,nenv,_,m) ->
// We only want expression types that end at the particular position in the file we are looking at.
posEq m.End endOfExprPos &&

// Get rid of function types. True, given a 2-arg curried function "f x y", it is legal to do "(f x).GetType()",
// but you almost never want to do this in practice, and we choose not to offer up any intellisense for
// F# function types.
not (isFunTy nenv.DisplayEnv.g ty))

/// Get captured expression types ending at the particular position.
let GetExprTypingForPosition (endOfExprPos: pos) =
let quals =
sResolutions.CapturedExpressionTypings
|> RangeScopedCollection.itemsByEndPos endOfExprPos
|> Seq.filter (fun (CET(ty,nenv,_,_)) ->
// Get rid of function types. True, given a 2-arg curried function "f x y", it is legal to do "(f x).GetType()",
// but you almost never want to do this in practice, and we choose not to offer up any intellisense for
// F# function types.
not (isFunTy nenv.DisplayEnv.g ty))
|> Seq.toArray

let thereWereSomeQuals = not (Array.isEmpty quals)
// filter out errors

let quals = quals
|> Array.filter (fun (ty,nenv,_,_) ->
|> Array.filter (fun (CET(ty,nenv,_,_)) ->
let denv = nenv.DisplayEnv
not (isTyparTy denv.g ty && (destTyparTy denv.g ty).IsFromError))
thereWereSomeQuals, quals
Expand All @@ -391,11 +389,11 @@ type internal TypeCheckInfo
match quals with
| [||] -> None
| quals ->
quals |> Array.tryFind (fun (_,_,_,rq) ->
quals |> Array.tryFind (fun (CET(_,_,_,rq)) ->
ignore(r) // for breakpoint
posEq r.Start rq.Start)
match bestQual with
| Some (ty,nenv,ad,m) when isRecdTy nenv.DisplayEnv.g ty ->
| Some (CET(ty,nenv,ad,m)) when isRecdTy nenv.DisplayEnv.g ty ->
let items = NameResolution.ResolveRecordOrClassFieldsOfType ncenv m ad ty false
Some (items, nenv.DisplayEnv, m)
| _ -> None
Expand Down Expand Up @@ -426,7 +424,7 @@ type internal TypeCheckInfo
// If not, then the stale typecheck info does not have a capturedExpressionTyping for this exact expression, and the
// user can wait for typechecking to catch up and second-chance intellisense to give the right result.
let qual =
quals |> Array.tryFind (fun (_,_,_,r) ->
quals |> Array.tryFind (fun (CET(_,_,_,r)) ->
ignore(r) // for breakpoint
posEq exprRange.Start r.Start)
qual, false
Expand All @@ -439,7 +437,7 @@ type internal TypeCheckInfo

match bestQual with
| Some bestQual ->
let (ty,nenv,ad,m) = bestQual
let (CET(ty,nenv,ad,m)) = bestQual
let items = ResolveCompletionsInType ncenv nenv (ResolveCompletionTargets.All(ConstraintSolver.IsApplicableMethApprox g amap m)) m ad false ty
let items = items |> List.map ItemWithNoInst
let items = items |> RemoveDuplicateItems g
Expand Down
Loading