Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
* IL: hold custom attributes in fields rather than a union case ([PR #20287](https://github.com/dotnet/fsharp/pull/20287))
* IL: reuse the cached ILTypeRef in ILTypeInfo.FromType ([PR #20255](https://github.com/dotnet/fsharp/pull/20255))
* Name resolution: group C#-style extension members per `open` and extended type ([PR #20298](https://github.com/dotnet/fsharp/pull/20298))
* `FSharpProjectSnapshot.FromOptions` takes an optional `getReferenceStamp: string -> DateTime`, so a host that already tracks the last-write times of on-disk references can supply them instead of having every `-r:` stat'd again each time a snapshot is built. ([PR #20459](https://github.com/dotnet/fsharp/pull/20459))

### Improved

Expand Down
10 changes: 7 additions & 3 deletions src/Compiler/Service/FSharpProjectSnapshot.fs
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,11 @@ and [<Experimental("This FCS API is experimental and subject to change.")>] FSha
ProjectSnapshotBase(projectConfig, referencedProjects, sourceFiles)
|> FSharpProjectSnapshot

static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) =
static member FromOptions
(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator, ?getReferenceStamp: string -> DateTime)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖🕵️ Keep the existing public FromOptions CLR method. Put shared logic in a private implementation and expose a distinctly named internal host entry point for the editor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xperiandri but arguments are optional

=
let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary())
let getReferenceStamp = defaultArg getReferenceStamp FileSystem.GetLastWriteTimeShim

async {

Expand All @@ -665,7 +668,8 @@ and [<Experimental("This FCS API is experimental and subject to change.")>] FSha
|> Seq.map (function
| FSharpReferencedProject.FSharpReference(outputName, options) ->
async {
let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator)
let! snapshot =
FSharpProjectSnapshot.FromOptions(options, getFileSnapshot, snapshotAccumulator, getReferenceStamp)

return FSharpReferencedProjectSnapshot.FSharpReference(outputName, snapshot)
}
Expand All @@ -686,7 +690,7 @@ and [<Experimental("This FCS API is experimental and subject to change.")>] FSha

{
Path = path
LastModified = FileSystem.GetLastWriteTimeShim(path)
LastModified = getReferenceStamp path
})
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,104 +1,61 @@
module FSharpChecker.ProjectSnapshot

open Xunit
open System
open System.IO
open System.Threading.Tasks
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.CodeAnalysis.ProjectSnapshot
open Xunit


// TODO: restore tests

//[<Fact>]
//let WithoutImplFilesThatHaveSignatures () =

// let snapshot = FSharpProjectSnapshot.Create(
// projectFileName = "Dummy.fsproj",
// projectId = None,
// sourceFiles = [
// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// ],
// referencesOnDisk = [],
// otherOptions = [],
// referencedProjects = [],
// isIncompleteTypeCheckEnvironment = true,
// useScriptResolutionRules = false,
// loadTime = DateTime(1234, 5, 6),
// unresolvedReferences = None,
// originalLoadReferences = [],
// stamp = None
// )

// let result = snapshot.WithoutImplFilesThatHaveSignatures

// let expected = [| "A.fsi"; "B.fs"; "C.fsi" |]

// Assert.Equal<string array>(expected, result.SourceFileNames |> List.toArray)

// Assert.Equal<byte array>(result.FullVersion, snapshot.SignatureVersion)

//[<Fact>]
//let WithoutImplFilesThatHaveSignaturesExceptLastOne () =

// let snapshot = FSharpProjectSnapshot.Create(
// projectFileName = "Dummy.fsproj",
// projectId = None,
// sourceFiles = [
// { FileName = "A.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "C.fsi"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// ],
// referencesOnDisk = [],
// otherOptions = [],
// referencedProjects = [],
// isIncompleteTypeCheckEnvironment = true,
// useScriptResolutionRules = false,
// loadTime = DateTime(1234, 5, 6),
// unresolvedReferences = None,
// originalLoadReferences = [],
// stamp = None
// )

// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne

// let expected = [| "A.fsi"; "B.fs"; "C.fsi"; "C.fs" |]

// Assert.Equal<string array>(expected, result.SourceFileNames |> List.toArray)

// Assert.Equal<byte array>(result.FullVersion, snapshot.LastFileVersion)


//[<Fact>]
//let WithoutImplFilesThatHaveSignaturesExceptLastOne_2 () =

// let snapshot = FSharpProjectSnapshot.Create(
// projectFileName = "Dummy.fsproj",
// projectId = None,
// sourceFiles = [
// { FileName = "A.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "B.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// { FileName = "C.fs"; Version = "1"; GetSource = Unchecked.defaultof<_> }
// ],
// referencesOnDisk = [],
// otherOptions = [],
// referencedProjects = [],
// isIncompleteTypeCheckEnvironment = true,
// useScriptResolutionRules = false,
// loadTime = DateTime(1234, 5, 6),
// unresolvedReferences = None,
// originalLoadReferences = [],
// stamp = None
// )

// let result = snapshot.WithoutImplFilesThatHaveSignaturesExceptLastOne

// let expected = [| "A.fs"; "B.fs"; "C.fs" |]

// Assert.Equal<string array>(expected, result.SourceFileNames |> List.toArray)

// Assert.Equal<byte array>(result.FullVersion, snapshot.LastFileVersion)

#nowarn "57"

let private projectOptions projectFileName references referencedProjects =
{
ProjectFileName = projectFileName
ProjectId = None
SourceFiles = [| Path.ChangeExtension(projectFileName, ".fs") |]
OtherOptions = [| for path in references -> $"-r:{path}" |]
ReferencedProjects = referencedProjects
IsIncompleteTypeCheckEnvironment = false
UseScriptResolutionRules = false
LoadTime = DateTime.UtcNow
UnresolvedReferences = None
OriginalLoadReferences = []
Stamp = None
}

let private emptySource _ path =
async { return FSharpFileSnapshot.CreateFromString(path, "") }

[<Fact>]
let ``FromOptions takes reference stamps from the host, including referenced projects`` () : Task =
task {
let stamps =
dict
[
"MainRef.dll", DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc)
"LibRef.dll", DateTime(2026, 2, 2, 0, 0, 0, DateTimeKind.Utc)
]

let lib = projectOptions "Lib.fsproj" [ "LibRef.dll" ] [||]

let main =
projectOptions "Main.fsproj" [ "MainRef.dll" ] [| FSharpReferencedProject.FSharpReference("Lib.dll", lib) |]

let! snapshot = FSharpProjectSnapshot.FromOptions(main, emptySource, getReferenceStamp = (fun path -> stamps[path]))

let libSnapshot =
match snapshot.ReferencedProjects with
| [ FSharpReferencedProjectSnapshot.FSharpReference(_, lib) ] -> lib
| other -> failwith $"Expected one referenced project, got %A{other}"

Assert.Equal<ReferenceOnDisk list>(
[ { Path = "MainRef.dll"; LastModified = stamps["MainRef.dll"] } ],
snapshot.ReferencesOnDisk
)

Assert.Equal<ReferenceOnDisk list>(
[ { Path = "LibRef.dll"; LastModified = stamps["LibRef.dll"] } ],
libSnapshot.ReferencesOnDisk
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2502,7 +2502,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FS
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences()
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, FSharp.Compiler.CodeAnalysis.DocumentSource)
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]])
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, Microsoft.FSharp.Core.FSharpFunc`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]]], Microsoft.FSharp.Core.FSharpOption`1[System.Collections.Generic.Dictionary`2[FSharp.Compiler.CodeAnalysis.FSharpProjectOptions,FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot]], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[System.String,System.DateTime]])
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] FromOptions(FSharp.Compiler.CodeAnalysis.FSharpProjectOptions, System.String, Int32, FSharp.Compiler.Text.ISourceText, FSharp.Compiler.CodeAnalysis.DocumentSource)
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences
FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences()
Expand Down
Loading