Skip to content

Commit 199dd0c

Browse files
committed
Added service_slim
1 parent d44950e commit 199dd0c

File tree

12 files changed

+607
-4
lines changed

12 files changed

+607
-4
lines changed

fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@
667667
<Compile Include="$(FSharpSourcesRoot)\fsharp\fsi\fsi.fs">
668668
<Link>Service/fsi.fs</Link>
669669
</Compile>
670+
<Compile Include="service_slim.fs" />
670671
</ItemGroup>
671672
<ItemGroup>
672673
<PackageReference Include="FSharp.Core" Version="$(FcsFSharpCorePkgVersion)" />
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
// Open up the compiler as an incremental service for parsing,
4+
// type checking and intellisense-like environment-reporting.
5+
6+
namespace FSharp.Compiler.SourceCodeServices
7+
8+
open System
9+
open System.Collections.Generic
10+
open System.Collections.Concurrent
11+
open System.Diagnostics
12+
open System.IO
13+
open System.Reflection
14+
open System.Text
15+
16+
open Microsoft.FSharp.Core.Printf
17+
open FSharp.Compiler
18+
open FSharp.Compiler.AbstractIL
19+
open FSharp.Compiler.AbstractIL.IL
20+
open FSharp.Compiler.AbstractIL.ILBinaryReader
21+
open FSharp.Compiler.AbstractIL.Diagnostics
22+
open FSharp.Compiler.AbstractIL.Internal
23+
open FSharp.Compiler.AbstractIL.Internal.Library
24+
25+
open FSharp.Compiler.AccessibilityLogic
26+
open FSharp.Compiler.Ast
27+
open FSharp.Compiler.CompileOps
28+
open FSharp.Compiler.CompileOptions
29+
open FSharp.Compiler.Driver
30+
open FSharp.Compiler.ErrorLogger
31+
open FSharp.Compiler.Lib
32+
open FSharp.Compiler.PrettyNaming
33+
open FSharp.Compiler.Parser
34+
open FSharp.Compiler.Range
35+
open FSharp.Compiler.Lexhelp
36+
open FSharp.Compiler.Layout
37+
open FSharp.Compiler.Tast
38+
open FSharp.Compiler.Tastops
39+
open FSharp.Compiler.TcGlobals
40+
open FSharp.Compiler.Text
41+
open FSharp.Compiler.Infos
42+
open FSharp.Compiler.InfoReader
43+
open FSharp.Compiler.NameResolution
44+
open FSharp.Compiler.TypeChecker
45+
open FSharp.Compiler.SourceCodeServices.SymbolHelpers
46+
47+
open Internal.Utilities
48+
open Internal.Utilities.Collections
49+
open FSharp.Compiler.Layout.TaggedTextOps
50+
51+
//-------------------------------------------------------------------------
52+
// InteractiveChecker
53+
//-------------------------------------------------------------------------
54+
55+
type internal TcResult = TcEnv * TopAttribs * TypedImplFile option * ModuleOrNamespaceType
56+
type internal TcErrors = FSharpErrorInfo[]
57+
58+
type InteractiveChecker internal (tcConfig, tcGlobals, tcImports, tcInitialState, ctok, reactorOps, parseCache, checkCache) =
59+
let userOpName = "Unknown"
60+
let suggestNamesForErrors = true
61+
62+
static member Create(projectOptions: FSharpProjectOptions) =
63+
let tcConfig =
64+
let tcConfigB = TcConfigBuilder.Initial
65+
tcConfigB.implicitIncludeDir <- Path.GetDirectoryName(projectOptions.ProjectFileName)
66+
tcConfigB.legacyReferenceResolver <- SimulatedMSBuildReferenceResolver.getResolver()
67+
let sourceFiles = projectOptions.SourceFiles |> Array.toList
68+
let argv = projectOptions.OtherOptions |> Array.toList
69+
let _sourceFiles = ApplyCommandLineArgs(tcConfigB, sourceFiles, argv)
70+
TcConfig.Create(tcConfigB, validate=false)
71+
72+
let tcConfigP = TcConfigProvider.Constant(tcConfig)
73+
74+
let ctok = CompilationThreadToken()
75+
let tcGlobals, tcImports =
76+
TcImports.BuildTcImports (ctok, tcConfigP)
77+
|> Cancellable.runWithoutCancellation
78+
79+
let niceNameGen = NiceNameGenerator()
80+
let assemblyName = projectOptions.ProjectFileName |> System.IO.Path.GetFileNameWithoutExtension
81+
let tcInitialEnv = GetInitialTcEnv (assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals)
82+
let tcInitialState = GetInitialTcState (rangeStartup, assemblyName, tcConfig, tcGlobals, tcImports, niceNameGen, tcInitialEnv)
83+
84+
let reactorOps =
85+
{ new IReactorOperations with
86+
member __.EnqueueAndAwaitOpAsync (userOpName, opName, opArg, op) =
87+
async.Return (Cancellable.runWithoutCancellation (op ctok))
88+
member __.EnqueueOp (userOpName, opName, opArg, op) = (op ctok) }
89+
90+
// parse cache, keyed on file name and source hash
91+
let parseCache = ConcurrentDictionary<string * int, FSharpParseFileResults>(HashIdentity.Structural)
92+
// type check cache, keyed on file name
93+
let checkCache = ConcurrentDictionary<string, (TcResult * TcErrors) * (TcState * ModuleNamesDict)>(HashIdentity.Structural)
94+
95+
InteractiveChecker (tcConfig, tcGlobals, tcImports, tcInitialState, ctok, reactorOps, parseCache, checkCache)
96+
97+
member private x.MakeProjectResults (projectFileName: string, parseResults: FSharpParseFileResults[], tcState: TcState, errors: FSharpErrorInfo[],
98+
symbolUses: TcSymbolUses list, topAttrsOpt: TopAttribs option, tcImplFilesOpt: TypedImplFile list option) =
99+
let assemblyRef = mkSimpleAssemblyRef "stdin"
100+
let assemblyDataOpt = None
101+
let access = tcState.TcEnvFromImpls.AccessRights
102+
let dependencyFiles = parseResults |> Seq.map (fun x -> x.DependencyFiles) |> Array.concat
103+
let details = (tcGlobals, tcImports, tcState.Ccu, tcState.CcuSig, symbolUses, topAttrsOpt, assemblyDataOpt, assemblyRef, access, tcImplFilesOpt, dependencyFiles)
104+
let keepAssemblyContents = true
105+
FSharpCheckProjectResults (projectFileName, Some tcConfig, keepAssemblyContents, errors, Some details)
106+
107+
member private x.ClearStaleCache (fileName: string, parsingOptions: FSharpParsingOptions) =
108+
let fileIndex = parsingOptions.SourceFiles |> Array.findIndex ((=) fileName)
109+
let filesAbove = parsingOptions.SourceFiles |> Array.take fileIndex
110+
// backup all cached typecheck entries above file
111+
let cachedAbove = filesAbove |> Array.choose (fun key ->
112+
match checkCache.TryGetValue(key) with
113+
| true, value -> Some (key, value)
114+
| false, _ -> None)
115+
// remove all parse cache entries with the same file name
116+
let staleParseKeys = parseCache.Keys |> Seq.filter (fun (n,_) -> n = fileName) |> Seq.toArray
117+
staleParseKeys |> Array.iter (fun key -> parseCache.TryRemove(key) |> ignore)
118+
checkCache.Clear(); // clear all typecheck cache
119+
// restore all cached typecheck entries above file
120+
cachedAbove |> Array.iter (fun (key, value) -> checkCache.TryAdd(key, value) |> ignore)
121+
122+
member private x.ParseFile (fileName: string, sourceHash: int, source: Lazy<string>, parsingOptions: FSharpParsingOptions) =
123+
let parseCacheKey = fileName, sourceHash
124+
parseCache.GetOrAdd(parseCacheKey, fun _ ->
125+
x.ClearStaleCache(fileName, parsingOptions)
126+
let sourceText = SourceText.ofString source.Value
127+
let parseErrors, parseTreeOpt, anyErrors = ParseAndCheckFile.parseFile (sourceText, fileName, parsingOptions, userOpName, suggestNamesForErrors)
128+
let dependencyFiles = [||] // interactions have no dependencies
129+
FSharpParseFileResults (parseErrors, parseTreeOpt, anyErrors, dependencyFiles) )
130+
131+
member private x.TypeCheckOneInput (parseResults: FSharpParseFileResults, tcSink: TcResultsSink, tcState: TcState, moduleNamesDict: ModuleNamesDict) =
132+
let input = parseResults.ParseTree.Value
133+
let capturingErrorLogger = CompilationErrorLogger("TypeCheckFile", tcConfig.errorSeverityOptions)
134+
let errorLogger = GetErrorLoggerFilteringByScopedPragmas(false, GetScopedPragmasForInput(input), capturingErrorLogger)
135+
use _errorScope = new CompilationGlobalsScope (errorLogger, BuildPhase.TypeCheck)
136+
137+
let checkForErrors () = parseResults.ParseHadErrors || errorLogger.ErrorCount > 0
138+
let prefixPathOpt = None
139+
140+
let input, moduleNamesDict = input |> DeduplicateParsedInputModuleName moduleNamesDict
141+
let tcResult, tcState =
142+
TypeCheckOneInputEventually (checkForErrors, tcConfig, tcImports, tcGlobals, prefixPathOpt, tcSink, tcState, input)
143+
|> Eventually.force ctok
144+
145+
let fileName = parseResults.FileName
146+
let tcErrors = ErrorHelpers.CreateErrorInfos (tcConfig.errorSeverityOptions, false, fileName, (capturingErrorLogger.GetErrors()), suggestNamesForErrors)
147+
(tcResult, tcErrors), (tcState, moduleNamesDict)
148+
149+
member private x.CheckFile (projectFileName: string, parseResults: FSharpParseFileResults, tcState: TcState, moduleNamesDict: ModuleNamesDict) =
150+
match parseResults.ParseTree with
151+
| Some _input ->
152+
let sink = TcResultsSinkImpl(tcGlobals)
153+
let tcSink = TcResultsSink.WithSink sink
154+
let (tcResult, tcErrors), (tcState, moduleNamesDict) =
155+
x.TypeCheckOneInput (parseResults, tcSink, tcState, moduleNamesDict)
156+
let fileName = parseResults.FileName
157+
checkCache.[fileName] <- ((tcResult, tcErrors), (tcState, moduleNamesDict))
158+
159+
let loadClosure = None
160+
let textSnapshotInfo = None
161+
let keepAssemblyContents = true
162+
163+
let tcEnvAtEnd, _topAttrs, implFile, ccuSigForFile = tcResult
164+
let errors = Array.append parseResults.Errors tcErrors
165+
166+
let scope = TypeCheckInfo (tcConfig, tcGlobals, ccuSigForFile, tcState.Ccu, tcImports, tcEnvAtEnd.AccessRights,
167+
projectFileName, fileName, sink.GetResolutions(), sink.GetSymbolUses(), tcEnvAtEnd.NameEnv,
168+
loadClosure, reactorOps, textSnapshotInfo, implFile, sink.GetOpenDeclarations())
169+
FSharpCheckFileResults (fileName, errors, Some scope, parseResults.DependencyFiles, None, reactorOps, keepAssemblyContents)
170+
|> Some
171+
| None ->
172+
None
173+
174+
member private x.TypeCheckClosedInputSet (parseResults: FSharpParseFileResults[], tcState) =
175+
let cachedTypeCheck (tcState, moduleNamesDict) (parseRes: FSharpParseFileResults) =
176+
let checkCacheKey = parseRes.FileName
177+
let typeCheckOneInput _fileName =
178+
x.TypeCheckOneInput (parseRes, TcResultsSink.NoSink, tcState, moduleNamesDict)
179+
checkCache.GetOrAdd(checkCacheKey, typeCheckOneInput)
180+
let results, (tcState, moduleNamesDict) =
181+
((tcState, Map.empty), parseResults) ||> Array.mapFold cachedTypeCheck
182+
let tcResults, tcErrors = Array.unzip results
183+
let (tcEnvAtEndOfLastFile, topAttrs, implFiles, _ccuSigsForFiles), tcState =
184+
TypeCheckMultipleInputsFinish(tcResults |> Array.toList, tcState)
185+
let tcState, declaredImpls = TypeCheckClosedInputSetFinish (implFiles, tcState)
186+
tcState, topAttrs, declaredImpls, tcEnvAtEndOfLastFile, moduleNamesDict, tcErrors
187+
188+
/// Errors grouped by file, sorted by line, column
189+
member private x.ErrorsByFile (fileNames: string[], errorList: FSharpErrorInfo[] list) =
190+
let errorMap = errorList |> Array.concat |> Array.groupBy (fun x -> x.FileName) |> Map.ofArray
191+
let errors = fileNames |> Array.choose errorMap.TryFind
192+
errors |> Array.iter (Array.sortInPlaceBy (fun x -> x.StartLineAlternate, x.StartColumn))
193+
errors |> Array.concat
194+
195+
/// Clears parse and typecheck caches.
196+
member x.ClearCache () =
197+
parseCache.Clear()
198+
checkCache.Clear()
199+
200+
/// Parses and checks the whole project, good for compilers (Fable etc.)
201+
/// Does not retain name resolutions and symbol uses which are quite memory hungry (so no intellisense etc.).
202+
/// Already parsed files will be cached so subsequent compilations will be faster.
203+
member x.ParseAndCheckProject (projectFileName: string, fileNames: string[], sourceReader: string->int*Lazy<string>) =
204+
// parse files
205+
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, fileNames, false)
206+
let parseResults = fileNames |> Array.map (fun fileName ->
207+
let sourceHash, source = sourceReader fileName
208+
x.ParseFile(fileName, sourceHash, source, parsingOptions))
209+
210+
// type check files
211+
let tcState, topAttrs, tcImplFiles, _tcEnvAtEnd, _moduleNamesDict, tcErrors =
212+
x.TypeCheckClosedInputSet (parseResults, tcInitialState)
213+
214+
// make project results
215+
let parseErrors = parseResults |> Array.collect (fun p -> p.Errors)
216+
let typedErrors = tcErrors |> Array.concat
217+
let errors = x.ErrorsByFile (fileNames, [ parseErrors; typedErrors ])
218+
let symbolUses = [] //TODO:
219+
let projectResults = x.MakeProjectResults (projectFileName, parseResults, tcState, errors, symbolUses, Some topAttrs, Some tcImplFiles)
220+
221+
projectResults
222+
223+
/// Parses and checks file in project, will compile and cache all the files up to this one
224+
/// (if not already done before), or fetch them from cache. Returns partial project results,
225+
/// up to and including the file requested. Returns parse and typecheck results containing
226+
/// name resolutions and symbol uses for the file requested only, so intellisense etc. works.
227+
member x.ParseAndCheckFileInProject (fileName: string, projectFileName: string, fileNames: string[], sources: string[]) =
228+
// get files before file
229+
let fileIndex = fileNames |> Array.findIndex ((=) fileName)
230+
let fileNamesBeforeFile = fileNames |> Array.take fileIndex
231+
let sourcesBeforeFile = sources |> Array.take fileIndex
232+
233+
// parse files before file
234+
let parsingOptions = FSharpParsingOptions.FromTcConfig(tcConfig, fileNames, false)
235+
let parseFile (fileName, source) = x.ParseFile (fileName, hash source, lazy source, parsingOptions)
236+
let parseResults = Array.zip fileNamesBeforeFile sourcesBeforeFile |> Array.map parseFile
237+
238+
// type check files before file
239+
let tcState, topAttrs, tcImplFiles, _tcEnvAtEnd, moduleNamesDict, tcErrors =
240+
x.TypeCheckClosedInputSet (parseResults, tcInitialState)
241+
242+
// parse and type check file
243+
let parseFileResults = parseFile (fileName, sources.[fileIndex])
244+
let checkFileResults = x.CheckFile (projectFileName, parseFileResults, tcState, moduleNamesDict)
245+
let (tcResult, _tcErrors), (tcState, _moduleNamesDict) = checkCache.[fileName]
246+
let _tcEnvAtEndFile, topAttrsFile, implFile, _ccuSigForFile = tcResult
247+
248+
// collect errors
249+
let parseErrorsBefore = parseResults |> Array.collect (fun p -> p.Errors)
250+
let typedErrorsBefore = tcErrors |> Array.concat
251+
let newErrors = match checkFileResults with | Some res -> res.Errors | None -> [||]
252+
let errors = x.ErrorsByFile (fileNames, [ parseErrorsBefore; typedErrorsBefore; newErrors ])
253+
254+
// make partial project results
255+
let parseResults = Array.append parseResults [| parseFileResults |]
256+
let tcImplFiles = List.append tcImplFiles (Option.toList implFile)
257+
let topAttrs = CombineTopAttrs topAttrsFile topAttrs
258+
let symbolUses = [] //TODO:
259+
let projectResults = x.MakeProjectResults (projectFileName, parseResults, tcState, errors, symbolUses, Some topAttrs, Some tcImplFiles)
260+
261+
parseFileResults, checkFileResults, projectResults

fcs/build.fsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ let dotnetExePath =
3131
if File.Exists(pathToCli) then
3232
pathToCli
3333
else
34-
DotNetCli.InstallDotNetSDK "2.2.105"
34+
DotNetCli.InstallDotNetSDK "3.0.100"
3535

3636
let runDotnet workingDir args =
3737
let result =
@@ -90,6 +90,10 @@ Target "BuildVersion" (fun _ ->
9090
Shell.Exec("appveyor", sprintf "UpdateBuild -Version \"%s\"" buildVersion) |> ignore
9191
)
9292

93+
Target "BuildTools" (fun _ ->
94+
runDotnet __SOURCE_DIRECTORY__ "build ../src/buildtools/buildtools.proj -v n -c Proto"
95+
)
96+
9397
Target "Build" (fun _ ->
9498
runDotnet __SOURCE_DIRECTORY__ "build ../src/buildtools/buildtools.proj -v n -c Proto"
9599
let fslexPath = __SOURCE_DIRECTORY__ + "/../artifacts/bin/fslex/Proto/netcoreapp2.1/fslex.dll"

0 commit comments

Comments
 (0)