From 1dc6ed2b1fc949b88d4a827ec6bde1159df13af2 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 10:11:01 +0100 Subject: [PATCH 01/11] cleanup --- src/fsharp/AugmentWithHashCompare.fs | 3 - src/fsharp/CheckFormatStrings.fs | 3 - src/fsharp/ConstraintSolver.fs | 3 - src/fsharp/FindUnsolved.fs | 10 -- src/fsharp/InfoReader.fs | 4 - src/fsharp/MethodOverrides.fs | 8 -- src/fsharp/NameResolution.fs | 20 ++-- src/fsharp/NicePrint.fs | 4 - src/fsharp/PatternMatchCompilation.fs | 2 - src/fsharp/PatternMatchCompilation.fsi | 1 - src/fsharp/PostInferenceChecks.fs | 3 - src/fsharp/QuotationPickler.fs | 8 -- src/fsharp/SignatureConformance.fs | 11 -- src/fsharp/TastOps.fs | 105 +++-------------- src/fsharp/TastPickle.fs | 1 - src/fsharp/TcGlobals.fs | 3 - src/fsharp/TypeRelations.fs | 12 -- src/fsharp/tainted.fsi | 11 ++ src/fsharp/tast.fs | 3 +- src/fsharp/vs/Reactor.fs | 28 ++--- src/fsharp/vs/Reactor.fsi | 8 +- src/fsharp/vs/ServiceDeclarationLists.fs | 2 +- src/fsharp/vs/service.fs | 108 +++++++++--------- .../SimplifyNameDiagnosticAnalyzer.fs | 3 + 24 files changed, 113 insertions(+), 251 deletions(-) diff --git a/src/fsharp/AugmentWithHashCompare.fs b/src/fsharp/AugmentWithHashCompare.fs index abb06547223..c0be1b15f34 100644 --- a/src/fsharp/AugmentWithHashCompare.fs +++ b/src/fsharp/AugmentWithHashCompare.fs @@ -3,7 +3,6 @@ /// Generate the hash/compare functions we add to user-defined types by default. module internal Microsoft.FSharp.Compiler.AugmentWithHashCompare -open Internal.Utilities open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.IL @@ -13,8 +12,6 @@ open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger -open Microsoft.FSharp.Compiler.PrettyNaming -open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.Infos diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index 0b5fbe686ed..49d9f19d655 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -4,12 +4,9 @@ module internal Microsoft.FSharp.Compiler.CheckFormatStrings open Internal.Utilities open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.Range -open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.TcGlobals diff --git a/src/fsharp/ConstraintSolver.fs b/src/fsharp/ConstraintSolver.fs index afa44586a7d..6bd237c9ee5 100644 --- a/src/fsharp/ConstraintSolver.fs +++ b/src/fsharp/ConstraintSolver.fs @@ -30,12 +30,10 @@ module internal Microsoft.FSharp.Compiler.ConstraintSolver // //------------------------------------------------------------------------- -open Internal.Utilities open Internal.Utilities.Collections open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler.Ast @@ -51,7 +49,6 @@ open Microsoft.FSharp.Compiler.Rational open Microsoft.FSharp.Compiler.InfoReader open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops -open Microsoft.FSharp.Compiler.Tastops.DebugPrint open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.TypeRelations diff --git a/src/fsharp/FindUnsolved.fs b/src/fsharp/FindUnsolved.fs index 4d2980bed26..6e8dd06b7bf 100644 --- a/src/fsharp/FindUnsolved.fs +++ b/src/fsharp/FindUnsolved.fs @@ -9,22 +9,12 @@ module internal Microsoft.FSharp.Compiler.FindUnsolved open Internal.Utilities open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics -open Microsoft.FSharp.Compiler.Range -open Microsoft.FSharp.Compiler.Ast -open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.TcGlobals -open Microsoft.FSharp.Compiler.Lib -open Microsoft.FSharp.Compiler.Layout -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.TypeRelations -open Microsoft.FSharp.Compiler.Infos type env = Nix diff --git a/src/fsharp/InfoReader.fs b/src/fsharp/InfoReader.fs index 32e39a7c282..7605baca2b9 100644 --- a/src/fsharp/InfoReader.fs +++ b/src/fsharp/InfoReader.fs @@ -4,10 +4,6 @@ /// Select members from a type by name, searching the type hierarchy if needed module internal Microsoft.FSharp.Compiler.InfoReader -open Internal.Utilities - -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library diff --git a/src/fsharp/MethodOverrides.fs b/src/fsharp/MethodOverrides.fs index 074c9586627..6ad2c83fc11 100644 --- a/src/fsharp/MethodOverrides.fs +++ b/src/fsharp/MethodOverrides.fs @@ -3,27 +3,19 @@ /// Primary logic related to method overrides. module internal Microsoft.FSharp.Compiler.MethodOverrides -open Internal.Utilities -open System.Text - open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.Infos open Microsoft.FSharp.Compiler.AccessibilityLogic open Microsoft.FSharp.Compiler.NameResolution -open Microsoft.FSharp.Compiler.PrettyNaming open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.InfoReader open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops -open Microsoft.FSharp.Compiler.Tastops.DebugPrint open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.TypeRelations diff --git a/src/fsharp/NameResolution.fs b/src/fsharp/NameResolution.fs index 0ad469c82cb..c6dd866b250 100644 --- a/src/fsharp/NameResolution.fs +++ b/src/fsharp/NameResolution.fs @@ -1,10 +1,7 @@ // 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. -//------------------------------------------------------------------------- -// Name environment and name resolution -//------------------------------------------------------------------------- - +/// Name environment and name resolution module internal Microsoft.FSharp.Compiler.NameResolution open Internal.Utilities @@ -27,7 +24,6 @@ open Microsoft.FSharp.Compiler.Infos open Microsoft.FSharp.Compiler.AccessibilityLogic open Microsoft.FSharp.Compiler.AttributeChecking open Microsoft.FSharp.Compiler.InfoReader -open Microsoft.FSharp.Compiler.Layout open Microsoft.FSharp.Compiler.PrettyNaming open System.Collections.Generic @@ -4038,9 +4034,7 @@ and ResolvePartialLongIdentToClassOrRecdFieldsImpl (ncenv: NameResolver) (nenv: | _-> [] modsOrNs @ qualifiedFields -(* Determining if an `Item` is resolvable at point by given `plid`. It's optimized by being lazy and early returning according to the given `Item` *) - -let private ResolveCompletionsInTypeForItem (ncenv: NameResolver) nenv m ad statics typ (item: Item) : seq = +let ResolveCompletionsInTypeForItem (ncenv: NameResolver) nenv m ad statics typ (item: Item) : seq = seq { let g = ncenv.g let amap = ncenv.amap @@ -4209,7 +4203,7 @@ let private ResolveCompletionsInTypeForItem (ncenv: NameResolver) nenv m ad stat | _ -> () } -let rec private ResolvePartialLongIdentInTypeForItem (ncenv: NameResolver) nenv m ad statics plid (item: Item) typ = +let rec ResolvePartialLongIdentInTypeForItem (ncenv: NameResolver) nenv m ad statics plid (item: Item) typ = seq { let g = ncenv.g let amap = ncenv.amap @@ -4260,7 +4254,7 @@ let rec private ResolvePartialLongIdentInTypeForItem (ncenv: NameResolver) nenv yield! finfo.FieldType(amap, m) |> ResolvePartialLongIdentInTypeForItem ncenv nenv m ad false rest item } -let rec private ResolvePartialLongIdentInModuleOrNamespaceForItem (ncenv: NameResolver) nenv m ad (modref: ModuleOrNamespaceRef) plid (item: Item) = +let rec ResolvePartialLongIdentInModuleOrNamespaceForItem (ncenv: NameResolver) nenv m ad (modref: ModuleOrNamespaceRef) plid (item: Item) = let g = ncenv.g let mty = modref.ModuleOrNamespaceType @@ -4339,7 +4333,7 @@ let rec private ResolvePartialLongIdentInModuleOrNamespaceForItem (ncenv: NameRe yield! tcref |> generalizedTyconRef |> ResolvePartialLongIdentInTypeForItem ncenv nenv m ad true rest item } -let rec private PartialResolveLookupInModuleOrNamespaceAsModuleOrNamespaceThenLazy f plid (modref: ModuleOrNamespaceRef) = +let rec PartialResolveLookupInModuleOrNamespaceAsModuleOrNamespaceThenLazy f plid (modref: ModuleOrNamespaceRef) = let mty = modref.ModuleOrNamespaceType match plid with | [] -> f modref @@ -4349,7 +4343,7 @@ let rec private PartialResolveLookupInModuleOrNamespaceAsModuleOrNamespaceThenLa PartialResolveLookupInModuleOrNamespaceAsModuleOrNamespaceThenLazy f rest (modref.NestedTyconRef mty) | None -> Seq.empty -let private PartialResolveLongIndentAsModuleOrNamespaceThenLazy (nenv:NameResolutionEnv) plid f = +let PartialResolveLongIndentAsModuleOrNamespaceThenLazy (nenv:NameResolutionEnv) plid f = seq { match plid with | id :: rest -> @@ -4361,7 +4355,7 @@ let private PartialResolveLongIndentAsModuleOrNamespaceThenLazy (nenv:NameResolu | [] -> () } -let rec private GetCompletionForItem (ncenv: NameResolver) (nenv: NameResolutionEnv) m ad plid (item: Item) : seq = +let rec GetCompletionForItem (ncenv: NameResolver) (nenv: NameResolutionEnv) m ad plid (item: Item) : seq = seq { let g = ncenv.g diff --git a/src/fsharp/NicePrint.fs b/src/fsharp/NicePrint.fs index 550887b59a1..4095ab0195c 100755 --- a/src/fsharp/NicePrint.fs +++ b/src/fsharp/NicePrint.fs @@ -6,20 +6,16 @@ module internal Microsoft.FSharp.Compiler.NicePrint -open Internal.Utilities open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Rational open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops -open Microsoft.FSharp.Compiler.Tastops.DebugPrint open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.Infos diff --git a/src/fsharp/PatternMatchCompilation.fs b/src/fsharp/PatternMatchCompilation.fs index fc3024109e6..8a4a51a5def 100644 --- a/src/fsharp/PatternMatchCompilation.fs +++ b/src/fsharp/PatternMatchCompilation.fs @@ -3,7 +3,6 @@ module internal Microsoft.FSharp.Compiler.PatternMatchCompilation open System.Collections.Generic -open Internal.Utilities open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.Internal @@ -14,7 +13,6 @@ open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops -open Microsoft.FSharp.Compiler.Infos open Microsoft.FSharp.Compiler.Tastops.DebugPrint open Microsoft.FSharp.Compiler.PrettyNaming open Microsoft.FSharp.Compiler.TypeRelations diff --git a/src/fsharp/PatternMatchCompilation.fsi b/src/fsharp/PatternMatchCompilation.fsi index b8f7fc3849a..2186401f352 100644 --- a/src/fsharp/PatternMatchCompilation.fsi +++ b/src/fsharp/PatternMatchCompilation.fsi @@ -12,7 +12,6 @@ open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.Range - /// What should the decision tree contain for any incomplete match? type ActionOnFailure = | ThrowIncompleteMatchException diff --git a/src/fsharp/PostInferenceChecks.fs b/src/fsharp/PostInferenceChecks.fs index 1760d1da1f7..61005ab5da4 100644 --- a/src/fsharp/PostInferenceChecks.fs +++ b/src/fsharp/PostInferenceChecks.fs @@ -5,14 +5,12 @@ module internal Microsoft.FSharp.Compiler.PostTypeCheckSemanticChecks open System.Collections.Generic -open Internal.Utilities open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.AccessibilityLogic open Microsoft.FSharp.Compiler.Ast @@ -22,7 +20,6 @@ open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.TcGlobals open Microsoft.FSharp.Compiler.Lib -open Microsoft.FSharp.Compiler.Layout open Microsoft.FSharp.Compiler.Infos open Microsoft.FSharp.Compiler.PrettyNaming open Microsoft.FSharp.Compiler.InfoReader diff --git a/src/fsharp/QuotationPickler.fs b/src/fsharp/QuotationPickler.fs index dfd981c2daf..09e153c14ee 100644 --- a/src/fsharp/QuotationPickler.fs +++ b/src/fsharp/QuotationPickler.fs @@ -1,19 +1,11 @@ // 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. -//------------------------------------------------------------------------- -// Expression and Type Specifications. These are what we save -//------------------------------------------------------------------------- - - module internal Microsoft.FSharp.Compiler.QuotationPickler open System.Text -open Internal.Utilities open Internal.Utilities.Collections -open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.Internal -open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Lib diff --git a/src/fsharp/SignatureConformance.fs b/src/fsharp/SignatureConformance.fs index a70d56f1b43..d999b5f4902 100644 --- a/src/fsharp/SignatureConformance.fs +++ b/src/fsharp/SignatureConformance.fs @@ -4,29 +4,18 @@ /// constraint solving and method overload resolution. module internal Microsoft.FSharp.Compiler.SignatureConformance -open Internal.Utilities open System.Text open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops -open Microsoft.FSharp.Compiler.Tastops.DebugPrint -open Microsoft.FSharp.Compiler.TcGlobals -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.Infos -open Microsoft.FSharp.Compiler.PrettyNaming -open Microsoft.FSharp.Compiler.AccessibilityLogic -open Microsoft.FSharp.Compiler.NameResolution -open Microsoft.FSharp.Compiler.TypeRelations #if EXTENSIONTYPING open Microsoft.FSharp.Compiler.ExtensionTyping diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs index 0e4b50de0d4..a91df19e944 100644 --- a/src/fsharp/TastOps.fs +++ b/src/fsharp/TastOps.fs @@ -33,6 +33,7 @@ open Microsoft.FSharp.Compiler.ExtensionTyping [] type TyparMap<'T> = | TPMap of StampMap<'T> + member tm.Item with get (v: Typar) = let (TPMap m) = tm @@ -1071,13 +1072,13 @@ let mkCond spBind spTarget m ty e1 e2 e3 = primMkCond spBind spTarget spTarget let exprForValRef m vref = Expr.Val(vref,NormalValUse,m) let exprForVal m v = exprForValRef m (mkLocalValRef v) -let gen_mk_local m s ty mut compgen = +let mkLocalAux m s ty mut compgen = let thisv = NewVal(s,m,None,ty,mut,compgen,None,taccessPublic,ValNotInRecScope,None,NormalVal,[],ValInline.Optional,XmlDoc.Empty,false,false,false,false,false,false,None,ParentNone) thisv,exprForVal m thisv -let mkLocal m s ty = gen_mk_local m s ty Immutable false -let mkCompGenLocal m s ty = gen_mk_local m s ty Immutable true -let mkMutableCompGenLocal m s ty = gen_mk_local m s ty Mutable true +let mkLocal m s ty = mkLocalAux m s ty Immutable false +let mkCompGenLocal m s ty = mkLocalAux m s ty Immutable true +let mkMutableCompGenLocal m s ty = mkLocalAux m s ty Mutable true // Type gives return type. For type-lambdas this is the formal return type. @@ -4028,80 +4029,10 @@ let inline accFreeTyvars (opts:FreeVarOptions) f v acc = if ftyvs === ftyvs' then acc else { acc with FreeTyvars = ftyvs' } -#if FREEVARS_IN_TYPES_ANALYSIS -type CheckCachability<'key,'acc>(name,f: FreeVarOptions -> 'key -> 'acc -> bool * 'acc) = - let dict = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference) - let idem = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference) - let closed = System.Collections.Generic.Dictionary<'key,int>(HashIdentity.Reference) - let mutable saved = 0 - do System.AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> - let hist = dict |> Seq.groupBy (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sortBy (fun (n,_) -> n) - let total = hist |> Seq.sumBy (fun (nhits,nels) -> nels) - let totalHits = hist |> Seq.sumBy (fun (nhits,nels) -> nhits * nels) - printfn "*** %s saved %d hits (%g%%) ***" name saved (float saved / float (saved + totalHits) * 100.0) - printfn "*** %s had %d hits total, possible saving %d ***" name totalHits (totalHits - total) - //for (nhits,nels) in hist do - // printfn "%s, %g%% els for %g%% hits had %d hits" name (float nels / float total * 100.0) (float (nels * nhits) / float totalHits * 100.0) nhits - - let hist = idem |> Seq.groupBy (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sortBy (fun (n,_) -> n) - let total = hist |> Seq.sumBy (fun (nhits,nels) -> nels) - let totalHits = hist |> Seq.sumBy (fun (nhits,nels) -> nhits * nels) - printfn "*** %s had %d idempotent hits total, possible saving %d ***" name totalHits (totalHits - total) - //for (nhits,nels) in hist do - // printfn "%s, %g%% els for %g%% hits had %d idempotent hits" name (float nels / float total * 100.0) (float (nels * nhits) / float totalHits * 100.0) nhits - - let hist = closed |> Seq.groupBy (fun (KeyValue(k,v)) -> v) |> Seq.map (fun (n,els) -> (n,Seq.length els)) |> Seq.sortBy (fun (n,_) -> n) - let total = hist |> Seq.sumBy (fun (nhits,nels) -> nels) - let totalHits = hist |> Seq.sumBy (fun (nhits,nels) -> nhits * nels) - printfn "*** %s had %d closed hits total, possible saving %d ***" name totalHits (totalHits - total) - ) - - member cache.Apply(opts,key,acc) = - if not opts.collectInTypes then - saved <- saved + 1 - acc - else - let cls,res = f opts key acc - if opts.canCache then - if dict.ContainsKey key then - dict.[key] <- dict.[key] + 1 - else - dict.[key] <- 1 - if res === acc then - if idem.ContainsKey key then - idem.[key] <- idem.[key] + 1 - else - idem.[key] <- 1 - if cls then - if closed.ContainsKey key then - closed.[key] <- closed.[key] + 1 - else - closed.[key] <- 1 - res - - - //member cache.OnExit() = - -let accFreeVarsInTy_cache = CheckCachability("accFreeVarsInTy", (fun opts ty fvs -> (freeInType opts ty === emptyFreeTyvars), accFreeTyvars opts (accFreeInType opts) ty fvs)) -let accFreevarsInValCache = CheckCachability("accFreevarsInVal", (fun opts v fvs -> (freeInVal opts v === emptyFreeTyvars), accFreeTyvars opts (accFreeInVal opts) v fvs)) -let accFreeVarsInTys_cache = CheckCachability("accFreeVarsInTys", (fun opts tys fvs -> (freeInTypes opts tys === emptyFreeTyvars), accFreeTyvars opts (accFreeInTypes opts) tys fvs)) -let accFreevarsInTyconCache = CheckCachability("accFreevarsInTycon", (fun opts tys fvs -> false,accFreeTyvars opts (accFreeTycon opts) tys fvs)) - -let accFreeVarsInTy opts ty fvs = accFreeVarsInTy_cache.Apply(opts,ty,fvs) -let accFreeVarsInTys opts tys fvs = - if isNil tys then fvs else accFreeVarsInTys_cache.Apply(opts,tys,fvs) -let accFreevarsInTycon opts (tcr:TyconRef) acc = - match tcr.IsLocalRef with - | true -> accFreevarsInTyconCache.Apply(opts,tcr,acc) - | _ -> acc -let accFreevarsInVal opts v fvs = accFreevarsInValCache.Apply(opts,v,fvs) -#else - let accFreeVarsInTy opts ty acc = accFreeTyvars opts accFreeInType ty acc let accFreeVarsInTys opts tys acc = if isNil tys then acc else accFreeTyvars opts accFreeInTypes tys acc let accFreevarsInTycon opts tcref acc = accFreeTyvars opts accFreeTycon tcref acc let accFreevarsInVal opts v acc = accFreeTyvars opts accFreeInVal v acc -#endif let accFreeVarsInTraitSln opts tys acc = accFreeTyvars opts accFreeInTraitSln tys acc @@ -5744,10 +5675,10 @@ let mkArray (argty, args, m) = Expr.Op(TOp.Array, [argty],args,m) //--------------------------------------------------------------------------- let rec IterateRecursiveFixups g (selfv : Val option) rvs ((access : Expr),set) exprToFix = - let exprToFix = stripExpr exprToFix - match exprToFix with - | Expr.Const _ -> () - | Expr.Op (TOp.Tuple tupInfo,argtys,args,m) when not (evalTupInfoIsStruct tupInfo) -> + let exprToFix = stripExpr exprToFix + match exprToFix with + | Expr.Const _ -> () + | Expr.Op (TOp.Tuple tupInfo,argtys,args,m) when not (evalTupInfoIsStruct tupInfo) -> args |> List.iteri (fun n -> IterateRecursiveFixups g None rvs (mkTupleFieldGet g (tupInfo,access,argtys,n,m), @@ -5756,7 +5687,7 @@ let rec IterateRecursiveFixups g (selfv : Val option) rvs ((access : Expr),set) errorR(Error(FSComp.SR.tastRecursiveValuesMayNotBeInConstructionOfTuple(),m)); e))) - | Expr.Op (TOp.UnionCase (c),tinst,args,m) -> + | Expr.Op (TOp.UnionCase (c),tinst,args,m) -> args |> List.iteri (fun n -> IterateRecursiveFixups g None rvs (mkUnionCaseFieldGetUnprovenViaExprAddr (access,c,tinst,n,m), @@ -5767,7 +5698,7 @@ let rec IterateRecursiveFixups g (selfv : Val option) rvs ((access : Expr),set) errorR(Error(FSComp.SR.tastRecursiveValuesMayNotAppearInConstructionOfType(tcref.LogicalName),m)); mkUnionCaseFieldSet (access,c,tinst,n,e,m)))) - | Expr.Op (TOp.Recd (_,tcref),tinst,args,m) -> + | Expr.Op (TOp.Recd (_,tcref),tinst,args,m) -> (tcref.TrueInstanceFieldsAsRefList, args) ||> List.iter2 (fun fref arg -> let fspec = fref.RecdField IterateRecursiveFixups g None rvs @@ -5777,13 +5708,13 @@ let rec IterateRecursiveFixups g (selfv : Val option) rvs ((access : Expr),set) if not fspec.IsMutable && not (entityRefInThisAssembly g.compilingFslib tcref) then errorR(Error(FSComp.SR.tastRecursiveValuesMayNotBeAssignedToNonMutableField(fspec.rfield_id.idText, tcref.LogicalName),m)); mkRecdFieldSetViaExprAddr (access,fref,tinst,e,m))) arg ) - | Expr.Val _ - | Expr.Lambda _ - | Expr.Obj _ - | Expr.TyChoose _ - | Expr.TyLambda _ -> - rvs selfv access set exprToFix - | _ -> () + | Expr.Val _ + | Expr.Lambda _ + | Expr.Obj _ + | Expr.TyChoose _ + | Expr.TyLambda _ -> + rvs selfv access set exprToFix + | _ -> () diff --git a/src/fsharp/TastPickle.fs b/src/fsharp/TastPickle.fs index af745c40bd9..336a10268a8 100755 --- a/src/fsharp/TastPickle.fs +++ b/src/fsharp/TastPickle.fs @@ -11,7 +11,6 @@ open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics -open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX.Types open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.Lib.Bits diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 9c5a5ec5306..52f9ba02bd2 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -13,14 +13,11 @@ open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX -open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.Ast -open Microsoft.FSharp.Compiler.ErrorLogger -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.PrettyNaming diff --git a/src/fsharp/TypeRelations.fs b/src/fsharp/TypeRelations.fs index 17ff9e953b9..da7ccae5f9c 100755 --- a/src/fsharp/TypeRelations.fs +++ b/src/fsharp/TypeRelations.fs @@ -4,28 +4,16 @@ /// constraint solving and method overload resolution. module internal Microsoft.FSharp.Compiler.TypeRelations -open Internal.Utilities -open System.Text - open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.AbstractIL -open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics -open Microsoft.FSharp.Compiler.Range -open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.ErrorLogger open Microsoft.FSharp.Compiler.Tast open Microsoft.FSharp.Compiler.Tastops open Microsoft.FSharp.Compiler.Tastops.DebugPrint open Microsoft.FSharp.Compiler.TcGlobals -open Microsoft.FSharp.Compiler.AbstractIL.IL -open Microsoft.FSharp.Compiler.Lib open Microsoft.FSharp.Compiler.Infos open Microsoft.FSharp.Compiler.PrettyNaming -open Microsoft.FSharp.Compiler.AccessibilityLogic -open Microsoft.FSharp.Compiler.NameResolution //------------------------------------------------------------------------- // a :> b without coercion based on finalized (no type variable) types diff --git a/src/fsharp/tainted.fsi b/src/fsharp/tainted.fsi index 5e00f95b317..acba3359570 100644 --- a/src/fsharp/tainted.fsi +++ b/src/fsharp/tainted.fsi @@ -17,15 +17,22 @@ type internal TypeProviderError = /// creates new instance of TypeProviderError that represents one error new : (int * string) * string * range -> TypeProviderError + /// creates new instance of TypeProviderError that represents collection of errors new : int * string * range * seq -> TypeProviderError + member Number : int + member Range : range + member ContextualErrorMessage : string + /// creates new instance of TypeProviderError with specified type\method names member WithContext : string * string -> TypeProviderError + /// creates new instance of TypeProviderError based on current instance information(message) member MapText : (string -> int * string) * string * range -> TypeProviderError + /// provides uniform way to process aggregated errors member Iter : (TypeProviderError -> unit) -> unit @@ -87,14 +94,18 @@ type internal Tainted<'T> = [] module internal Tainted = + /// Test whether the tainted value is null val (|Null|_|) : Tainted<'T> -> unit option when 'T : null + /// Test whether the tainted value equals given value. /// Failure in call to equality operation will be blamed on type provider of first operand val Eq : Tainted<'T> -> 'T -> bool when 'T : equality + /// Test whether the tainted value equals given value. Type providers are ignored (equal tainted values produced by different type providers are equal) /// Failure in call to equality operation will be blamed on type provider of first operand val EqTainted : Tainted<'T> -> Tainted<'T> -> bool when 'T : equality and 'T : not struct + /// Compute the hash value for the tainted value val GetHashCodeTainted : Tainted<'T> -> int when 'T : equality diff --git a/src/fsharp/tast.fs b/src/fsharp/tast.fs index eadd784b58c..b1e0c902c26 100755 --- a/src/fsharp/tast.fs +++ b/src/fsharp/tast.fs @@ -14,7 +14,6 @@ open Microsoft.FSharp.Compiler.AbstractIL open Microsoft.FSharp.Compiler.AbstractIL.IL open Microsoft.FSharp.Compiler.AbstractIL.Internal open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics open Microsoft.FSharp.Compiler.AbstractIL.Extensions.ILX.Types open Microsoft.FSharp.Compiler @@ -34,8 +33,10 @@ open Microsoft.FSharp.Core.CompilerServices /// Unique name generator for stamps attached to lambdas and object expressions type Unique = int64 + //++GLOBAL MUTABLE STATE (concurrency-safe) let newUnique = let i = ref 0L in fun () -> System.Threading.Interlocked.Increment(i) + type Stamp = int64 /// Unique name generator for stamps attached to to val_specs, tycon_specs etc. diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index b97b083dd92..ffe9a8896f1 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -13,15 +13,15 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library /// Represents the capability to schedule work in the compiler service operations queue for the compilation thread type internal IReactorOperations = - abstract EnqueueAndAwaitOpAsync : string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> - abstract EnqueueOp: string * (CompilationThreadToken -> unit) -> unit + abstract EnqueueAndAwaitOpAsync : string * string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + abstract EnqueueOp: string * string * (CompilationThreadToken -> unit) -> unit [] type internal ReactorCommands = /// Kick off a build. | SetBackgroundOp of (CompilationThreadToken -> bool) option /// Do some work not synchronized in the mailbox. - | Op of string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) + | Op of string * string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) /// Finish the background building | WaitForBackgroundOpCompletion of AsyncReplyChannel /// Finish all the queued ops @@ -70,16 +70,16 @@ type Reactor() = | Some (SetBackgroundOp bgOpOpt) -> Trace.TraceInformation("Reactor: --> set background op, remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) return! loop (bgOpOpt, onComplete, false) - | Some (Op (desc, ct, op, ccont)) -> + | Some (Op (opName, opArg, ct, op, ccont)) -> if ct.IsCancellationRequested then ccont() else - Trace.TraceInformation("Reactor: --> {0}, remaining {1}, mem {2}, gc2 {3}", desc, inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> {0} ({1}), remaining {2}, mem {3}, gc2 {4}", opName, opArg, inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) let time = Stopwatch() time.Start() op ctok time.Stop() let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- {0}, remaining {1}, took {2}ms", desc, inbox.CurrentQueueLength, span.TotalMilliseconds) + Trace.TraceInformation("Reactor: <-- {0}, remaining {1}, took {2}ms", opName, inbox.CurrentQueueLength, span.TotalMilliseconds) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> Trace.TraceInformation("Reactor: --> wait for background (debug only), remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) @@ -121,13 +121,13 @@ type Reactor() = Trace.TraceInformation("Reactor: enqueue start background, length {0}", builder.CurrentQueueLength) builder.Post(SetBackgroundOp build) - member r.EnqueueOp(desc, op) = - Trace.TraceInformation("Reactor: enqueue {0}, length {1}", desc, builder.CurrentQueueLength) - builder.Post(Op(desc, CancellationToken.None, op, (fun () -> ()))) + member r.EnqueueOp(opName, opArg, op) = + Trace.TraceInformation("Reactor: enqueue {0} ({1}), length {2}", opName, opArg, builder.CurrentQueueLength) + builder.Post(Op(opName, opArg, CancellationToken.None, op, (fun () -> ()))) - member r.EnqueueOpPrim(desc, ct, op, ccont) = - Trace.TraceInformation("Reactor: enqueue {0}, length {1}", desc, builder.CurrentQueueLength) - builder.Post(Op(desc, ct, op, ccont)) + member r.EnqueueOpPrim(opName, opArg, ct, op, ccont) = + Trace.TraceInformation("Reactor: enqueue {0} ({1}), length {2}", opName, opArg, builder.CurrentQueueLength) + builder.Post(Op(opName, opArg, ct, op, ccont)) member r.CurrentQueueLength = builder.CurrentQueueLength @@ -142,11 +142,11 @@ type Reactor() = Trace.TraceInformation("Reactor: enqueue wait for all ops, length {0}", builder.CurrentQueueLength) builder.PostAndReply CompleteAllQueuedOps - member r.EnqueueAndAwaitOpAsync (desc, f) = + member r.EnqueueAndAwaitOpAsync (opName, opArg, f) = async { let! ct = Async.CancellationToken let resultCell = AsyncUtil.AsyncResultCell<_>() - r.EnqueueOpPrim(desc, ct, + r.EnqueueOpPrim(opName, opArg, ct, op=(fun ctok -> let result = try diff --git a/src/fsharp/vs/Reactor.fsi b/src/fsharp/vs/Reactor.fsi index 4311238962b..1d6860932c5 100755 --- a/src/fsharp/vs/Reactor.fsi +++ b/src/fsharp/vs/Reactor.fsi @@ -10,10 +10,10 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library type internal IReactorOperations = /// Put the operation in the queue, and return an async handle to its result. - abstract EnqueueAndAwaitOpAsync : description: string * action: (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + abstract EnqueueAndAwaitOpAsync : opName: string * descFilename: string * action: (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> /// Enqueue an operation and return immediately. - abstract EnqueueOp: description: string * action: (CompilationThreadToken -> unit) -> unit + abstract EnqueueOp: opName: string * descFilename: string * action: (CompilationThreadToken -> unit) -> unit /// Reactor is intended for long-running but interruptible operations, interleaved /// with one-off asynchronous operations. @@ -35,13 +35,13 @@ type internal Reactor = member CompleteAllQueuedOps : unit -> unit /// Enqueue an uncancellable operation and return immediately. - member EnqueueOp : description: string * op:(CompilationThreadToken -> unit) -> unit + member EnqueueOp : opName: string * opArg: string * op:(CompilationThreadToken -> unit) -> unit /// For debug purposes member CurrentQueueLength : int /// Put the operation in the queue, and return an async handle to its result. - member EnqueueAndAwaitOpAsync : description: string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + member EnqueueAndAwaitOpAsync : opName: string * opArg: string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> /// The timespan in milliseconds before background work begins after the operations queue is empty member PauseBeforeBackgroundWork : int with get, set diff --git a/src/fsharp/vs/ServiceDeclarationLists.fs b/src/fsharp/vs/ServiceDeclarationLists.fs index 50e41d84089..4e58fd59004 100644 --- a/src/fsharp/vs/ServiceDeclarationLists.fs +++ b/src/fsharp/vs/ServiceDeclarationLists.fs @@ -484,7 +484,7 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin match info with | Choice1Of2 (items: CompletionItem list, infoReader, m, denv, reactor:IReactorOperations, checkAlive) -> // reactor causes the lambda to execute on the background compiler thread, through the Reactor - reactor.EnqueueAndAwaitOpAsync ("StructuredDescriptionTextAsync", fun ctok -> + reactor.EnqueueAndAwaitOpAsync ("StructuredDescriptionTextAsync", name, fun ctok -> RequireCompilationThread ctok // This is where we do some work which may touch TAST data structures owned by the IncrementalBuilder - infoReader, item etc. // It is written to be robust to a disposal of an IncrementalBuilder, in which case it will just return the empty string. diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index a86127392f5..fe5919d15a2 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -43,7 +43,7 @@ open Internal.Utilities open Internal.Utilities.Collections open Microsoft.FSharp.Compiler.Layout.TaggedTextOps -type internal Layout = Internal.Utilities.StructuredFormat.Layout +type internal Layout = StructuredFormat.Layout [] module EnvMisc = @@ -63,7 +63,7 @@ module EnvMisc = /// (via an Eventually<_> value of case NotYetDone) that can be called as the next piece of work. let maxTimeShareMilliseconds = match System.Environment.GetEnvironmentVariable("FCS_MaxTimeShare") with - | null | "" -> 50L + | null | "" -> 100L | s -> int64 s @@ -881,8 +881,8 @@ type TypeCheckInfo let (nenv, ad), m = GetBestEnvForPos cursorPos NameResolution.GetVisibleNamespacesAndModulesAtPoint ncenv nenv m ad - member __.IsRelativeNameResolvable(cursorPos: pos, plid: string list, item: Item) : bool = /// Determines if a long ident is resolvable at a specific point. + member __.IsRelativeNameResolvable(cursorPos: pos, plid: string list, item: Item) : bool = ErrorScope.Protect Range.range0 (fun () -> @@ -891,9 +891,6 @@ type TypeCheckInfo NameResolution.IsItemResolvable ncenv nenv m ad plid item) (fun _ -> false) - //let items = NameResolution.ResolvePartialLongIdent ncenv nenv (fun _ _ -> true) m ad plid true - //items |> List.exists (ItemsAreEffectivelyEqual g item) - /// Get the auto-complete items at a location member __.GetDeclarations (ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, getAllSymbols, hasTextChangedSinceLastTypecheck) = let isInterfaceFile = SourceFileImpl.IsInterfaceFile mainInputFileName @@ -1534,7 +1531,7 @@ module internal Parser = |> Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled maxTimeShareMilliseconds ct (fun ctok f -> f ctok) |> Eventually.forceAsync (fun work -> - reactorOps.EnqueueAndAwaitOpAsync("TypeCheckOneFile", + reactorOps.EnqueueAndAwaitOpAsync("TypeCheckOneFile.Fragment", mainInputFileName, fun ctok -> // This work is not cancellable let res = @@ -1646,7 +1643,7 @@ type FSharpProjectContext(thisCcu: CcuThunk, assemblies: FSharpAssembly list, ad [] // 'details' is an option because the creation of the tcGlobals etc. for the project may have failed. -type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string list) option, reactorOps: IReactorOperations) = +type FSharpCheckProjectResults(projectFileName, keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string list) option, reactorOps: IReactorOperations) = let getDetails() = match details with @@ -1674,7 +1671,7 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], member info.GetUsesOfSymbol(symbol:FSharpSymbol) = let (tcGlobals, _tcImports, _thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() // This probably doesn't need to be run on the reactor since all data touched by GetUsesOfSymbol is immutable. - reactorOps.EnqueueAndAwaitOpAsync("GetUsesOfSymbol", fun ctok -> + reactorOps.EnqueueAndAwaitOpAsync("GetUsesOfSymbol", projectFileName, fun ctok -> DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok [| for r in tcSymbolUses do yield! r.GetUsesOfSymbol(symbol.Item) |] @@ -1688,7 +1685,7 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], member info.GetAllUsesOfAllSymbols() = let (tcGlobals, tcImports, thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() // This probably doesn't need to be run on the reactor since all data touched by GetAllUsesOfSymbols is immutable. - reactorOps.EnqueueAndAwaitOpAsync("GetAllUsesOfAllSymbols", fun ctok -> + reactorOps.EnqueueAndAwaitOpAsync("GetAllUsesOfAllSymbols", projectFileName, fun ctok -> DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok [| for r in tcSymbolUses do @@ -1722,7 +1719,7 @@ type FSharpCheckProjectResults(keepAssemblyContents, errors: FSharpErrorInfo[], // // There is an important property of all the objects returned by the methods of this type: they do not require // the corresponding background builder to be alive. That is, they are simply plain-old-data through pre-formatting of all result text. -type FSharpCheckFileResults(errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo option, dependencyFiles: string list, builderX: IncrementalBuilder option, reactorOpsX:IReactorOperations) = +type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo option, dependencyFiles: string list, builderX: IncrementalBuilder option, reactorOpsX:IReactorOperations) = // This may be None initially, or may be set to None when the object is disposed or finalized let mutable details = match scopeOptX with None -> None | Some scopeX -> Some (scopeX, builderX, reactorOpsX) @@ -1743,13 +1740,13 @@ type FSharpCheckFileResults(errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo | Some (_,_,reactor) -> // Make sure we run disposal in the reactor thread, since it may trigger type provider disposals etc. details <- None - reactor.EnqueueOp ("Dispose", fun ctok -> + reactor.EnqueueOp ("FSharpCheckFileResults.DecrementUsageCountOnIncrementalBuilder", filename, fun ctok -> RequireCompilationThread ctok decrementer.Dispose()) | _ -> () // Run an operation that needs to access a builder and be run in the reactor thread - let reactorOp desc dflt f = + let reactorOp opName dflt f = async { match details with | None -> @@ -1760,7 +1757,7 @@ type FSharpCheckFileResults(errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo | Some (scope, builderOpt, reactor) -> // Increment the usage count to ensure the builder doesn't get released while running operations asynchronously. use _unwind = IncrementalBuilder.KeepBuilderAlive builderOpt - let! res = reactor.EnqueueAndAwaitOpAsync(desc, fun ctok -> f ctok scope |> cancellable.Return) + let! res = reactor.EnqueueAndAwaitOpAsync(opName, filename, fun ctok -> f ctok scope |> cancellable.Return) return res } @@ -1892,11 +1889,12 @@ type FSharpCheckFileResults(errors: FSharpErrorInfo[], scopeOptX: TypeCheckInfo yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) member info.GetVisibleNamespacesAndModulesAtPoint(pos: pos) : Async = - reactorOp "GetVisibleNamespacesAndModulesAtPoint" [| |] (fun _ctok scope -> scope.GetVisibleNamespacesAndModulesAtPosition(pos) |> List.toArray) + reactorOp "GetVisibleNamespacesAndModulesAtPoint" [| |] (fun _ctok scope -> + scope.GetVisibleNamespacesAndModulesAtPosition(pos) |> List.toArray) member info.IsRelativeNameResolvable(pos: pos, plid: string list, item: Item) : Async = reactorOp "IsRelativeNameResolvable" true (fun ctok scope -> - DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok + RequireCompilationThread ctok scope.IsRelativeNameResolvable(pos, plid, item)) @@ -2080,8 +2078,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let mutable implicitlyStartBackgroundWork = true let reactorOps = { new IReactorOperations with - member __.EnqueueAndAwaitOpAsync (desc, op) = reactor.EnqueueAndAwaitOpAsync (desc, op) - member __.EnqueueOp (desc, op) = reactor.EnqueueOp (desc, op) } + member __.EnqueueAndAwaitOpAsync (opName, opArg, op) = reactor.EnqueueAndAwaitOpAsync (opName, opArg, op) + member __.EnqueueOp (opName, opArg, op) = reactor.EnqueueOp (opName, opArg, op) } // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache /// Information about the derived script closure. @@ -2209,10 +2207,10 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent static let mutable foregroundParseCount = 0 static let mutable foregroundTypeCheckCount = 0 - let MakeCheckFileResultsEmpty(creationErrors) = - FSharpCheckFileResults (Array.ofList creationErrors, None, [], None, reactorOps) + let MakeCheckFileResultsEmpty(filename, creationErrors) = + FSharpCheckFileResults (filename, Array.ofList creationErrors, None, [], None, reactorOps) - let MakeCheckFileResults(options:FSharpProjectOptions, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors) = + let MakeCheckFileResults(filename, options:FSharpProjectOptions, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors) = let errors = [| yield! creationErrors yield! parseErrors @@ -2221,12 +2219,12 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent else yield! tcErrors |] - FSharpCheckFileResults (errors, Some scope, dependencyFiles, Some builder, reactorOps) + FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, Some builder, reactorOps) - let MakeCheckFileAnswer(tcFileResult, options:FSharpProjectOptions, builder, dependencyFiles, creationErrors, parseErrors, tcErrors) = + let MakeCheckFileAnswer(filename, tcFileResult, options:FSharpProjectOptions, builder, dependencyFiles, creationErrors, parseErrors, tcErrors) = match tcFileResult with | Parser.TypeCheckAborted.Yes -> FSharpCheckFileAnswer.Aborted - | Parser.TypeCheckAborted.No scope -> FSharpCheckFileAnswer.Succeeded(MakeCheckFileResults(options, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors)) + | Parser.TypeCheckAborted.No scope -> FSharpCheckFileAnswer.Succeeded(MakeCheckFileResults(filename, options, builder, scope, dependencyFiles, creationErrors, parseErrors, tcErrors)) member bc.RecordTypeCheckFileInProjectResults(filename,options,parseResults,fileVersion,priorTimeStamp,checkAnswer,source) = match checkAnswer with @@ -2253,7 +2251,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> async.Return parseResults | _ -> - reactor.EnqueueAndAwaitOpAsync("ParseFileInProject " + filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync("ParseFileInProject ", filename, fun ctok -> cancellable { // Try the caches again - it may have been filled by the time this operation runs match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with @@ -2281,7 +2279,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) member bc.GetBackgroundParseResultsForFileInProject(filename, options) = - reactor.EnqueueAndAwaitOpAsync("GetBackgroundParseResultsForFileInProject " + filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync("GetBackgroundParseResultsForFileInProject ", filename, fun ctok -> cancellable { let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options) use _unwind = decrement @@ -2295,7 +2293,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent ) member bc.MatchBraces(filename:string, source, options)= - reactor.EnqueueAndAwaitOpAsync("MatchBraces " + filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync("MatchBraces ", filename, fun ctok -> cancellable { let! builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) use _unwind = decrement @@ -2368,7 +2366,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let! tcErrors, tcFileResult = Parser.TypeCheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo) - let checkAnswer = MakeCheckFileAnswer(tcFileResult, options, builder, tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) + let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) bc.RecordTypeCheckFileInProjectResults(fileName, options, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source) return checkAnswer finally @@ -2386,7 +2384,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. member bc.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo: obj option) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProjectAllowingStaleCachedResults " + filename, action >> cancellable.Return) + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProjectAllowingStaleCachedResults ", filename, action >> cancellable.Return) async { try let! cachedResults = @@ -2417,12 +2415,12 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProject " + filename, action) + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProject", filename, action) async { let! builderOpt,creationErrors, decrement = execWithReactorAsync <| fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options) use _unwind = decrement match builderOpt with - | None -> return FSharpCheckFileAnswer.Succeeded (MakeCheckFileResultsEmpty(creationErrors)) + | None -> return FSharpCheckFileAnswer.Succeeded (MakeCheckFileResultsEmpty(filename, creationErrors)) | Some builder -> // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date let cachedResults = bc.GetCachedCheckFileResult(builder, filename, source, options) @@ -2438,7 +2436,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Parses and checks the source file and returns untyped AST and check results. member bc.ParseAndCheckFileInProject(filename:string, fileVersion, source, options:FSharpProjectOptions,textSnapshotInfo) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("ParseAndCheckFileInProject " + filename, action) + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("ParseAndCheckFileInProject", filename, action) async { let! builderOpt,creationErrors,decrement = execWithReactorAsync <| fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options) use _unwind = decrement @@ -2469,15 +2467,15 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent } /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) - member bc.GetBackgroundCheckResultsForFileInProject(filename,options) = - reactor.EnqueueAndAwaitOpAsync("GetBackgroundCheckResultsForFileInProject " + filename, fun ctok -> + member bc.GetBackgroundCheckResultsForFileInProject(filename, options) = + reactor.EnqueueAndAwaitOpAsync("GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> cancellable { let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options) use _unwind = decrement match builderOpt with | None -> let parseResults = FSharpParseFileResults(Array.ofList creationErrors, None, true, []) - let typedResults = MakeCheckFileResultsEmpty(creationErrors) + let typedResults = MakeCheckFileResultsEmpty(filename, creationErrors) return (parseResults, typedResults) | Some builder -> let! (inputOpt, _, _, untypedErrors) = builder.GetParseResultsForFile (ctok, filename) @@ -2493,7 +2491,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent List.last tcProj.TcSymbolUses, tcProj.TcEnvAtEnd.NameEnv, loadClosure, reactorOps, (fun () -> builder.IsAlive), None) - let typedResults = MakeCheckFileResults(options, builder, scope, tcProj.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) + let typedResults = MakeCheckFileResults(filename, options, builder, scope, tcProj.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) return (parseResults, typedResults) }) @@ -2515,11 +2513,11 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent use _unwind = decrement match builderOpt with | None -> - return FSharpCheckProjectResults (keepAssemblyContents, Array.ofList creationErrors, None, reactorOps) + return FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, Array.ofList creationErrors, None, reactorOps) | Some builder -> let! (tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt) = builder.GetCheckResultsAndImplementationsForProject(ctok) let errors = [| yield! creationErrors; yield! ErrorHelpers.CreateErrorInfos (tcProj.TcConfig, true, Microsoft.FSharp.Compiler.TcGlobals.DummyFileNameForRangesWithoutASpecificLocation, tcProj.Errors) |] - return FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, tcProj.TcDependencyFiles), reactorOps) + return FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, errors, Some(tcProj.TcGlobals, tcProj.TcImports, tcProj.TcState.Ccu, tcProj.TcState.PartialAssemblySignature, tcProj.TcSymbolUses, tcProj.TopAttribs, tcAssemblyDataOpt, ilAssemRef, tcProj.TcEnvAtEnd.AccessRights, tcAssemblyExprOpt, tcProj.TcDependencyFiles), reactorOps) } /// Get the timestamp that would be on the output if fully built immediately @@ -2535,7 +2533,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Keep the projet builder alive over a scope member bc.KeepProjectAlive(options) = - reactor.EnqueueAndAwaitOpAsync("KeepProjectAlive " + options.ProjectFileName, fun ctok -> + reactor.EnqueueAndAwaitOpAsync("KeepProjectAlive", options.ProjectFileName, fun ctok -> cancellable { let! _builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) return decrement @@ -2543,10 +2541,10 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Parse and typecheck the whole project. member bc.ParseAndCheckProject(options) = - reactor.EnqueueAndAwaitOpAsync("ParseAndCheckProject " + options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok)) + reactor.EnqueueAndAwaitOpAsync("ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok)) member bc.GetProjectOptionsFromScript(filename, source, ?loadedTimeStamp, ?otherFlags, ?useFsiAuxLib, ?assumeDotNetFramework, ?extraProjectInfo: obj, ?optionsStamp: int64) = - reactor.EnqueueAndAwaitOpAsync ("GetProjectOptionsFromScript " + filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync ("GetProjectOptionsFromScript", filename, fun ctok -> cancellable { use errors = new ErrorScope() // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? @@ -2592,7 +2590,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent member bc.InvalidateConfiguration(options : FSharpProjectOptions, ?startBackgroundCompile) = let startBackgroundCompile = defaultArg startBackgroundCompile implicitlyStartBackgroundWork // This operation can't currently be cancelled nor awaited - reactor.EnqueueOp("InvalidateConfiguration", fun ctok -> + reactor.EnqueueOp("InvalidateConfiguration", options.ProjectFileName, fun ctok -> // If there was a similar entry then re-establish an empty builder . This is a somewhat arbitrary choice - it // will have the effect of releasing memory associated with the previous builder, but costs some time. if incrementalBuildersCache.ContainsSimilarKey (ctok, options) then @@ -2607,7 +2605,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent bc.CheckProjectInBackground(options)) member bc.NotifyProjectCleaned (options : FSharpProjectOptions) = - reactor.EnqueueAndAwaitOpAsync("NotifyProjectCleaned", fun ctok -> + reactor.EnqueueAndAwaitOpAsync("NotifyProjectCleaned", options.ProjectFileName, fun ctok -> cancellable { // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous @@ -2649,7 +2647,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent member bc.CurrentQueueLength = reactor.CurrentQueueLength member bc.ClearCachesAsync () = - reactor.EnqueueAndAwaitOpAsync ("ClearCachesAsync", fun ctok -> + reactor.EnqueueAndAwaitOpAsync ("ClearCachesAsync", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.Clear ltok parseAndCheckFileInProjectCache.Clear ltok @@ -2660,7 +2658,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent cancellable.Return ()) member bc.DownsizeCaches() = - reactor.EnqueueAndAwaitOpAsync ("DownsizeCaches", fun ctok -> + reactor.EnqueueAndAwaitOpAsync ("DownsizeCaches", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) parseAndCheckFileInProjectCache.Resize(ltok, keepStrongly=1) @@ -2748,14 +2746,14 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke backgroundCompiler.TryGetRecentCheckResultsForFile(filename,options,source) member ic.Compile(argv: string[]) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", fun ctok -> + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", "", fun ctok -> cancellable { return CompileHelpers.compileFromArgs (ctok, argv, referenceResolver, None, None) } ) member ic.Compile (ast:ParsedInput list, assemblyName:string, outFile:string, dependencies:string list, ?pdbFile:string, ?executable:bool, ?noframework:bool) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", fun ctok -> + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", assemblyName, fun ctok -> cancellable { let noframework = defaultArg noframework false return CompileHelpers.compileFromAsts (ctok, referenceResolver, ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None) @@ -2763,7 +2761,7 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke ) member ic.CompileToDynamicAssembly (otherFlags: string[], execute: (TextWriter * TextWriter) option) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", fun ctok -> + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", "", fun ctok -> cancellable { CompileHelpers.setOutputStreams execute @@ -2790,7 +2788,7 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke ) member ic.CompileToDynamicAssembly (asts:ParsedInput list, assemblyName:string, dependencies:string list, execute: (TextWriter * TextWriter) option, ?debug:bool, ?noframework:bool) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", fun ctok -> + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", assemblyName, fun ctok -> cancellable { CompileHelpers.setOutputStreams execute @@ -2967,10 +2965,10 @@ type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tc member __.ParseAndCheckInteraction (ctok, source) = async { - let mainInputFileName = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") + let filename = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") // Note: projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true (which it is in this case). let projectSourceFiles = [ ] - let parseErrors, _matchPairs, inputOpt, anyErrors = Parser.ParseOneFile (ctok, source, false, true, mainInputFileName, projectSourceFiles, tcConfig) + let parseErrors, _matchPairs, inputOpt, anyErrors = Parser.ParseOneFile (ctok, source, false, true, filename, projectSourceFiles, tcConfig) let dependencyFiles = [] // interactions have no dependencies let parseResults = FSharpParseFileResults(parseErrors, inputOpt, parseHadErrors = anyErrors, dependencyFiles = dependencyFiles) @@ -2982,17 +2980,17 @@ type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tc let fsiCompilerOptions = CompileOptions.GetCoreFsiCompilerOptions tcConfigB CompileOptions.ParseCompilerOptions (ignore, fsiCompilerOptions, [ ]) - let loadClosure = LoadClosure.ComputeClosureOfSourceText(ctok, referenceResolver, defaultFSharpBinariesDir, mainInputFileName, source, CodeContext.Editing, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework) + let loadClosure = LoadClosure.ComputeClosureOfSourceText(ctok, referenceResolver, defaultFSharpBinariesDir, filename, source, CodeContext.Editing, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework) let! tcErrors, tcFileResult = - Parser.TypeCheckOneFile(parseResults,source,mainInputFileName,"project",tcConfig,tcGlobals,tcImports, tcState, + Parser.TypeCheckOneFile(parseResults,source,filename,"project",tcConfig,tcGlobals,tcImports, tcState, Some loadClosure,backgroundDiagnostics,reactorOps,(fun () -> true),None) return match tcFileResult with | Parser.TypeCheckAborted.No scope -> let errors = [| yield! parseErrors; yield! tcErrors |] - let typeCheckResults = FSharpCheckFileResults (errors, Some scope, dependencyFiles, None, reactorOps) - let projectResults = FSharpCheckProjectResults (keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps) + let typeCheckResults = FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, None, reactorOps) + let projectResults = FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps) parseResults, typeCheckResults, projectResults | _ -> failwith "unexpected aborted" diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 6260503299f..1ce4876d223 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -26,6 +26,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker let getPlidLength (plid: string list) = (plid |> List.sumBy String.length) + plid.Length static let cache = ConditionalWeakTable>() + // Make sure only one document is being analyzed at a time, to be nice static let guard = new SemaphoreSlim(1) static let Descriptor = @@ -54,6 +55,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = match cache.TryGetValue document.Id with | true, (oldTextVersionHash, diagnostics) when oldTextVersionHash = textVersionHash -> return diagnostics | _ -> + do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start let! sourceText = document.GetTextAsync() let checker = getChecker document let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true) @@ -83,6 +85,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = let getNecessaryPlid (plid: string list) : Async = let rec loop (rest: string list) (current: string list) = async { + do! Async.Sleep 10 // be less intrusive, give other work priority most of the time match rest with | [] -> return current | headIdent :: restPlid -> From 4eff4aabe099c24f01caef59ac039ddf31776315 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 10:11:31 +0100 Subject: [PATCH 02/11] cleanup --- src/fsharp/vs/service.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index fe5919d15a2..f8715c8fd1d 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -2990,7 +2990,7 @@ type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tc | Parser.TypeCheckAborted.No scope -> let errors = [| yield! parseErrors; yield! tcErrors |] let typeCheckResults = FSharpCheckFileResults (filename, errors, Some scope, dependencyFiles, None, reactorOps) - let projectResults = FSharpCheckProjectResults (options.ProjectFileName, keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps) + let projectResults = FSharpCheckProjectResults (filename, keepAssemblyContents, errors, Some(tcGlobals, tcImports, scope.ThisCcu, scope.CcuSig, [scope.ScopeSymbolUses], None, None, mkSimpleAssRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles), reactorOps) parseResults, typeCheckResults, projectResults | _ -> failwith "unexpected aborted" From 6314e9119183f8a8ac6cb6fabb1664e6ab7ff300 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 16:54:30 +0100 Subject: [PATCH 03/11] diagnostics --- src/fsharp/vs/Reactor.fs | 28 +- src/fsharp/vs/Reactor.fsi | 8 +- src/fsharp/vs/ServiceDeclarationLists.fs | 3 +- src/fsharp/vs/service.fs | 463 +++++++++--------- src/fsharp/vs/service.fsi | 121 +++-- .../Classification/ColorizationService.fs | 3 +- .../CodeFix/AddOpenCodeFixProvider.fs | 4 +- .../ImplementInterfaceCodeFixProvider.fs | 3 +- .../CodeFix/ProposeUppercaseLabel.fs | 3 +- .../CodeFix/RenameUnusedValue.fs | 4 +- .../Commands/HelpContextService.fs | 3 +- .../Commands/XmlDocCommandService.fs | 5 +- .../Completion/CompletionProvider.fs | 14 +- .../FSharp.Editor/Completion/SignatureHelp.fs | 3 +- .../Debugging/BreakpointResolutionService.fs | 3 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 5 +- .../SimplifyNameDiagnosticAnalyzer.fs | 5 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 3 +- .../UnusedOpensDiagnosticAnalyzer.fs | 3 +- .../DocumentHighlightsService.fs | 4 +- .../Formatting/BraceMatchingService.fs | 3 +- .../InlineRename/InlineRenameService.fs | 7 +- .../FSharpCheckerExtensions.fs | 32 +- .../LanguageService/SymbolHelpers.fs | 10 +- .../Navigation/FindUsagesService.fs | 12 +- .../Navigation/GoToDefinitionService.fs | 16 +- .../Navigation/NavigationBarItemService.fs | 3 +- .../QuickInfo/QuickInfoProvider.fs | 8 +- .../Structure/BlockStructureService.fs | 4 +- .../unittests/GoToDefinitionServiceTests.fs | 4 +- 30 files changed, 440 insertions(+), 347 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index ffe9a8896f1..f093943f2c5 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -13,15 +13,15 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library /// Represents the capability to schedule work in the compiler service operations queue for the compilation thread type internal IReactorOperations = - abstract EnqueueAndAwaitOpAsync : string * string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> - abstract EnqueueOp: string * string * (CompilationThreadToken -> unit) -> unit + abstract EnqueueAndAwaitOpAsync : userOpName:string * opName:string * opArg:string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + abstract EnqueueOp: userOpName:string * opName:string * opArg:string * (CompilationThreadToken -> unit) -> unit [] type internal ReactorCommands = /// Kick off a build. | SetBackgroundOp of (CompilationThreadToken -> bool) option /// Do some work not synchronized in the mailbox. - | Op of string * string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) + | Op of userOpName: string * opName: string * opArg: string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) /// Finish the background building | WaitForBackgroundOpCompletion of AsyncReplyChannel /// Finish all the queued ops @@ -70,16 +70,16 @@ type Reactor() = | Some (SetBackgroundOp bgOpOpt) -> Trace.TraceInformation("Reactor: --> set background op, remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) return! loop (bgOpOpt, onComplete, false) - | Some (Op (opName, opArg, ct, op, ccont)) -> + | Some (Op (userOpName, opName, opArg, ct, op, ccont)) -> if ct.IsCancellationRequested then ccont() else - Trace.TraceInformation("Reactor: --> {0} ({1}), remaining {2}, mem {3}, gc2 {4}", opName, opArg, inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> {0}.{1} ({2}), remaining {3}, mem {4}, gc2 {5}", userOpName, opName, opArg, inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) let time = Stopwatch() time.Start() op ctok time.Stop() let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- {0}, remaining {1}, took {2}ms", opName, inbox.CurrentQueueLength, span.TotalMilliseconds) + Trace.TraceInformation("Reactor: <-- {0}.{1} ({2}), remaining {3}, took {4}ms", userOpName, opName, opArg, inbox.CurrentQueueLength, span.TotalMilliseconds) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> Trace.TraceInformation("Reactor: --> wait for background (debug only), remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) @@ -121,13 +121,13 @@ type Reactor() = Trace.TraceInformation("Reactor: enqueue start background, length {0}", builder.CurrentQueueLength) builder.Post(SetBackgroundOp build) - member r.EnqueueOp(opName, opArg, op) = - Trace.TraceInformation("Reactor: enqueue {0} ({1}), length {2}", opName, opArg, builder.CurrentQueueLength) - builder.Post(Op(opName, opArg, CancellationToken.None, op, (fun () -> ()))) + member r.EnqueueOp(userOpName, opName, opArg, op) = + Trace.TraceInformation("Reactor: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) + builder.Post(Op(userOpName, opName, opArg, CancellationToken.None, op, (fun () -> ()))) - member r.EnqueueOpPrim(opName, opArg, ct, op, ccont) = - Trace.TraceInformation("Reactor: enqueue {0} ({1}), length {2}", opName, opArg, builder.CurrentQueueLength) - builder.Post(Op(opName, opArg, ct, op, ccont)) + member r.EnqueueOpPrim(userOpName, opName, opArg, ct, op, ccont) = + Trace.TraceInformation("Reactor: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) + builder.Post(Op(userOpName, opName, opArg, ct, op, ccont)) member r.CurrentQueueLength = builder.CurrentQueueLength @@ -142,11 +142,11 @@ type Reactor() = Trace.TraceInformation("Reactor: enqueue wait for all ops, length {0}", builder.CurrentQueueLength) builder.PostAndReply CompleteAllQueuedOps - member r.EnqueueAndAwaitOpAsync (opName, opArg, f) = + member r.EnqueueAndAwaitOpAsync (userOpName, opName, opArg, f) = async { let! ct = Async.CancellationToken let resultCell = AsyncUtil.AsyncResultCell<_>() - r.EnqueueOpPrim(opName, opArg, ct, + r.EnqueueOpPrim(userOpName, opName, opArg, ct, op=(fun ctok -> let result = try diff --git a/src/fsharp/vs/Reactor.fsi b/src/fsharp/vs/Reactor.fsi index 1d6860932c5..336cafcde39 100755 --- a/src/fsharp/vs/Reactor.fsi +++ b/src/fsharp/vs/Reactor.fsi @@ -10,10 +10,10 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library type internal IReactorOperations = /// Put the operation in the queue, and return an async handle to its result. - abstract EnqueueAndAwaitOpAsync : opName: string * descFilename: string * action: (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + abstract EnqueueAndAwaitOpAsync : userOpName:string * opName:string * opArg:string * action: (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> /// Enqueue an operation and return immediately. - abstract EnqueueOp: opName: string * descFilename: string * action: (CompilationThreadToken -> unit) -> unit + abstract EnqueueOp: userOpName:string * opName:string * opArg:string * action: (CompilationThreadToken -> unit) -> unit /// Reactor is intended for long-running but interruptible operations, interleaved /// with one-off asynchronous operations. @@ -35,13 +35,13 @@ type internal Reactor = member CompleteAllQueuedOps : unit -> unit /// Enqueue an uncancellable operation and return immediately. - member EnqueueOp : opName: string * opArg: string * op:(CompilationThreadToken -> unit) -> unit + member EnqueueOp : userOpName:string * opName: string * opArg: string * op:(CompilationThreadToken -> unit) -> unit /// For debug purposes member CurrentQueueLength : int /// Put the operation in the queue, and return an async handle to its result. - member EnqueueAndAwaitOpAsync : opName: string * opArg: string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> + member EnqueueAndAwaitOpAsync : userOpName:string * opName:string * opArg:string * (CompilationThreadToken -> Cancellable<'T>) -> Async<'T> /// The timespan in milliseconds before background work begins after the operations queue is empty member PauseBeforeBackgroundWork : int with get, set diff --git a/src/fsharp/vs/ServiceDeclarationLists.fs b/src/fsharp/vs/ServiceDeclarationLists.fs index 4e58fd59004..0bef14be78d 100644 --- a/src/fsharp/vs/ServiceDeclarationLists.fs +++ b/src/fsharp/vs/ServiceDeclarationLists.fs @@ -481,10 +481,11 @@ type FSharpDeclarationListItem(name: string, nameInCode: string, fullName: strin member __.NameInCode = nameInCode member __.StructuredDescriptionTextAsync = + let userOpName = "ToolTip" match info with | Choice1Of2 (items: CompletionItem list, infoReader, m, denv, reactor:IReactorOperations, checkAlive) -> // reactor causes the lambda to execute on the background compiler thread, through the Reactor - reactor.EnqueueAndAwaitOpAsync ("StructuredDescriptionTextAsync", name, fun ctok -> + reactor.EnqueueAndAwaitOpAsync (userOpName, "StructuredDescriptionTextAsync", name, fun ctok -> RequireCompilationThread ctok // This is where we do some work which may touch TAST data structures owned by the IncrementalBuilder - infoReader, item etc. // It is written to be robust to a disposal of an IncrementalBuilder, in which case it will just return the empty string. diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index f8715c8fd1d..3e2fabfb737 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -132,10 +132,10 @@ type SemanticClassificationType = | Operator | Disposable -// A scope represents everything we get back from the typecheck of a file. -// It acts like an in-memory database about the file. -// It is effectively immutable and not updated: when we re-typecheck we just drop the previous -// scope object on the floor and make a new one. +/// A TypeCheckInfo represents everything we get back from the typecheck of a file. +/// It acts like an in-memory database about the file. +/// It is effectively immutable and not updated: when we re-typecheck we just drop the previous +/// scope object on the floor and make a new one. [] type TypeCheckInfo (// Information corresponding to miscellaneous command-line options (--define, etc). @@ -1138,11 +1138,9 @@ type TypeCheckInfo [ for x in tcImports.GetImportedAssemblies() do yield FSharpAssembly(g, tcImports, x.FSharpViewOfMetadata) ] - // Note, this does not have to be a SyncOp, it can be called from any thread member __.GetFormatSpecifierLocationsAndArity() = sSymbolUses.GetFormatSpecifierLocationsAndArity() - // Not, this does not have to be a SyncOp, it can be called from any thread member __.GetSemanticClassification(range: range option) : (range * SemanticClassificationType) [] = let (|LegitTypeOccurence|_|) = function | ItemOccurence.UseInType @@ -1247,13 +1245,26 @@ type TypeCheckInfo |> Seq.toArray |> Array.append (sSymbolUses.GetFormatSpecifierLocationsAndArity() |> Array.map (fun m -> fst m, SemanticClassificationType.Printf)) + /// The resolutions in the file member __.ScopeResolutions = sResolutions + + /// The uses of symbols in the analyzed file member __.ScopeSymbolUses = sSymbolUses + member __.TcGlobals = g + member __.TcImports = tcImports + + /// The inferred signature of the file member __.CcuSig = ccuSig + + /// The assembly being analyzed member __.ThisCcu = thisCcu + override __.ToString() = "TypeCheckInfo(" + mainInputFileName + ")" + + + module internal Parser = // We'll need number of lines for adjusting error messages at EOF @@ -1429,7 +1440,8 @@ module internal Parser = reactorOps: IReactorOperations, // Used by 'FSharpDeclarationListInfo' to check the IncrementalBuilder is still alive. checkAlive : (unit -> bool), - textSnapshotInfo : obj option) = + textSnapshotInfo : obj option, + userOpName: string) = async { match parseResults.ParseTree with @@ -1531,7 +1543,7 @@ module internal Parser = |> Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled maxTimeShareMilliseconds ct (fun ctok f -> f ctok) |> Eventually.forceAsync (fun work -> - reactorOps.EnqueueAndAwaitOpAsync("TypeCheckOneFile.Fragment", mainInputFileName, + reactorOps.EnqueueAndAwaitOpAsync(userOpName, "TypeCheckOneFile.Fragment", mainInputFileName, fun ctok -> // This work is not cancellable let res = @@ -1573,7 +1585,7 @@ module internal Parser = return errors, TypeCheckAborted.Yes } -type UnresolvedReferencesSet = UnresolvedReferencesSet of UnresolvedAssemblyReference list +type UnresolvedReferencesSet = UnresolvedReferencesSet of UnresolvedAssemblyReference list // NOTE: may be better just to move to optional arguments here type FSharpProjectOptions = @@ -1620,16 +1632,7 @@ type FSharpProjectOptions = /// Compute the project directory. member po.ProjectDirectory = System.IO.Path.GetDirectoryName(po.ProjectFileName) - override this.ToString() = - let files = - let sb = new StringBuilder() - this.SourceFiles |> Array.iter (fun file -> sb.AppendFormat(" {0}\n", file) |> ignore) - sb.ToString() - let options = - let sb = new StringBuilder() - this.OtherOptions |> Array.iter (fun op -> sb.AppendFormat("{0} ", op) |> ignore) - sb.ToString() - sprintf "OtherOptions(%s)\n Files:\n%s Options: %s" this.ProjectFileName files options + override this.ToString() = "FSharpProjectOptions(" + this.ProjectFileName + ")" [] @@ -1643,7 +1646,7 @@ type FSharpProjectContext(thisCcu: CcuThunk, assemblies: FSharpAssembly list, ad [] // 'details' is an option because the creation of the tcGlobals etc. for the project may have failed. -type FSharpCheckProjectResults(projectFileName, keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string list) option, reactorOps: IReactorOperations) = +type FSharpCheckProjectResults(projectFileName:string, keepAssemblyContents, errors: FSharpErrorInfo[], details:(TcGlobals*TcImports*CcuThunk*ModuleOrNamespaceType*TcSymbolUses list*TopAttribs option*CompileOps.IRawFSharpAssemblyData option * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string list) option, _reactorOps: IReactorOperations) = let getDetails() = match details with @@ -1670,30 +1673,24 @@ type FSharpCheckProjectResults(projectFileName, keepAssemblyContents, errors: FS // Not, this does not have to be a SyncOp, it can be called from any thread member info.GetUsesOfSymbol(symbol:FSharpSymbol) = let (tcGlobals, _tcImports, _thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() - // This probably doesn't need to be run on the reactor since all data touched by GetUsesOfSymbol is immutable. - reactorOps.EnqueueAndAwaitOpAsync("GetUsesOfSymbol", projectFileName, fun ctok -> - DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - - [| for r in tcSymbolUses do yield! r.GetUsesOfSymbol(symbol.Item) |] - |> Seq.distinctBy (fun (itemOcc,_denv,m) -> itemOcc, m) - |> Seq.filter (fun (itemOcc,_,_) -> itemOcc <> ItemOccurence.RelatedText) - |> Seq.map (fun (itemOcc,denv,m) -> FSharpSymbolUse(tcGlobals, denv, symbol, itemOcc, m)) - |> Seq.toArray - |> cancellable.Return) + + [| for r in tcSymbolUses do yield! r.GetUsesOfSymbol(symbol.Item) |] + |> Seq.distinctBy (fun (itemOcc,_denv,m) -> itemOcc, m) + |> Seq.filter (fun (itemOcc,_,_) -> itemOcc <> ItemOccurence.RelatedText) + |> Seq.map (fun (itemOcc,denv,m) -> FSharpSymbolUse(tcGlobals, denv, symbol, itemOcc, m)) + |> Seq.toArray + |> async.Return // Not, this does not have to be a SyncOp, it can be called from any thread member info.GetAllUsesOfAllSymbols() = let (tcGlobals, tcImports, thisCcu, _ccuSig, tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() - // This probably doesn't need to be run on the reactor since all data touched by GetAllUsesOfSymbols is immutable. - reactorOps.EnqueueAndAwaitOpAsync("GetAllUsesOfAllSymbols", projectFileName, fun ctok -> - DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - [| for r in tcSymbolUses do - for (item,itemOcc,denv,m) in r.GetAllUsesOfSymbols() do - if itemOcc <> ItemOccurence.RelatedText then - let symbol = FSharpSymbol.Create(tcGlobals, thisCcu, tcImports, item) - yield FSharpSymbolUse(tcGlobals, denv, symbol, itemOcc, m) |] - |> cancellable.Return) + [| for r in tcSymbolUses do + for (item,itemOcc,denv,m) in r.GetAllUsesOfSymbols() do + if itemOcc <> ItemOccurence.RelatedText then + let symbol = FSharpSymbol.Create(tcGlobals, thisCcu, tcImports, item) + yield FSharpSymbolUse(tcGlobals, denv, symbol, itemOcc, m) |] + |> async.Return member info.ProjectContext = let (tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() @@ -1714,6 +1711,8 @@ type FSharpCheckProjectResults(projectFileName, keepAssemblyContents, errors: FS let (_tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles) = getDetails() ilAssemRef.QualifiedName + override info.ToString() = "FSharpCheckProjectResults(" + projectFileName + ")" + [] /// A live object of this type keeps the background corresponding background builder (and type providers) alive (through reference-counting). // @@ -1740,13 +1739,13 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp | Some (_,_,reactor) -> // Make sure we run disposal in the reactor thread, since it may trigger type provider disposals etc. details <- None - reactor.EnqueueOp ("FSharpCheckFileResults.DecrementUsageCountOnIncrementalBuilder", filename, fun ctok -> + reactor.EnqueueOp ("GCFinalizer","FSharpCheckFileResults.DecrementUsageCountOnIncrementalBuilder", filename, fun ctok -> RequireCompilationThread ctok decrementer.Dispose()) | _ -> () // Run an operation that needs to access a builder and be run in the reactor thread - let reactorOp opName dflt f = + let reactorOp userOpName opName dflt f = async { match details with | None -> @@ -1757,20 +1756,15 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp | Some (scope, builderOpt, reactor) -> // Increment the usage count to ensure the builder doesn't get released while running operations asynchronously. use _unwind = IncrementalBuilder.KeepBuilderAlive builderOpt - let! res = reactor.EnqueueAndAwaitOpAsync(opName, filename, fun ctok -> f ctok scope |> cancellable.Return) + let! res = reactor.EnqueueAndAwaitOpAsync(userOpName, opName, filename, fun ctok -> f ctok scope |> cancellable.Return) return res } // Run an operation that can be called from any thread let threadSafeOp dflt f = match details with - | None -> - dflt() - | Some (_, Some builder, _) when not builder.IsAlive -> - System.Diagnostics.Debug.Assert(false,"unexpected dead builder") - dflt() - | Some (scope, builderOpt, ops) -> - f(scope, builderOpt, ops) + | None -> dflt() + | Some (scope, _builderOpt, _ops) -> f scope // At the moment we only dispose on finalize - we never explicitly dispose these objects. Explicitly disposing is not // really worth much since the underlying project builds are likely to still be in the incrementalBuilder cache. @@ -1781,58 +1775,67 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp member info.HasFullTypeCheckInfo = details.IsSome /// Intellisense autocompletions - member info.GetDeclarationListInfo(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, getAllEntities, ?hasTextChangedSinceLastTypecheck) = + member info.GetDeclarationListInfo(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, getAllEntities, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" let hasTextChangedSinceLastTypecheck = defaultArg hasTextChangedSinceLastTypecheck (fun _ -> false) - reactorOp "GetDeclarations" FSharpDeclarationListInfo.Empty (fun ctok scope -> + reactorOp userOpName "GetDeclarations" FSharpDeclarationListInfo.Empty (fun ctok scope -> scope.GetDeclarations(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, getAllEntities, hasTextChangedSinceLastTypecheck)) - member info.GetDeclarationListSymbols(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, ?hasTextChangedSinceLastTypecheck) = + member info.GetDeclarationListSymbols(parseResultsOpt, line, colAtEndOfNamesAndResidue, lineStr, qualifyingNames, partialName, ?hasTextChangedSinceLastTypecheck, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" let hasTextChangedSinceLastTypecheck = defaultArg hasTextChangedSinceLastTypecheck (fun _ -> false) - reactorOp "GetDeclarationListSymbols" List.empty (fun ctok scope -> scope.GetDeclarationListSymbols(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, hasTextChangedSinceLastTypecheck)) + reactorOp userOpName "GetDeclarationListSymbols" List.empty (fun ctok scope -> scope.GetDeclarationListSymbols(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, hasTextChangedSinceLastTypecheck)) /// Resolve the names at the given location to give a data tip - member info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag) = + member info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpToolTipText [] match tokenTagToTokenId tokenTag with | TOKEN_IDENT -> - reactorOp "GetToolTipText" dflt (fun ctok scope -> scope.GetStructuredToolTipText(ctok, line, lineStr, colAtEndOfNames, names)) + reactorOp userOpName "GetToolTipText" dflt (fun ctok scope -> scope.GetStructuredToolTipText(ctok, line, lineStr, colAtEndOfNames, names)) | TOKEN_STRING | TOKEN_STRING_TEXT -> - reactorOp "GetReferenceResolutionToolTipText" dflt (fun ctok scope -> scope.GetReferenceResolutionStructuredToolTipText(ctok, line, colAtEndOfNames) ) + reactorOp userOpName "GetReferenceResolutionToolTipText" dflt (fun ctok scope -> scope.GetReferenceResolutionStructuredToolTipText(ctok, line, colAtEndOfNames) ) | _ -> async.Return dflt - member info.GetToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag) = - info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag) + member info.GetToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, userOpName) = + info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName=userOpName) |> Tooltips.Map Tooltips.ToFSharpToolTipText - member info.GetF1KeywordAlternate (line, colAtEndOfNames, lineStr, names) = - reactorOp "GetF1Keyword" None (fun ctok scope -> + member info.GetF1KeywordAlternate (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + reactorOp userOpName "GetF1Keyword" None (fun ctok scope -> scope.GetF1Keyword (ctok, line, lineStr, colAtEndOfNames, names)) // Resolve the names at the given location to a set of methods - member info.GetMethodsAlternate(line, colAtEndOfNames, lineStr, names) = + member info.GetMethodsAlternate(line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpMethodGroup("",[| |]) - reactorOp "GetMethods" dflt (fun ctok scope -> + reactorOp userOpName "GetMethods" dflt (fun ctok scope -> scope.GetMethods (ctok, line, lineStr, colAtEndOfNames, names)) - member info.GetDeclarationLocationAlternate (line, colAtEndOfNames, lineStr, names, ?preferFlag) = + member info.GetDeclarationLocationAlternate (line, colAtEndOfNames, lineStr, names, ?preferFlag, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpFindDeclResult.DeclNotFound FSharpFindDeclFailureReason.Unknown - reactorOp "GetDeclarationLocation" dflt (fun ctok scope -> + reactorOp userOpName "GetDeclarationLocation" dflt (fun ctok scope -> scope.GetDeclarationLocation (ctok, line, lineStr, colAtEndOfNames, names, preferFlag)) - member info.GetSymbolUseAtLocation (line, colAtEndOfNames, lineStr, names) = - reactorOp "GetSymbolUseAtLocation" None (fun ctok scope -> + member info.GetSymbolUseAtLocation (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + reactorOp userOpName "GetSymbolUseAtLocation" None (fun ctok scope -> scope.GetSymbolUseAtLocation (ctok, line, lineStr, colAtEndOfNames, names) |> Option.map (fun (sym,denv,m) -> FSharpSymbolUse(scope.TcGlobals,denv,sym,ItemOccurence.Use,m))) - member info.GetMethodsAsSymbols (line, colAtEndOfNames, lineStr, names) = - reactorOp "GetMethodsAsSymbols" None (fun ctok scope -> + member info.GetMethodsAsSymbols (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + reactorOp userOpName "GetMethodsAsSymbols" None (fun ctok scope -> scope.GetMethodsAsSymbols (ctok, line, lineStr, colAtEndOfNames, names) |> Option.map (fun (symbols,denv,m) -> symbols |> List.map (fun sym -> FSharpSymbolUse(scope.TcGlobals,denv,sym,ItemOccurence.Use,m)))) - member info.GetSymbolAtLocationAlternate (line, colAtEndOfNames, lineStr, names) = - reactorOp "GetSymbolUseAtLocation" None (fun ctok scope -> + member info.GetSymbolAtLocationAlternate (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + reactorOp userOpName "GetSymbolUseAtLocation" None (fun ctok scope -> scope.GetSymbolUseAtLocation (ctok, line, lineStr, colAtEndOfNames, names) |> Option.map (fun (sym,_,_) -> sym)) @@ -1842,62 +1845,67 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp member info.GetFormatSpecifierLocationsAndArity() = threadSafeOp (fun () -> [| |]) - (fun (scope, _builder, _reactor) -> + (fun scope -> // This operation is not asynchronous - GetFormatSpecifierLocationsAndArity can be run on the calling thread scope.GetFormatSpecifierLocationsAndArity()) member info.GetSemanticClassification(range: range option) = threadSafeOp (fun () -> [| |]) - (fun (scope, _builder, _reactor) -> - // This operation is not asynchronous - GetExtraColorizations can be run on the calling thread + (fun scope -> + // This operation is not asynchronous - GetSemanticClassification can be run on the calling thread scope.GetSemanticClassification(range)) member info.PartialAssemblySignature = threadSafeOp (fun () -> failwith "not available") - (fun (scope, _builder, _reactor) -> + (fun scope -> // This operation is not asynchronous - PartialAssemblySignature can be run on the calling thread scope.PartialAssemblySignature()) member info.ProjectContext = threadSafeOp (fun () -> failwith "not available") - (fun (scope, _builder, _reactor) -> + (fun scope -> // This operation is not asynchronous - GetReferencedAssemblies can be run on the calling thread FSharpProjectContext(scope.ThisCcu, scope.GetReferencedAssemblies(), scope.AccessRights)) member info.DependencyFiles = dependencyFiles member info.GetAllUsesOfAllSymbolsInFile() = - reactorOp "GetAllUsesOfAllSymbolsInFile" [| |] (fun ctok scope -> - - DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - - [| for (item,itemOcc,denv,m) in scope.ScopeSymbolUses.GetAllUsesOfSymbols() do - if itemOcc <> ItemOccurence.RelatedText then - let symbol = FSharpSymbol.Create(scope.TcGlobals, scope.ThisCcu, scope.TcImports, item) - yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) + threadSafeOp + (fun () -> [| |]) + (fun scope -> + [| for (item,itemOcc,denv,m) in scope.ScopeSymbolUses.GetAllUsesOfSymbols() do + if itemOcc <> ItemOccurence.RelatedText then + let symbol = FSharpSymbol.Create(scope.TcGlobals, scope.ThisCcu, scope.TcImports, item) + yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) + |> async.Return member info.GetUsesOfSymbolInFile(symbol:FSharpSymbol) = - reactorOp "GetUsesOfSymbolInFile" [| |] (fun ctok scope -> - - DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok - - [| for (itemOcc,denv,m) in scope.ScopeSymbolUses.GetUsesOfSymbol(symbol.Item) |> Seq.distinctBy (fun (itemOcc,_denv,m) -> itemOcc, m) do - if itemOcc <> ItemOccurence.RelatedText then - yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) - - member info.GetVisibleNamespacesAndModulesAtPoint(pos: pos) : Async = - reactorOp "GetVisibleNamespacesAndModulesAtPoint" [| |] (fun _ctok scope -> - scope.GetVisibleNamespacesAndModulesAtPosition(pos) |> List.toArray) + threadSafeOp + (fun () -> [| |]) + (fun scope -> + [| for (itemOcc,denv,m) in scope.ScopeSymbolUses.GetUsesOfSymbol(symbol.Item) |> Seq.distinctBy (fun (itemOcc,_denv,m) -> itemOcc, m) do + if itemOcc <> ItemOccurence.RelatedText then + yield FSharpSymbolUse(scope.TcGlobals, denv, symbol, itemOcc, m) |]) + |> async.Return + + member info.GetVisibleNamespacesAndModulesAtPoint(pos: pos) = + threadSafeOp + (fun () -> [| |]) + (fun scope -> scope.GetVisibleNamespacesAndModulesAtPosition(pos) |> List.toArray) + |> async.Return - member info.IsRelativeNameResolvable(pos: pos, plid: string list, item: Item) : Async = - reactorOp "IsRelativeNameResolvable" true (fun ctok scope -> + member info.IsRelativeNameResolvable(pos: pos, plid: string list, item: Item, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + reactorOp userOpName "IsRelativeNameResolvable" true (fun ctok scope -> RequireCompilationThread ctok scope.IsRelativeNameResolvable(pos, plid, item)) + override info.ToString() = "FSharpCheckFileResults(" + filename + ")" + //---------------------------------------------------------------------------- // BackgroundCompiler // @@ -2078,8 +2086,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let mutable implicitlyStartBackgroundWork = true let reactorOps = { new IReactorOperations with - member __.EnqueueAndAwaitOpAsync (opName, opArg, op) = reactor.EnqueueAndAwaitOpAsync (opName, opArg, op) - member __.EnqueueOp (opName, opArg, op) = reactor.EnqueueOp (opName, opArg, op) } + member __.EnqueueAndAwaitOpAsync (userOpName, opName, opArg, op) = reactor.EnqueueAndAwaitOpAsync (userOpName, opName, opArg, op) + member __.EnqueueOp (userOpName, opName, opArg, op) = reactor.EnqueueOp (userOpName, opName, opArg, op) } // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.backgroundCompiler.scriptClosureCache /// Information about the derived script closure. @@ -2093,7 +2101,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// CreateOneIncrementalBuilder (for background type checking). Note that fsc.fs also /// creates an incremental builder used by the command line compiler. - let CreateOneIncrementalBuilder (ctok, options:FSharpProjectOptions) = + let CreateOneIncrementalBuilder (ctok, options:FSharpProjectOptions, userOpName) = cancellable { let projectReferences = @@ -2101,11 +2109,11 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent { new IProjectReference with member x.EvaluateRawContents(ctok) = cancellable { - let! r = self.ParseAndCheckProjectImpl(opts, ctok) + let! r = self.ParseAndCheckProjectImpl(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") return r.RawFSharpAssemblyData } member x.TryGetLogicalTimeStamp(cache, ctok) = - self.TryGetLogicalTimeStampForProject(cache, ctok, opts) + self.TryGetLogicalTimeStampForProject(cache, ctok, opts, userOpName + ".TimeStampReferencedProject("+nm+")") member x.FileName = nm } ] let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) @@ -2125,7 +2133,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent // Register the behaviour that responds to CCUs being invalidated because of type // provider Invalidate events. This invalidates the configuration in the build. builder.ImportedCcusInvalidated.Add (fun _ -> - self.InvalidateConfiguration options) + self.InvalidateConfiguration(options, None, userOpName)) // Register the callback called just before a file is typechecked by the background builder (without recording // errors or intellisense information). @@ -2153,7 +2161,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent requiredToKeep=(fun (builderOpt,_,_) -> match builderOpt with None -> false | Some (b:IncrementalBuilder) -> b.IsBeingKeptAliveApartFromCacheEntry), onDiscard = (fun (_, _, decrement:IDisposable) -> decrement.Dispose())) - let getOrCreateBuilderAndKeepAlive (ctok, options) = + let getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) = cancellable { RequireCompilationThread ctok match incrementalBuildersCache.TryGet (ctok, options) with @@ -2161,7 +2169,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let decrement = IncrementalBuilder.KeepBuilderAlive builderOpt return builderOpt,creationErrors, decrement | None -> - let! (builderOpt,creationErrors,_) as info = CreateOneIncrementalBuilder (ctok, options) + let! (builderOpt,creationErrors,_) as info = CreateOneIncrementalBuilder (ctok, options, userOpName) incrementalBuildersCache.Set (ctok, options, info) let decrement = IncrementalBuilder.KeepBuilderAlive builderOpt return builderOpt, creationErrors, decrement @@ -2237,12 +2245,12 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent parseAndCheckFileInProjectCache.Set(ltok, (filename,source,options),(parseResults,typedResults,fileVersion,priorTimeStamp)) parseFileInProjectCache.Set(ltok, (filename,source,options),parseResults)) - member bc.ImplicitlyStartCheckProjectInBackground(options) = + member bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) = if implicitlyStartBackgroundWork then - bc.CheckProjectInBackground(options) + bc.CheckProjectInBackground(options, userOpName + ".ImplicitlyStartCheckProjectInBackground") /// Parses the source file and returns untyped AST - member bc.ParseFileInProject(filename:string, source,options:FSharpProjectOptions) = + member bc.ParseFileInProject(filename:string, source,options:FSharpProjectOptions, userOpName) = match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with | Some parseResults -> async.Return parseResults | None -> @@ -2251,7 +2259,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> async.Return parseResults | _ -> - reactor.EnqueueAndAwaitOpAsync("ParseFileInProject ", filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseFileInProject.CacheMiss", filename, fun ctok -> cancellable { // Try the caches again - it may have been filled by the time this operation runs match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with @@ -2262,7 +2270,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent | Some (parseResults, _checkResults,_,_) -> return parseResults | _ -> foregroundParseCount <- foregroundParseCount + 1 - let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with | None -> return FSharpParseFileResults(List.toArray creationErrors, None, true, []) @@ -2278,10 +2286,10 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent ) /// Fetch the parse information from the background compiler (which checks w.r.t. the FileSystem API) - member bc.GetBackgroundParseResultsForFileInProject(filename, options) = - reactor.EnqueueAndAwaitOpAsync("GetBackgroundParseResultsForFileInProject ", filename, fun ctok -> + member bc.GetBackgroundParseResultsForFileInProject(filename, options, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundParseResultsForFileInProject ", filename, fun ctok -> cancellable { - let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with | None -> return FSharpParseFileResults(List.toArray creationErrors, None, true, []) @@ -2292,10 +2300,10 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent } ) - member bc.MatchBraces(filename:string, source, options)= - reactor.EnqueueAndAwaitOpAsync("MatchBraces ", filename, fun ctok -> + member bc.MatchBraces(filename:string, source, options, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "MatchBraces ", filename, fun ctok -> cancellable { - let! builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with | None -> return [| |] @@ -2345,7 +2353,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent fileVersion : int, builder : IncrementalBuilder, tcPrior : PartialCheckResults, - creationErrors : FSharpErrorInfo list) = + creationErrors : FSharpErrorInfo list, + userOpName: string) = async { let beingCheckedFileKey = fileName, options, fileVersion @@ -2365,7 +2374,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) let! tcErrors, tcFileResult = Parser.TypeCheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, - tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo) + tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo, userOpName) let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) bc.RecordTypeCheckFileInProjectResults(fileName, options, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source) return checkAnswer @@ -2383,8 +2392,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent } /// Type-check the result obtained by parsing, but only if the antecedent type checking context is available. - member bc.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo: obj option) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProjectAllowingStaleCachedResults ", filename, action >> cancellable.Return) + member bc.CheckFileInProjectAllowingStaleCachedResults(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo: obj option, userOpName) = + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProjectAllowingStaleCachedResults ", filename, action >> cancellable.Return) async { try let! cachedResults = @@ -2406,18 +2415,18 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename match tcPrior with | Some tcPrior -> - let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors) + let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) return Some checkResults | None -> return None // the incremental builder was not up to date finally - bc.ImplicitlyStartCheckProjectInBackground(options) + bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) } /// Type-check the result obtained by parsing. Force the evaluation of the antecedent type checking context if needed. - member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("CheckFileInProject", filename, action) + member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo, userOpName) = + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProject", filename, action) async { - let! builderOpt,creationErrors, decrement = execWithReactorAsync <| fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt,creationErrors, decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) use _unwind = decrement match builderOpt with | None -> return FSharpCheckFileAnswer.Succeeded (MakeCheckFileResultsEmpty(filename, creationErrors)) @@ -2429,16 +2438,16 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) - let! checkAnswer = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors) - bc.ImplicitlyStartCheckProjectInBackground(options) + let! checkAnswer = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) return checkAnswer } /// Parses and checks the source file and returns untyped AST and check results. - member bc.ParseAndCheckFileInProject(filename:string, fileVersion, source, options:FSharpProjectOptions,textSnapshotInfo) = - let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync("ParseAndCheckFileInProject", filename, action) + member bc.ParseAndCheckFileInProject (filename:string, fileVersion, source, options:FSharpProjectOptions, textSnapshotInfo, userOpName) = + let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckFileInProject", filename, action) async { - let! builderOpt,creationErrors,decrement = execWithReactorAsync <| fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt,creationErrors,decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) use _unwind = decrement match builderOpt with | None -> @@ -2461,16 +2470,16 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent Parser.ParseOneFile (ctok, source, false, true, filename, builder.SourceFiles, builder.TcConfig) |> cancellable.Return let parseResults = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors) - bc.ImplicitlyStartCheckProjectInBackground(options) + let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) return parseResults, checkResults } /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) - member bc.GetBackgroundCheckResultsForFileInProject(filename, options) = - reactor.EnqueueAndAwaitOpAsync("GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> + member bc.GetBackgroundCheckResultsForFileInProject(filename, options, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "GetBackgroundCheckResultsForFileInProject", filename, fun ctok -> cancellable { - let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt, creationErrors, decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with | None -> @@ -2497,19 +2506,19 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// Try to get recent approximate type check results for a file. - member bc.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, source) = + member bc.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, source, _userOpName: string) = match source with | Some sourceText -> - parseCacheLock.AcquireLock (fun ctok -> - match parseAndCheckFileInProjectCache.TryGet(ctok,(filename,sourceText,options)) with + parseCacheLock.AcquireLock (fun ltok -> + match parseAndCheckFileInProjectCache.TryGet(ltok,(filename,sourceText,options)) with | Some (a,b,c,_) -> Some (a,b,c) | None -> None) - | None -> parseCacheLock.AcquireLock (fun ctok -> parseAndCheckFileInProjectCachePossiblyStale.TryGet(ctok,(filename,options))) + | None -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.TryGet(ltok,(filename,options))) /// Parse and typecheck the whole project (the implementation, called recursively as project graph is evaluated) - member private bc.ParseAndCheckProjectImpl(options, ctok) : Cancellable = + member private bc.ParseAndCheckProjectImpl(options, ctok, userOpName) : Cancellable = cancellable { - let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement match builderOpt with | None -> @@ -2521,30 +2530,30 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent } /// Get the timestamp that would be on the output if fully built immediately - member private bc.TryGetLogicalTimeStampForProject(cache, ctok, options) = + member private bc.TryGetLogicalTimeStampForProject(cache, ctok, options, userOpName: string) = // NOTE: This creation of the background builder is currently run as uncancellable. Creating background builders is generally // cheap though the timestamp computations look suspicious for transitive project references. - let builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) |> Cancellable.runWithoutCancellation + let builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName + ".LogicalTimeStamp") |> Cancellable.runWithoutCancellation use _unwind = decrement match builderOpt with | None -> None | Some builder -> Some (builder.GetLogicalTimeStampForProject(cache, ctok)) /// Keep the projet builder alive over a scope - member bc.KeepProjectAlive(options) = - reactor.EnqueueAndAwaitOpAsync("KeepProjectAlive", options.ProjectFileName, fun ctok -> + member bc.KeepProjectAlive(options, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "KeepProjectAlive", options.ProjectFileName, fun ctok -> cancellable { - let! _builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) + let! _builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) return decrement }) /// Parse and typecheck the whole project. - member bc.ParseAndCheckProject(options) = - reactor.EnqueueAndAwaitOpAsync("ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok)) + member bc.ParseAndCheckProject(options, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckProject", options.ProjectFileName, fun ctok -> bc.ParseAndCheckProjectImpl(options, ctok, userOpName)) - member bc.GetProjectOptionsFromScript(filename, source, ?loadedTimeStamp, ?otherFlags, ?useFsiAuxLib, ?assumeDotNetFramework, ?extraProjectInfo: obj, ?optionsStamp: int64) = - reactor.EnqueueAndAwaitOpAsync ("GetProjectOptionsFromScript", filename, fun ctok -> + member bc.GetProjectOptionsFromScript(filename, source, loadedTimeStamp, otherFlags, useFsiAuxLib: bool option, assumeDotNetFramework: bool option, extraProjectInfo: obj option, optionsStamp: int64 option, userOpName) = + reactor.EnqueueAndAwaitOpAsync (userOpName, "GetProjectOptionsFromScript", filename, fun ctok -> cancellable { use errors = new ErrorScope() // Do we add a reference to FSharp.Compiler.Interactive.Settings by default? @@ -2587,25 +2596,25 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent return options, errors.Diagnostics }) - member bc.InvalidateConfiguration(options : FSharpProjectOptions, ?startBackgroundCompile) = + member bc.InvalidateConfiguration(options : FSharpProjectOptions, startBackgroundCompile, userOpName) = let startBackgroundCompile = defaultArg startBackgroundCompile implicitlyStartBackgroundWork // This operation can't currently be cancelled nor awaited - reactor.EnqueueOp("InvalidateConfiguration", options.ProjectFileName, fun ctok -> + reactor.EnqueueOp(userOpName, "InvalidateConfiguration", options.ProjectFileName, fun ctok -> // If there was a similar entry then re-establish an empty builder . This is a somewhat arbitrary choice - it // will have the effect of releasing memory associated with the previous builder, but costs some time. if incrementalBuildersCache.ContainsSimilarKey (ctok, options) then // We do not need to decrement here - the onDiscard function is called each time an entry is pushed out of the build cache, // including by incrementalBuildersCache.Set. - let newBuilderInfo = CreateOneIncrementalBuilder (ctok, options) |> Cancellable.runWithoutCancellation + let newBuilderInfo = CreateOneIncrementalBuilder (ctok, options, userOpName) |> Cancellable.runWithoutCancellation incrementalBuildersCache.Set(ctok, options, newBuilderInfo) // Start working on the project. Also a somewhat arbitrary choice if startBackgroundCompile then - bc.CheckProjectInBackground(options)) + bc.CheckProjectInBackground(options, userOpName)) - member bc.NotifyProjectCleaned (options : FSharpProjectOptions) = - reactor.EnqueueAndAwaitOpAsync("NotifyProjectCleaned", options.ProjectFileName, fun ctok -> + member bc.NotifyProjectCleaned (options : FSharpProjectOptions, userOpName) = + reactor.EnqueueAndAwaitOpAsync(userOpName, "NotifyProjectCleaned", options.ProjectFileName, fun ctok -> cancellable { // If there was a similar entry (as there normally will have been) then re-establish an empty builder . This // is a somewhat arbitrary choice - it will have the effect of releasing memory associated with the previous @@ -2613,14 +2622,14 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent if incrementalBuildersCache.ContainsSimilarKey (ctok, options) then // We do not need to decrement here - the onDiscard function is called each time an entry is pushed out of the build cache, // including by incrementalBuildersCache.Set. - let! newBuilderInfo = CreateOneIncrementalBuilder (ctok, options) + let! newBuilderInfo = CreateOneIncrementalBuilder (ctok, options, userOpName) incrementalBuildersCache.Set(ctok, options, newBuilderInfo) }) - member bc.CheckProjectInBackground (options) = + member bc.CheckProjectInBackground (options, userOpName) = reactor.SetBackgroundOp (Some (fun ctok -> // The creation of the background builder can't currently be cancelled - let builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options) |> Cancellable.runWithoutCancellation + let builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) |> Cancellable.runWithoutCancellation use _unwind = decrement match builderOpt with | None -> false @@ -2646,8 +2655,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent member bc.CurrentQueueLength = reactor.CurrentQueueLength - member bc.ClearCachesAsync () = - reactor.EnqueueAndAwaitOpAsync ("ClearCachesAsync", "", fun ctok -> + member bc.ClearCachesAsync (userOpName) = + reactor.EnqueueAndAwaitOpAsync (userOpName, "ClearCachesAsync", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.Clear ltok parseAndCheckFileInProjectCache.Clear ltok @@ -2657,8 +2666,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.Clear ltok) cancellable.Return ()) - member bc.DownsizeCaches() = - reactor.EnqueueAndAwaitOpAsync ("DownsizeCaches", "", fun ctok -> + member bc.DownsizeCaches(userOpName) = + reactor.EnqueueAndAwaitOpAsync (userOpName, "DownsizeCaches", "", fun ctok -> parseCacheLock.AcquireLock (fun ltok -> parseAndCheckFileInProjectCachePossiblyStale.Resize(ltok, keepStrongly=1) parseAndCheckFileInProjectCache.Resize(ltok, keepStrongly=1) @@ -2687,9 +2696,6 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke static let globalInstance = lazy FSharpChecker.Create() - // Parse using backgroundCompiler - let ComputeBraceMatching(filename:string,source,options:FSharpProjectOptions) = - backgroundCompiler.MatchBraces(filename,source,options) // STATIC ROOT: FSharpLanguageServiceTestable.FSharpChecker.braceMatchCache. Most recently used cache for brace matching. Accessed on the // background UI thread, not on the compiler thread. @@ -2721,47 +2727,54 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke member ic.ReferenceResolver = referenceResolver - member ic.MatchBracesAlternate(filename, source, options) = + member ic.MatchBracesAlternate(filename, source, options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" async { match braceMatchCache.TryGet (AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options)) with | Some res -> return res | None -> - let! res = ComputeBraceMatching (filename, source, options) + let! res = backgroundCompiler.MatchBraces(filename, source, options, userOpName) braceMatchCache.Set (AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options), res) return res } - member ic.ParseFileInProject(filename, source, options) = + member ic.ParseFileInProject(filename, source, options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.ParseFileInProject(filename, source, options) + backgroundCompiler.ParseFileInProject(filename, source, options, userOpName) - member ic.GetBackgroundParseResultsForFileInProject (filename,options) = - backgroundCompiler.GetBackgroundParseResultsForFileInProject(filename,options) + member ic.GetBackgroundParseResultsForFileInProject (filename,options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.GetBackgroundParseResultsForFileInProject(filename, options, userOpName) - member ic.GetBackgroundCheckResultsForFileInProject (filename,options) = - backgroundCompiler.GetBackgroundCheckResultsForFileInProject(filename,options) + member ic.GetBackgroundCheckResultsForFileInProject (filename,options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.GetBackgroundCheckResultsForFileInProject(filename,options, userOpName) /// Try to get recent approximate type check results for a file. - member ic.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, ?source) = - backgroundCompiler.TryGetRecentCheckResultsForFile(filename,options,source) - - member ic.Compile(argv: string[]) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", "", fun ctok -> - cancellable { - return CompileHelpers.compileFromArgs (ctok, argv, referenceResolver, None, None) - } - ) - - member ic.Compile (ast:ParsedInput list, assemblyName:string, outFile:string, dependencies:string list, ?pdbFile:string, ?executable:bool, ?noframework:bool) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("Compile", assemblyName, fun ctok -> + member ic.TryGetRecentCheckResultsForFile(filename: string, options:FSharpProjectOptions, ?source, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.TryGetRecentCheckResultsForFile(filename,options,source, userOpName) + + member ic.Compile(argv: string[], ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync (userOpName, "Compile", "", fun ctok -> + cancellable { + return CompileHelpers.compileFromArgs (ctok, argv, referenceResolver, None, None) + }) + + member ic.Compile (ast:ParsedInput list, assemblyName:string, outFile:string, dependencies:string list, ?pdbFile:string, ?executable:bool, ?noframework:bool, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync (userOpName, "Compile", assemblyName, fun ctok -> cancellable { let noframework = defaultArg noframework false return CompileHelpers.compileFromAsts (ctok, referenceResolver, ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None) } ) - member ic.CompileToDynamicAssembly (otherFlags: string[], execute: (TextWriter * TextWriter) option) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", "", fun ctok -> + member ic.CompileToDynamicAssembly (otherFlags: string[], execute: (TextWriter * TextWriter) option, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync (userOpName, "CompileToDynamicAssembly", "", fun ctok -> cancellable { CompileHelpers.setOutputStreams execute @@ -2787,8 +2800,9 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke } ) - member ic.CompileToDynamicAssembly (asts:ParsedInput list, assemblyName:string, dependencies:string list, execute: (TextWriter * TextWriter) option, ?debug:bool, ?noframework:bool) = - backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync ("CompileToDynamicAssembly", assemblyName, fun ctok -> + member ic.CompileToDynamicAssembly (asts:ParsedInput list, assemblyName:string, dependencies:string list, execute: (TextWriter * TextWriter) option, ?debug:bool, ?noframework:bool, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.Reactor.EnqueueAndAwaitOpAsync (userOpName, "CompileToDynamicAssembly", assemblyName, fun ctok -> cancellable { CompileHelpers.setOutputStreams execute @@ -2826,22 +2840,24 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke member ic.InvalidateAll() = ic.ClearCaches() - member ic.ClearCachesAsync() = + member ic.ClearCachesAsync(?userOpName: string) = let utok = AssumeAnyCallerThreadWithoutEvidence() + let userOpName = defaultArg userOpName "Unknown" braceMatchCache.Clear(utok) - backgroundCompiler.ClearCachesAsync() + backgroundCompiler.ClearCachesAsync(userOpName) - member ic.ClearCaches() = - ic.ClearCachesAsync() |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run + member ic.ClearCaches(?userOpName) = + ic.ClearCachesAsync(?userOpName=userOpName) |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run - member ic.CheckMaxMemoryReached() = + member ic.CheckMaxMemoryReached(?userOpName: string) = if not maxMemoryReached && System.GC.GetTotalMemory(false) > int64 maxMB * 1024L * 1024L then // If the maxMB limit is reached, drastic action is taken // - reduce strong cache sizes to a minimum + let userOpName = defaultArg userOpName "Unknown" backgroundCompiler.CompleteAllQueuedOps() maxMemoryReached <- true braceMatchCache.Resize(AssumeAnyCallerThreadWithoutEvidence(), keepStrongly=1) - backgroundCompiler.DownsizeCaches() |> Async.RunSynchronously + backgroundCompiler.DownsizeCaches(userOpName) |> Async.RunSynchronously maxMemEvent.Trigger( () ) // This is for unit testing only @@ -2854,40 +2870,48 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke /// This function is called when the configuration is known to have changed for reasons not encoded in the ProjectOptions. /// For example, dependent references may have been deleted or created. - member ic.InvalidateConfiguration(options: FSharpProjectOptions, ?startBackgroundCompile) = - backgroundCompiler.InvalidateConfiguration(options,?startBackgroundCompile=startBackgroundCompile) + member ic.InvalidateConfiguration(options: FSharpProjectOptions, ?startBackgroundCompile, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.InvalidateConfiguration(options, startBackgroundCompile, userOpName) /// This function is called when a project has been cleaned, and thus type providers should be refreshed. - member ic.NotifyProjectCleaned(options: FSharpProjectOptions) = - backgroundCompiler.NotifyProjectCleaned options + member ic.NotifyProjectCleaned(options: FSharpProjectOptions, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.NotifyProjectCleaned (options, userOpName) /// Typecheck a source code file, returning a handle to the results of the /// parse including the reconstructed types in the file. - member ic.CheckFileInProjectAllowingStaleCachedResults(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj) = - backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults(parseResults,filename,fileVersion,source,options,textSnapshotInfo) + member ic.CheckFileInProjectAllowingStaleCachedResults(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.CheckFileInProjectAllowingStaleCachedResults(parseResults,filename,fileVersion,source,options,textSnapshotInfo, userOpName) /// Typecheck a source code file, returning a handle to the results of the /// parse including the reconstructed types in the file. - member ic.CheckFileInProject(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj) = + member ic.CheckFileInProject(parseResults:FSharpParseFileResults, filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.CheckFileInProject(parseResults,filename,fileVersion,source,options,textSnapshotInfo) + backgroundCompiler.CheckFileInProject(parseResults,filename,fileVersion,source,options,textSnapshotInfo, userOpName) /// Typecheck a source code file, returning a handle to the results of the /// parse including the reconstructed types in the file. - member ic.ParseAndCheckFileInProject(filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj) = + member ic.ParseAndCheckFileInProject(filename:string, fileVersion:int, source:string, options:FSharpProjectOptions, ?textSnapshotInfo:obj, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.ParseAndCheckFileInProject(filename, fileVersion, source, options, textSnapshotInfo) + backgroundCompiler.ParseAndCheckFileInProject(filename, fileVersion, source, options, textSnapshotInfo, userOpName) - member ic.ParseAndCheckProject(options) = + member ic.ParseAndCheckProject(options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" ic.CheckMaxMemoryReached() - backgroundCompiler.ParseAndCheckProject(options) + backgroundCompiler.ParseAndCheckProject(options, userOpName) - member ic.KeepProjectAlive(options) = - backgroundCompiler.KeepProjectAlive(options) + member ic.KeepProjectAlive(options, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.KeepProjectAlive(options, userOpName) /// For a given script file, get the ProjectOptions implied by the #load closure - member ic.GetProjectOptionsFromScript(filename, source, ?loadedTimeStamp, ?otherFlags, ?useFsiAuxLib, ?assumeDotNetFramework, ?extraProjectInfo: obj, ?optionsStamp: int64) = - backgroundCompiler.GetProjectOptionsFromScript(filename,source,?loadedTimeStamp=loadedTimeStamp, ?otherFlags=otherFlags, ?useFsiAuxLib=useFsiAuxLib, ?assumeDotNetFramework=assumeDotNetFramework, ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp) + member ic.GetProjectOptionsFromScript(filename, source, ?loadedTimeStamp, ?otherFlags, ?useFsiAuxLib, ?assumeDotNetFramework, ?extraProjectInfo: obj, ?optionsStamp: int64, ?userOpName: string) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.GetProjectOptionsFromScript(filename, source, loadedTimeStamp, otherFlags, useFsiAuxLib, assumeDotNetFramework, extraProjectInfo, optionsStamp, userOpName) member ic.GetProjectOptionsFromCommandLineArgs(projectFileName, argv, ?loadedTimeStamp, ?extraProjectInfo: obj) = let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading @@ -2904,13 +2928,17 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke Stamp = None } /// Begin background parsing the given project. - member ic.StartBackgroundCompile(options) = backgroundCompiler.CheckProjectInBackground(options) + member ic.StartBackgroundCompile(options, ?userOpName) = + let userOpName = defaultArg userOpName "Unknown" + backgroundCompiler.CheckProjectInBackground(options, userOpName) /// Begin background parsing the given project. - member ic.CheckProjectInBackground(options) = backgroundCompiler.CheckProjectInBackground(options) + member ic.CheckProjectInBackground(options, ?userOpName) = + ic.StartBackgroundCompile(options, ?userOpName=userOpName) /// Stop the background compile. - member ic.StopBackgroundCompile() = backgroundCompiler.StopBackgroundCompile() + member ic.StopBackgroundCompile() = + backgroundCompiler.StopBackgroundCompile() /// Block until the background compile finishes. // @@ -2963,8 +2991,9 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tcConfig: TcConfig, tcGlobals, tcImports, tcState) = let keepAssemblyContents = false - member __.ParseAndCheckInteraction (ctok, source) = + member __.ParseAndCheckInteraction (ctok, source, ?userOpName: string) = async { + let userOpName = defaultArg userOpName "Unknown" let filename = Path.Combine(tcConfig.implicitIncludeDir, "stdin.fsx") // Note: projectSourceFiles is only used to compute isLastCompiland, and is ignored if Build.IsScript(mainInputFileName) is true (which it is in this case). let projectSourceFiles = [ ] @@ -2981,9 +3010,7 @@ type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tc CompileOptions.ParseCompilerOptions (ignore, fsiCompilerOptions, [ ]) let loadClosure = LoadClosure.ComputeClosureOfSourceText(ctok, referenceResolver, defaultFSharpBinariesDir, filename, source, CodeContext.Editing, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework) - let! tcErrors, tcFileResult = - Parser.TypeCheckOneFile(parseResults,source,filename,"project",tcConfig,tcGlobals,tcImports, tcState, - Some loadClosure,backgroundDiagnostics,reactorOps,(fun () -> true),None) + let! tcErrors, tcFileResult = Parser.TypeCheckOneFile(parseResults, source, filename, "project", tcConfig, tcGlobals, tcImports, tcState, Some loadClosure, backgroundDiagnostics, reactorOps, (fun () -> true), None, userOpName) return match tcFileResult with diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index 00a0a1af02c..0f15cc473d2 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -143,8 +143,8 @@ type internal FSharpCheckFileResults = /// callback to the client to check if the text has changed. If it has, then give up /// and assume that we're going to repeat the operation later on. /// - - member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * getAllSymbols: (unit -> AssemblySymbol list) * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetDeclarationListInfo : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * getAllSymbols: (unit -> AssemblySymbol list) * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async /// Get the items for a declaration list in FSharpSymbol format /// @@ -167,7 +167,8 @@ type internal FSharpCheckFileResults = /// callback to the client to check if the text has changed. If it has, then give up /// and assume that we're going to repeat the operation later on. /// - member GetDeclarationListSymbols : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetDeclarationListSymbols : ParsedFileResultsOpt:FSharpParseFileResults option * line: int * colAtEndOfPartialName: int * lineText:string * qualifyingNames: string list * partialName: string * ?hasTextChangedSinceLastTypecheck: (obj * range -> bool) * ?userOpName: string -> Async /// Compute a formatted tooltip for the given location @@ -177,7 +178,8 @@ type internal FSharpCheckFileResults = /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. /// Used to discriminate between 'identifiers', 'strings' and others. For strings, an attempt is made to give a tooltip for a #r "..." location. Use a value from FSharpTokenInfo.Tag, or FSharpTokenTag.Identifier, unless you have other information available. - member GetStructuredToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetStructuredToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async /// Compute a formatted tooltip for the given location /// @@ -186,7 +188,8 @@ type internal FSharpCheckFileResults = /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. /// Used to discriminate between 'identifiers', 'strings' and others. For strings, an attempt is made to give a tooltip for a #r "..." location. Use a value from FSharpTokenInfo.Tag, or FSharpTokenTag.Identifier, unless you have other information available. - member GetToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async /// Compute the Visual Studio F1-help key identifier for the given location, based on name resolution results /// @@ -194,7 +197,8 @@ type internal FSharpCheckFileResults = /// The column number at the end of the identifiers where the information is being requested. /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. - member GetF1KeywordAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetF1KeywordAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async /// Compute a set of method overloads to show in a dialog relevant to the given code location. @@ -203,14 +207,16 @@ type internal FSharpCheckFileResults = /// The column number at the end of the identifiers where the information is being requested. /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. - member GetMethodsAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list option -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetMethodsAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list option * ?userOpName: string -> Async /// Compute a set of method overloads to show in a dialog relevant to the given code location. The resulting method overloads are returned as symbols. /// The line number where the information is being requested. /// The column number at the end of the identifiers where the information is being requested. /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. - member GetMethodsAsSymbols : line:int * colAtEndOfNames:int * lineText:string * names:string list -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetMethodsAsSymbols : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async /// Resolve the names at the given location to the declaration location of the corresponding construct. /// @@ -219,7 +225,8 @@ type internal FSharpCheckFileResults = /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. /// If not given, then get the location of the symbol. If false, then prefer the location of the corresponding symbol in the implementation of the file (rather than the signature if present). If true, prefer the location of the corresponding symbol in the signature of the file (rather than the implementation). - member GetDeclarationLocationAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?preferFlag:bool -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetDeclarationLocationAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?preferFlag:bool * ?userOpName: string -> Async /// Resolve the names at the given location to a use of symbol. @@ -228,7 +235,8 @@ type internal FSharpCheckFileResults = /// The column number at the end of the identifiers where the information is being requested. /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. - member GetSymbolUseAtLocation : line:int * colAtEndOfNames:int * lineText:string * names:string list -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetSymbolUseAtLocation : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async /// Get any extra colorization info that is available after the typecheck member GetSemanticClassification : range option -> (range * SemanticClassificationType)[] @@ -241,7 +249,7 @@ type internal FSharpCheckFileResults = member GetFormatSpecifierLocationsAndArity : unit -> (range*int)[] /// Get all textual usages of all symbols throughout the file - member GetAllUsesOfAllSymbolsInFile : unit -> Async + member GetAllUsesOfAllSymbolsInFile : unit -> Async /// Get the textual usages that resolved to the given symbol throughout the file member GetUsesOfSymbolInFile : symbol:FSharpSymbol -> Async @@ -249,7 +257,8 @@ type internal FSharpCheckFileResults = member internal GetVisibleNamespacesAndModulesAtPoint : pos -> Async /// Determines if a long ident is resolvable at a specific point. - member internal IsRelativeNameResolvable: cursorPos : pos * plid : string list * item: Item -> Async + /// An optional string used for tracing compiler operations associated with this request. + member internal IsRelativeNameResolvable: cursorPos : pos * plid : string list * item: Item * ?userOpName: string -> Async /// A handle to the results of CheckFileInProject. [] @@ -260,30 +269,30 @@ type internal FSharpCheckProjectResults = #endif /// The errors returned by processing the project - member Errors : FSharpErrorInfo[] + member Errors: FSharpErrorInfo[] /// Get a view of the overall signature of the assembly. Only valid to use if HasCriticalErrors is false. - member AssemblySignature : FSharpAssemblySignature + member AssemblySignature: FSharpAssemblySignature /// Get a view of the overall contents of the assembly. Only valid to use if HasCriticalErrors is false. - member AssemblyContents : FSharpAssemblyContents + member AssemblyContents: FSharpAssemblyContents /// Get the resolution of the ProjectOptions - member ProjectContext : FSharpProjectContext + member ProjectContext: FSharpProjectContext /// Get the textual usages that resolved to the given symbol throughout the project - member GetUsesOfSymbol : symbol:FSharpSymbol -> Async + member GetUsesOfSymbol: symbol:FSharpSymbol -> Async /// Get all textual usages of all symbols throughout the project - member GetAllUsesOfAllSymbols : unit -> Async + member GetAllUsesOfAllSymbols: unit -> Async /// Indicates if critical errors existed in the project options - member HasCriticalErrors : bool + member HasCriticalErrors: bool /// Indicates the set of files which must be watched to accurately track changes that affect these results, /// Clients interested in reacting to updates to these files should watch these files and take actions as described /// in the documentation for compiler service. - member DependencyFiles : string list + member DependencyFiles: string list /// Unused in this API #if COMPILER_PUBLIC_API @@ -375,7 +384,8 @@ type internal FSharpChecker = /// The filename for the file, used to help caching of results. /// The full source for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. - member MatchBracesAlternate : filename : string * source: string * options: FSharpProjectOptions -> Async<(range * range)[]> + /// An optional string used for tracing compiler operations associated with this request. + member MatchBracesAlternate : filename : string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> /// /// Parse a source code file, returning a handle that can be used for obtaining navigation bar information @@ -386,7 +396,8 @@ type internal FSharpChecker = /// The filename for the file. /// The full source for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. - member ParseFileInProject : filename: string * source: string * options: FSharpProjectOptions -> Async + /// An optional string used for tracing compiler operations associated with this request. + member ParseFileInProject : filename: string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async /// /// Check a source code file, returning a handle to the results of the parse including @@ -409,8 +420,8 @@ type internal FSharpChecker = /// an approximate intellisense resolution is inaccurate because a range of text has changed. This /// can be used to marginally increase accuracy of intellisense results in some situations. /// - /// - member CheckFileInProjectAllowingStaleCachedResults : parsed: FSharpParseFileResults * filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj -> Async + /// An optional string used for tracing compiler operations associated with this request. + member CheckFileInProjectAllowingStaleCachedResults : parsed: FSharpParseFileResults * filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj * ?userOpName: string -> Async /// /// @@ -434,8 +445,8 @@ type internal FSharpChecker = /// an approximate intellisense resolution is inaccurate because a range of text has changed. This /// can be used to marginally increase accuracy of intellisense results in some situations. /// - /// - member CheckFileInProject : parsed: FSharpParseFileResults * filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj -> Async + /// An optional string used for tracing compiler operations associated with this request. + member CheckFileInProject : parsed: FSharpParseFileResults * filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj * ?userOpName: string -> Async /// /// @@ -458,8 +469,8 @@ type internal FSharpChecker = /// an approximate intellisense resolution is inaccurate because a range of text has changed. This /// can be used to marginally increase accuracy of intellisense results in some situations. /// - /// - member ParseAndCheckFileInProject : filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj -> Async + /// An optional string used for tracing compiler operations associated with this request. + member ParseAndCheckFileInProject : filename: string * fileversion: int * source: string * options: FSharpProjectOptions * ?textSnapshotInfo: obj * ?userOpName: string -> Async /// /// Parse and typecheck all files in a project. @@ -467,14 +478,16 @@ type internal FSharpChecker = /// /// /// The options for the project or script. - member ParseAndCheckProject : options: FSharpProjectOptions -> Async + /// An optional string used for tracing compiler operations associated with this request. + member ParseAndCheckProject : options: FSharpProjectOptions * ?userOpName: string -> Async /// /// Create resources for the project and keep the project alive until the returned object is disposed. /// /// /// The options for the project or script. - member KeepProjectAlive : options: FSharpProjectOptions -> Async + /// An optional string used for tracing compiler operations associated with this request. + member KeepProjectAlive : options: FSharpProjectOptions * ?userOpName: string -> Async /// /// For a given script file, get the FSharpProjectOptions implied by the #load closure. @@ -487,7 +500,8 @@ type internal FSharpChecker = /// Indicates when the script was loaded into the editing environment, /// so that an 'unload' and 'reload' action will cause the script to be considered as a new project, /// so that references are re-resolved. - member GetProjectOptionsFromScript : filename: string * source: string * ?loadedTimeStamp: DateTime * ?otherFlags: string[] * ?useFsiAuxLib: bool * ?assumeDotNetFramework: bool * ?extraProjectInfo: obj * ?optionsStamp: int64 -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetProjectOptionsFromScript : filename: string * source: string * ?loadedTimeStamp: DateTime * ?otherFlags: string[] * ?useFsiAuxLib: bool * ?assumeDotNetFramework: bool * ?extraProjectInfo: obj * ?optionsStamp: int64 * ?userOpName: string -> Async /// /// Get the FSharpProjectOptions implied by a set of command line arguments. @@ -507,7 +521,8 @@ type internal FSharpChecker = /// /// The filename for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. - member GetBackgroundParseResultsForFileInProject : filename : string * options : FSharpProjectOptions -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetBackgroundParseResultsForFileInProject : filename : string * options : FSharpProjectOptions * ?userOpName: string -> Async /// /// Like ParseFileInProject, but uses the existing results from the background builder. @@ -516,16 +531,24 @@ type internal FSharpChecker = /// /// The filename for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. - member GetBackgroundCheckResultsForFileInProject : filename : string * options : FSharpProjectOptions -> Async + /// An optional string used for tracing compiler operations associated with this request. + member GetBackgroundCheckResultsForFileInProject : filename : string * options : FSharpProjectOptions * ?userOpName: string -> Async + /// /// Compile using the given flags. Source files names are resolved via the FileSystem API. /// The output file must be given by a -o flag. /// The first argument is ignored and can just be "fsc.exe". - member Compile: argv:string [] -> Async + /// + /// An optional string used for tracing compiler operations associated with this request. + member Compile: argv:string[] * ?userOpName: string -> Async + /// /// TypeCheck and compile provided AST - member Compile: ast:ParsedInput list * assemblyName:string * outFile:string * dependencies:string list * ?pdbFile:string * ?executable:bool * ?noframework:bool -> Async + /// + /// An optional string used for tracing compiler operations associated with this request. + member Compile: ast:ParsedInput list * assemblyName:string * outFile:string * dependencies:string list * ?pdbFile:string * ?executable:bool * ?noframework:bool * ?userOpName: string -> Async + /// /// Compiles to a dynamic assembly using the given flags. /// /// The first argument is ignored and can just be "fsc.exe". @@ -536,10 +559,15 @@ type internal FSharpChecker = /// If the 'execute' parameter is given the entry points for the code are executed and /// the given TextWriters are used for the stdout and stderr streams respectively. In this /// case, a global setting is modified during the execution. - member CompileToDynamicAssembly: otherFlags:string [] * execute:(TextWriter * TextWriter) option -> Async + /// + /// An optional string used for tracing compiler operations associated with this request. + member CompileToDynamicAssembly: otherFlags:string [] * execute:(TextWriter * TextWriter) option * ?userOpName: string -> Async + /// /// TypeCheck and compile provided AST - member CompileToDynamicAssembly: ast:ParsedInput list * assemblyName:string * dependencies:string list * execute:(TextWriter * TextWriter) option * ?debug:bool * ?noframework:bool -> Async + /// + /// An optional string used for tracing compiler operations associated with this request. + member CompileToDynamicAssembly: ast:ParsedInput list * assemblyName:string * dependencies:string list * execute:(TextWriter * TextWriter) option * ?debug:bool * ?noframework:bool * ?userOpName: string -> Async /// /// Try to get type check results for a file. This looks up the results of recent type checks of the @@ -550,21 +578,23 @@ type internal FSharpChecker = /// The filename for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// Optionally, specify source that must match the previous parse precisely. - member TryGetRecentCheckResultsForFile : filename: string * options:FSharpProjectOptions * ?source: string -> (FSharpParseFileResults * FSharpCheckFileResults * (*version*)int) option + /// An optional string used for tracing compiler operations associated with this request. + member TryGetRecentCheckResultsForFile : filename: string * options:FSharpProjectOptions * ?source: string * ?userOpName: string -> (FSharpParseFileResults * FSharpCheckFileResults * (*version*)int) option /// This function is called when the entire environment is known to have changed for reasons not encoded in the ProjectOptions of any project/compilation. member InvalidateAll : unit -> unit /// This function is called when the configuration is known to have changed for reasons not encoded in the ProjectOptions. /// For example, dependent references may have been deleted or created. - member InvalidateConfiguration: options: FSharpProjectOptions * ?startBackgroundCompile: bool -> unit + /// An optional string used for tracing compiler operations associated with this request. + member InvalidateConfiguration: options: FSharpProjectOptions * ?startBackgroundCompile: bool * ?userOpName: string -> unit /// Set the project to be checked in the background. Overrides any previous call to CheckProjectInBackground - member CheckProjectInBackground: options: FSharpProjectOptions -> unit + member CheckProjectInBackground: options: FSharpProjectOptions * ?userOpName: string -> unit /// Stop the background compile. //[] - member StopBackgroundCompile : unit -> unit + member StopBackgroundCompile : unit -> unit /// Block until the background compile finishes. //[] @@ -584,8 +614,11 @@ type internal FSharpChecker = /// may be in progress - such an operation is not counted in the queue length. member CurrentQueueLength : int + /// /// This function is called when a project has been cleaned/rebuilt, and thus any live type providers should be refreshed. - member NotifyProjectCleaned: options: FSharpProjectOptions -> Async + /// + /// An optional string used for tracing compiler operations associated with this request. + member NotifyProjectCleaned: options: FSharpProjectOptions * ?userOpName: string -> Async /// Notify the host that the logical type checking context for a file has now been updated internally /// and that the file has become eligible to be re-typechecked for errors. @@ -643,7 +676,9 @@ type internal FSharpChecker = // Used internally to provide intellisense over F# Interactive. type internal FsiInteractiveChecker = internal new : ReferenceResolver.Resolver * ops: IReactorOperations * tcConfig: TcConfig * tcGlobals: TcGlobals * tcImports: TcImports * tcState: TcState -> FsiInteractiveChecker - member internal ParseAndCheckInteraction : CompilationThreadToken * source:string -> Async + + /// An optional string used for tracing compiler operations associated with this request. + member internal ParseAndCheckInteraction : CompilationThreadToken * source:string * ?userOpName: string -> Async /// Information about the compilation environment #if COMPILER_PUBLIC_API diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index 05d5e944175..032df0c7bdd 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -21,6 +21,7 @@ type internal FSharpColorizationService checkerProvider: FSharpCheckerProvider, projectInfoManager: ProjectInfoManager ) = + static let userOpName = "FSharpColorizationService" interface IEditorClassificationService with // Do not perform classification if we don't have project options (#defines matter) @@ -37,7 +38,7 @@ type internal FSharpColorizationService asyncMaybe { let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false) + let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) // it's crucial to not return duplicated or overlapping `ClassifiedSpan`s because Find Usages service crashes. let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let colorizationData = checkResults.GetSemanticClassification (Some targetRange) |> Array.distinctBy fst diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 81c3d03159f..92983bc47fe 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -26,6 +26,8 @@ type internal FSharpAddOpenCodeFixProvider assemblyContentProvider: AssemblyContentProvider ) = inherit CodeFixProvider() + + static let userOpName = "FSharpAddOpenCodeFixProvider" let fixableDiagnosticIds = ["FS0039"; "FS0043"] let checker = checkerProvider.Checker @@ -94,7 +96,7 @@ type internal FSharpAddOpenCodeFixProvider let document = context.Document let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText) + let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName = userOpName) let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index f7853c1701c..f587c404edf 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -34,6 +34,7 @@ type internal FSharpImplementInterfaceCodeFixProvider inherit CodeFixProvider() let fixableDiagnosticIds = ["FS0366"] let checker = checkerProvider.Checker + static let userOpName = "ImplementInterfaceCodeFixProvider" let queryInterfaceState appendBracketAt (pos: pos) tokens (ast: Ast.ParsedInput) = asyncMaybe { @@ -141,7 +142,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject context.Document let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, options, sourceText = sourceText, allowStaleResults = true) + let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, options.OtherOptions |> Seq.toList) // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs index 4b5abcef60d..f414d725c8d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs @@ -16,13 +16,14 @@ type internal FSharpProposeUpperCaseLabelCodeFixProvider ) = inherit CodeFixProvider() let fixableDiagnosticIds = ["FS0053"] + static let userOpName = "ProposeUpperCaseLabel" override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override __.RegisterCodeFixesAsync context : Task = asyncMaybe { let textChanger (originalText: string) = originalText.[0].ToString().ToUpper() + originalText.Substring(1) - let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences(context.Document, context.Span, textChanger, projectInfoManager, checkerProvider.Checker) + let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences(context.Document, context.Span, textChanger, projectInfoManager, checkerProvider.Checker, userOpName) let title = FSComp.SR.replaceWithSuggestion (textChanger originalText) context.RegisterCodeFix( CodeAction.Create(title, solutionChanger, title), diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index c8f1c580682..44ed6b33153 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -24,7 +24,9 @@ type internal FSharpRenameUnusedValueCodeFixProvider ) = inherit CodeFixProvider() + static let userOpName = "RenameUnusedValueCodeFix" let fixableDiagnosticIds = ["FS1182"] + let checker = checkerProvider.Checker let createCodeFix (context: CodeFixContext, symbolName: string, titleFormat: string, textChange: TextChange) = let title = String.Format(titleFormat, symbolName) @@ -52,7 +54,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, sourceText = sourceText, userOpName=userOpName) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, options.OtherOptions |> Seq.toList) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index d411b8c6952..e3067605807 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -22,9 +22,10 @@ type internal FSharpHelpContextService projectInfoManager: ProjectInfoManager ) = + static let userOpName = "ImplementInterfaceCodeFix" static member GetHelpTerm(checker: FSharpChecker, sourceText : SourceText, fileName, options, span: TextSpan, tokens: List, textVersion) : Async = asyncMaybe { - let! _, _, check = checker.ParseAndCheckDocument(fileName, textVersion, sourceText.ToString(), options, allowStaleResults = true) + let! _, _, check = checker.ParseAndCheckDocument(fileName, textVersion, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) let line = lineInfo.LineNumber diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index dcca77d3ca0..c5ed4e46537 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -24,6 +24,9 @@ type internal XmlDocCommandFilter projectInfoManager: ProjectInfoManager, workspace: VisualStudioWorkspaceImpl ) = + + static let userOpName = "XmlDocCommand" + let checker = checkerProvider.Checker let document = @@ -65,7 +68,7 @@ type internal XmlDocCommandFilter let! document = document.Value let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let sourceText = wpfTextView.TextBuffer.CurrentSnapshot.GetText() - let! parsedInput = checker.ParseDocument(document, options, sourceText) + let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) let xmlDocables = XmlDocParser.getXmlDocables (sourceText, Some parsedInput) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 52b3a1bb9f1..4ef0a48cee7 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -30,8 +30,10 @@ type internal FSharpCompletionProvider projectInfoManager: ProjectInfoManager, assemblyContentProvider: AssemblyContentProvider ) = + inherit CompletionProvider() + static let userOpName = "CompletionProvider" static let completionTriggers = [| '.' |] static let declarationItemsCache = ConditionalWeakTable() static let [] NameInCodePropName = "NameInCode" @@ -49,6 +51,8 @@ type internal FSharpCompletionProvider .AddProperty("description", description) .AddProperty(IsKeywordPropName, "")) + let checker = checkerProvider.Checker + let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) @@ -85,7 +89,7 @@ type internal FSharpCompletionProvider static member ProvideCompletionsAsyncAux(checker: FSharpChecker, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, filePath: string, textVersionHash: int, getAllSymbols: unit -> AssemblySymbol list) = asyncMaybe { - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) + let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) //#if DEBUG //Logging.Logging.logInfof "AST:\n%+A" parsedInput @@ -108,7 +112,7 @@ type internal FSharpCompletionProvider //#endif let! declarations = - checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName, getAllSymbols) |> liftAsync + checkFileResults.GetDeclarationListInfo(Some(parseResults), fcsCaretLineNumber, caretLineColumn, caretLine.ToString(), qualifyingNames, partialName, getAllSymbols, userOpName=userOpName) |> liftAsync let results = List() @@ -214,13 +218,13 @@ type internal FSharpCompletionProvider do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = context.Document.GetTextVersionAsync(context.CancellationToken) - let! _, _, fileCheckResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, true) + let! _, _, fileCheckResults = checker.ParseAndCheckDocument(document, options, true, userOpName=userOpName) let getAllSymbols() = if Settings.IntelliSense.ShowAllSymbols then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else [] let! results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checkerProvider.Checker, sourceText, context.Position, options, + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, sourceText, context.Position, options, document.FilePath, textVersion.GetHashCode(), getAllSymbols) context.AddItems(results) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken @@ -271,7 +275,7 @@ type internal FSharpCompletionProvider let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, options) + let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 7327b97db95..0aa0e139d5f 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -28,6 +28,7 @@ type internal FSharpSignatureHelpProvider projectInfoManager: ProjectInfoManager ) = + static let userOpName = "SignatureHelpProvider" let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) @@ -36,7 +37,7 @@ type internal FSharpSignatureHelpProvider // Unit-testable core routine static member internal ProvideMethodsAsyncAux(checker: FSharpChecker, documentationBuilder: IDocumentationBuilder, sourceText: SourceText, caretPosition: int, options: FSharpProjectOptions, triggerIsTypedChar: char option, filePath: string, textVersionHash: int) = async { - let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) + let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options, userOpName = userOpName) match checkFileAnswer with | FSharpCheckFileAnswer.Aborted -> return None | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 629569bf9dc..537463030c3 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -26,6 +26,7 @@ type internal FSharpBreakpointResolutionService projectInfoManager: ProjectInfoManager ) = + static let userOpName = "BreakpointResolution" static member GetBreakpointLocation(checker: FSharpChecker, sourceText: SourceText, fileName: string, textSpan: TextSpan, options: FSharpProjectOptions) = async { // REVIEW: ParseFileInProject can cause FSharp.Compiler.Service to become unavailable (i.e. not responding to requests) for @@ -41,7 +42,7 @@ type internal FSharpBreakpointResolutionService else let textLineColumn = textLinePos.Character let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let! parseResults = checker.ParseFileInProject(fileName, sourceText.ToString(), options) + let! parseResults = checker.ParseFileInProject(fileName, sourceText.ToString(), options, userOpName = userOpName) return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) } diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 386e8a6ed9f..d1945e34318 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -25,6 +25,7 @@ type internal DiagnosticsType = type internal FSharpDocumentDiagnosticAnalyzer() = inherit DocumentDiagnosticAnalyzer() + static let userOpName = "DocumentDiagnosticAnalyzer" let getChecker(document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker @@ -58,12 +59,12 @@ type internal FSharpDocumentDiagnosticAnalyzer() = static member GetDiagnostics(checker: FSharpChecker, filePath: string, sourceText: SourceText, textVersionHash: int, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = async { - let! parseResults = checker.ParseFileInProject(filePath, sourceText.ToString(), options) + let! parseResults = checker.ParseFileInProject(filePath, sourceText.ToString(), options, userOpName=userOpName) let! errors = async { match diagnosticType with | DiagnosticsType.Semantic -> - let! checkResultsAnswer = checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options) + let! checkResultsAnswer = checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToString(), options, userOpName=userOpName) match checkResultsAnswer with | FSharpCheckFileAnswer.Aborted -> return [||] | FSharpCheckFileAnswer.Succeeded results -> diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 1ce4876d223..be16aeb1168 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -22,6 +22,7 @@ type private TextVersionHash = int type internal SimplifyNameDiagnosticAnalyzer() = inherit DocumentDiagnosticAnalyzer() + static let userOpName = "SimplifyNameDiagnosticAnalyzer" let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().ProjectInfoManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker let getPlidLength (plid: string list) = (plid |> List.sumBy String.length) + plid.Length @@ -58,7 +59,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let mutable result = ResizeArray() let symbolUses = @@ -89,7 +90,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = match rest with | [] -> return current | headIdent :: restPlid -> - let! res = checkResults.IsRelativeNameResolvable(posAtStartOfName, current, symbolUse.Symbol.Item) + let! res = checkResults.IsRelativeNameResolvable(posAtStartOfName, current, symbolUse.Symbol.Item, userOpName=userOpName) if res then return current else return! loop restPlid (headIdent :: current) } diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 7dfd7f9c1f5..779c9212d4c 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -15,6 +15,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices type internal UnusedDeclarationsAnalyzer() = inherit DocumentDiagnosticAnalyzer() + static let userOpName = "UnusedDeclarationsAnalyzer" let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().ProjectInfoManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker let [] DescriptorId = "FS1182" @@ -102,7 +103,7 @@ type internal UnusedDeclarationsAnalyzer() = | Some options -> let! sourceText = document.GetTextAsync() let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true) + let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) return diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 2f7e61df598..2f3722a79c4 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -145,6 +145,7 @@ type internal UnusedOpensDiagnosticAnalyzer() = let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().ProjectInfoManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker + static let userOpName = "UnusedOpensAnalyzer" static let Descriptor = DiagnosticDescriptor( id = IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId, @@ -162,7 +163,7 @@ type internal UnusedOpensDiagnosticAnalyzer() = asyncMaybe { do! Option.guard Settings.CodeFixes.UnusedOpens let! sourceText = document.GetTextAsync() - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true) + let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync return UnusedOpens.getUnusedOpens sourceText parsedInput symbolUses } diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index bf31a750683..ef8d74e37cd 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -24,6 +24,8 @@ type internal FSharpHighlightSpan = [, FSharpConstants.FSharpLanguageName)>] type internal FSharpDocumentHighlightsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: ProjectInfoManager) = + static let userOpName = "DocumentHighlights" + /// Fix invalid spans if they appear to have redundant suffix and prefix. static let fixInvalidSymbolSpans (sourceText: SourceText) (lastIdent: string) (spans: FSharpHighlightSpan []) = spans @@ -57,7 +59,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync return diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index 52e9675f9ad..8f497862d90 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -15,9 +15,10 @@ type internal FSharpBraceMatchingService projectInfoManager: ProjectInfoManager ) = + static let userOpName = "BraceMatching" static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, options, position: int) = async { - let! matchedBraces = checker.MatchBracesAlternate(fileName, sourceText.ToString(), options) + let! matchedBraces = checker.MatchBracesAlternate(fileName, sourceText.ToString(), options, userOpName = userOpName) let isPositionInRange range = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range).Contains(position) return matchedBraces |> Array.tryFind(fun (left, right) -> isPositionInRange left || isPositionInRange right) } diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 7e07a21a1f0..2691235bf9f 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -82,6 +82,8 @@ type internal InlineRenameInfo checkFileResults: FSharpCheckFileResults ) = + static let userOpName = "InlineRename" + let getDocumentText (document: Document) cancellationToken = match document.TryGetText() with | true, text -> text @@ -92,7 +94,7 @@ type internal InlineRenameInfo Tokenizer.fixupSpan(sourceText, span) let symbolUses = - SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution) + SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution, userOpName) |> Async.cache interface IInlineRenameInfo with @@ -144,6 +146,7 @@ type internal InlineRenameService [] _refactorNotifyServices: seq ) = + static let userOpName = "InlineRename" static member GetInlineRenameInfo(checker: FSharpChecker, projectInfoManager: ProjectInfoManager, document: Document, sourceText: SourceText, position: int, defines: string list, options: FSharpProjectOptions) : Async = asyncMaybe { @@ -151,7 +154,7 @@ type internal InlineRenameService let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) return InlineRenameInfo(checker, projectInfoManager, document, sourceText, symbol, symbolUse, declLoc, checkFileResults) :> IInlineRenameInfo diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs index 04d0ca00554..01e6763164b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -15,25 +15,19 @@ type CheckResults = | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> type FSharpChecker with - member this.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string) = + member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string, userOpName: string) = asyncMaybe { - let! fileParseResults = this.ParseFileInProject(document.FilePath, sourceText, options) |> liftAsync + let! fileParseResults = checker.ParseFileInProject(document.FilePath, sourceText, options, userOpName=userOpName) |> liftAsync return! fileParseResults.ParseTree } - member this.ParseDocument(document: Document, options: FSharpProjectOptions, ?sourceText: SourceText) = - asyncMaybe { - let! sourceText = - match sourceText with - | Some x -> Task.FromResult x - | None -> document.GetTextAsync() - return! this.ParseDocument(document, options, sourceText.ToString()) - } + member checker.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: SourceText, userOpName: string) = + checker.ParseDocument(document, options, sourceText=sourceText.ToString(), userOpName=userOpName) - member this.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = + member checker.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string) = let parseAndCheckFile = async { - let! parseResults, checkFileAnswer = this.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText, options) + let! parseResults, checkFileAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText, options, userOpName=userOpName) return match checkFileAnswer with | FSharpCheckFileAnswer.Aborted -> @@ -69,7 +63,7 @@ type FSharpChecker with | Ready x -> async.Return x | StillRunning worker -> async { - match allowStaleResults, this.TryGetRecentCheckResultsForFile(filePath, options) with + match allowStaleResults, checker.TryGetRecentCheckResultsForFile(filePath, options) with | true, Some (parseResults, checkFileResults, _) -> return Some (parseResults, checkFileResults) | _ -> @@ -80,7 +74,7 @@ type FSharpChecker with else parseAndCheckFile |> Async.map bindParsedInput - member this.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, ?sourceText: SourceText) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = + member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string, ?sourceText: SourceText) = async { let! cancellationToken = Async.CancellationToken let! sourceText = @@ -88,21 +82,21 @@ type FSharpChecker with | Some x -> Task.FromResult x | None -> document.GetTextAsync() let! textVersion = document.GetTextVersionAsync(cancellationToken) - return! this.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options, allowStaleResults) + return! checker.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options, allowStaleResults, userOpName=userOpName) } - member self.TryParseAndCheckFileInProject (projectOptions, fileName, source) = async { - let! (parseResults, checkAnswer) = self.ParseAndCheckFileInProject (fileName,0, source,projectOptions) + member checker.TryParseAndCheckFileInProject (projectOptions, fileName, source, userOpName) = async { + let! (parseResults, checkAnswer) = checker.ParseAndCheckFileInProject (fileName,0, source,projectOptions, userOpName=userOpName) match checkAnswer with | FSharpCheckFileAnswer.Aborted -> return None | FSharpCheckFileAnswer.Succeeded checkResults -> return Some (parseResults,checkResults) } - member self.GetAllUsesOfAllSymbolsInSourceString (projectOptions, fileName, source: string, checkForUnusedOpens) = async { + member checker.GetAllUsesOfAllSymbolsInSourceString (projectOptions, fileName, source: string, checkForUnusedOpens, userOpName) = async { - let! parseAndCheckResults = self.TryParseAndCheckFileInProject (projectOptions, fileName, source) + let! parseAndCheckResults = checker.TryParseAndCheckFileInProject (projectOptions, fileName, source, userOpName=userOpName) match parseAndCheckResults with | None -> return [||] | Some(_parseResults,checkResults) -> diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 38922522406..addc9ea37ab 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -16,7 +16,7 @@ open Microsoft.VisualStudio.FSharp.Editor.Symbols module internal SymbolHelpers = let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, - projectInfoManager: ProjectInfoManager, checker: FSharpChecker, solution: Solution) = + projectInfoManager: ProjectInfoManager, checker: FSharpChecker, solution: Solution, userOpName) = async { let! symbolUses = match declLoc with @@ -36,7 +36,7 @@ module internal SymbolHelpers = async { match projectInfoManager.TryGetOptionsForProject(project.Id) with | Some options -> - let! projectCheckResults = checker.ParseAndCheckProject(options) + let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) return! projectCheckResults.GetUsesOfSymbol(symbol) | None -> return [||] }) @@ -63,7 +63,7 @@ module internal SymbolHelpers = type OriginalText = string - let changeAllSymbolReferences (document: Document, symbolSpan: TextSpan, textChanger: string -> string, projectInfoManager: ProjectInfoManager, checker: FSharpChecker) + let changeAllSymbolReferences (document: Document, symbolSpan: TextSpan, textChanger: string -> string, projectInfoManager: ProjectInfoManager, checker: FSharpChecker, userOpName) : Async<(Func> * OriginalText) option> = asyncMaybe { do! Option.guard (symbolSpan.Length > 0) @@ -74,7 +74,7 @@ module internal SymbolHelpers = let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = textLinePos.Line + 1 @@ -86,7 +86,7 @@ module internal SymbolHelpers = Func<_,_>(fun (cancellationToken: CancellationToken) -> async { let! symbolUsesByDocumentId = - getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution) + getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution, userOpName) let mutable solution = document.Project.Solution diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index f4c9c3f61c2..d2dd2bcc7a9 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -23,6 +23,8 @@ type internal FSharpFindUsagesService projectInfoManager: ProjectInfoManager ) = + static let userOpName = "FindUsages" + // File can be included in more than one project, hence single `range` may results with multiple `Document`s. let rangeToDocumentSpans (solution: Solution, range: range, cancellationToken: CancellationToken) = async { @@ -44,12 +46,12 @@ type internal FSharpFindUsagesService return spans |> Array.choose id |> Array.toList } - let findReferencedSymbolsAsync(document: Document, position: int, context: IFindUsagesContext, allReferences: bool) : Async = + let findReferencedSymbolsAsync(document: Document, position: int, context: IFindUsagesContext, allReferences: bool, userOpName: string) : Async = asyncMaybe { let! sourceText = document.GetTextAsync(context.CancellationToken) let checker = checkerProvider.Checker let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) @@ -110,7 +112,7 @@ type internal FSharpFindUsagesService |> Seq.map (fun project -> asyncMaybe { let! options = projectInfoManager.TryGetOptionsForProject(project.Id) - let! projectCheckResults = checker.ParseAndCheckProject(options) |> liftAsync + let! projectCheckResults = checker.ParseAndCheckProject(options, userOpName = userOpName) |> liftAsync return! projectCheckResults.GetUsesOfSymbol(symbolUse.Symbol) |> liftAsync } |> Async.map (Option.defaultValue [||])) |> Async.Parallel @@ -141,9 +143,9 @@ type internal FSharpFindUsagesService interface IFindUsagesService with member __.FindReferencesAsync(document, position, context) = - findReferencedSymbolsAsync(document, position, context, true) + findReferencedSymbolsAsync(document, position, context, true, userOpName) |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) member __.FindImplementationsAsync(document, position, context) = - findReferencedSymbolsAsync(document, position, context, false) + findReferencedSymbolsAsync(document, position, context, false, userOpName) |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 5a3c993dc54..f9505ea36f5 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -35,6 +35,9 @@ type internal FSharpNavigableItem(document: Document, textSpan: TextSpan) = member this.ChildItems = ImmutableArray.Empty type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: ProjectInfoManager) = + + static let userOpName = "GoToDefinition" + /// Use an origin document to provide the solution & workspace used to /// find the corresponding textSpan and INavigableItem for the range let rangeToNavigableItem (range: range, document: Document) = @@ -63,8 +66,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: Project let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! _, _, checkFileResults = - checker.ParseAndCheckDocument (originDocument,projectOptions,allowStaleResults=true,sourceText=sourceText) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument,projectOptions,allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) let idRange = lexerSymbol.Ident.idRange let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) let symbol = fsSymbolUse.Symbol @@ -77,7 +79,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: Project let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject implDoc - let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, allowStaleResults=true, sourceText=implSourceText) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, allowStaleResults=true, sourceText=implSourceText, userOpName = userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol |> liftAsync let! implSymbol = symbolUses |> Array.tryHead let implTextSpan = RoslynHelpers.FSharpRangeToTextSpan (implSourceText, implSymbol.RangeAlternate) @@ -98,7 +100,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: Project /// use the targetSymbol to find the first instance of its presence in the provided source file member __.FindSymbolDeclarationInFile(targetSymbolUse: FSharpSymbolUse, filePath: string, source: string, options: FSharpProjectOptions, fileVersion:int) = asyncMaybe { - let! _, checkFileAnswer = checker.ParseAndCheckFileInProject (filePath, fileVersion, source, options) |> liftAsync + let! _, checkFileAnswer = checker.ParseAndCheckFileInProject (filePath, fileVersion, source, options, userOpName = userOpName) |> liftAsync match checkFileAnswer with | FSharpCheckFileAnswer.Aborted -> return! None | FSharpCheckFileAnswer.Succeeded checkFileResults -> @@ -108,6 +110,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: Project } type private StatusBar(statusBar: IVsStatusbar) = + let mutable searchIcon = int16 Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Find :> obj let clear() = @@ -149,6 +152,8 @@ type internal FSharpGoToDefinitionService projectInfoManager: ProjectInfoManager, [] _presenters: IEnumerable ) = + + static let userOpName = "GoToDefinition" let gotoDefinition = GoToDefinition(checkerProvider.Checker, projectInfoManager) let serviceProvider = ServiceProvider.GlobalProvider let statusBar = StatusBar(serviceProvider.GetService()) @@ -211,8 +216,7 @@ type internal FSharpGoToDefinitionService let preferSignature = isSignatureFile originDocument.FilePath - let! _, _, checkFileResults = - checkerProvider.Checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true, sourceText=sourceText) + let! _, _, checkFileResults = checkerProvider.Checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true, sourceText=sourceText, userOpName=userOpName) let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) let idRange = lexerSymbol.Ident.idRange diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index d5281a81684..ef1811553ac 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -26,6 +26,7 @@ type internal FSharpNavigationBarItemService projectInfoManager: ProjectInfoManager ) = + static let userOpName = "NavigationBarItem" static let emptyResult: IList = upcast [||] interface INavigationBarItemService with @@ -33,7 +34,7 @@ type internal FSharpNavigationBarItemService asyncMaybe { let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checkerProvider.Checker.ParseDocument(document, options, sourceText) + let! parsedInput = checkerProvider.Checker.ParseDocument(document, options, sourceText=sourceText, userOpName=userOpName) let navItems = NavigationImpl.getNavigation parsedInput let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index c7f9c19bc78..fe04f655c05 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -29,6 +29,8 @@ type private TooltipInfo = module private FSharpQuickInfo = + let userOpName = "QuickInfo" + // when a construct has been declared in a signature file the documentation comments that are // written in that file are the ones that go into the generated xml when the project is compiled // therefore we should include these doccoms in our design time tooltips @@ -55,7 +57,7 @@ module private FSharpQuickInfo = let! extProjectOptions = projectInfoManager.TryGetOptionsForProject extDocId.ProjectId let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing (extDocument.FilePath, List.ofSeq extProjectOptions.OtherOptions) let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true) - let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument,extProjectOptions,allowStaleResults=true,sourceText=extSourceText) + let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) let! extTooltipText = extCheckFileResults.GetStructuredToolTipTextAlternate @@ -91,7 +93,7 @@ module private FSharpQuickInfo = let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, projectOptions.OtherOptions |> Seq.toList) let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true) let idRange = lexerSymbol.Ident.idRange - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText) + let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText, userOpName = userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() @@ -171,7 +173,7 @@ type internal FSharpQuickInfoProvider static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, options: FSharpProjectOptions, textVersionHash: int) = asyncMaybe { - let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) + let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = FSharpQuickInfo.userOpName) let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 29405bf9e44..c3ec952913a 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -142,13 +142,15 @@ open BlockStructure type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoManager: ProjectInfoManager) = inherit BlockStructureService() + static let userOpName = "BlockStructure" + override __.Language = FSharpConstants.FSharpLanguageName override __.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, options, sourceText) + let! parsedInput = checker.ParseDocument(document, options, sourceText, userOpName) return createBlockSpans sourceText parsedInput |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs index 6ea2c0dc516..2e37987155f 100644 --- a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs @@ -51,9 +51,7 @@ module GoToDefinitionServiceTests = let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = - checker.ParseAndCheckDocument - (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) |> Async.RunSynchronously + let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName="GoToDefinitionServiceTests") |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocationAlternate From 18ac84bc5460ee107e4a028f86f0a22309b98460 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 18:05:04 +0100 Subject: [PATCH 04/11] cleanup diagnostics --- src/fsharp/vs/Reactor.fs | 16 +++---- src/fsharp/vs/service.fs | 42 +++++++++---------- src/fsharp/vs/service.fsi | 12 +++--- .../Utils/LanguageServiceProfiling/Program.fs | 7 +--- .../CodeFix/AddOpenCodeFixProvider.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 4 +- .../CodeFix/RenameUnusedValue.fs | 2 +- .../Commands/HelpContextService.fs | 2 +- .../FSharp.Editor/Completion/SignatureHelp.fs | 2 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 1 + .../UnusedOpensDiagnosticAnalyzer.fs | 1 + .../DocumentHighlightsService.fs | 2 +- .../Formatting/BraceMatchingService.fs | 2 +- .../InlineRename/InlineRenameService.fs | 2 +- .../LanguageService/LanguageService.fs | 6 ++- .../LanguageService/SymbolHelpers.fs | 2 +- .../Navigation/FindUsagesService.fs | 4 +- .../Navigation/GoToDefinitionService.fs | 15 ++----- .../QuickInfo/QuickInfoProvider.fs | 24 +++++------ .../BackgroundRequests.fs | 2 +- .../FSharp.LanguageService/GotoDefinition.fs | 2 +- .../FSharp.LanguageService/Intellisense.fs | 6 +-- .../unittests/GoToDefinitionServiceTests.fs | 8 ++-- 23 files changed, 76 insertions(+), 90 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index f093943f2c5..07f97f593b0 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -45,7 +45,7 @@ type Reactor() = // Async workflow which receives messages and dispatches to worker functions. let rec loop (bgOpOpt, onComplete, bg) = - async { Trace.TraceInformation("Reactor: receiving..., remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + async { //Trace.TraceInformation("Reactor: receiving..., remaining {0}", inbox.CurrentQueueLength) // Explanation: The reactor thread acts as the compilation thread in hosted scenarios let ctok = AssumeCompilationThreadWithoutEvidence() @@ -68,21 +68,21 @@ type Reactor() = #endif match msg with | Some (SetBackgroundOp bgOpOpt) -> - Trace.TraceInformation("Reactor: --> set background op, remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + //Trace.TraceInformation("Reactor: --> set background op, remaining {0}", inbox.CurrentQueueLength) return! loop (bgOpOpt, onComplete, false) | Some (Op (userOpName, opName, opArg, ct, op, ccont)) -> if ct.IsCancellationRequested then ccont() else - Trace.TraceInformation("Reactor: --> {0}.{1} ({2}), remaining {3}, mem {4}, gc2 {5}", userOpName, opName, opArg, inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> {0}.{1} ({2}), remaining {3}", userOpName, opName, opArg, inbox.CurrentQueueLength) let time = Stopwatch() time.Start() op ctok time.Stop() let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- {0}.{1} ({2}), remaining {3}, took {4}ms", userOpName, opName, opArg, inbox.CurrentQueueLength, span.TotalMilliseconds) + Trace.TraceInformation("Reactor: <-- {0}.{1}, took {2} ms", userOpName, opName, span.TotalMilliseconds) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> - Trace.TraceInformation("Reactor: --> wait for background (debug only), remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> wait for background, remaining {0}", inbox.CurrentQueueLength) match bgOpOpt with | None -> () | Some bgOp -> @@ -91,20 +91,20 @@ type Reactor() = channel.Reply(()) return! loop (None, onComplete, false) | Some (CompleteAllQueuedOps channel) -> - Trace.TraceInformation("Reactor: --> stop background work and complete all queued ops, remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> stop background work and complete all queued ops, remaining {0}", inbox.CurrentQueueLength) return! loop (None, Some channel, false) | None -> match bgOpOpt, onComplete with | _, Some onComplete -> onComplete.Reply() | Some bgOp, None -> - Trace.TraceInformation("Reactor: --> background step, remaining {0}, mem {1}, gc2 {2}", inbox.CurrentQueueLength, GC.GetTotalMemory(false)/1000000L, GC.CollectionCount(2)) + Trace.TraceInformation("Reactor: --> background step", inbox.CurrentQueueLength) let time = Stopwatch() time.Start() let res = bgOp ctok time.Stop() let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- background step, remaining {0}, took {1}ms", inbox.CurrentQueueLength, span.TotalMilliseconds) + Trace.TraceInformation("Reactor: <-- background step, took {0}ms", span.TotalMilliseconds) return! loop ((if res then Some bgOp else None), onComplete, true) | None, None -> failwith "unreachable, should have used inbox.Receive" } diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 3e2fabfb737..aeb71b95030 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -6,10 +6,11 @@ namespace Microsoft.FSharp.Compiler.SourceCodeServices open System -open System.IO -open System.Text open System.Collections.Generic open System.Collections.Concurrent +open System.Diagnostics +open System.IO +open System.Text open Microsoft.FSharp.Core.Printf open Microsoft.FSharp.Compiler @@ -941,8 +942,6 @@ type TypeCheckInfo // Remove all duplicates. We've put the types first, so this removes the DelegateCtor and DefaultStructCtor's. let items = items |> RemoveDuplicateCompletionItems g - if verbose then dprintf "service.ml: mkDecls: %d found groups after filtering\n" (List.length items); - // Group by display name let items = items |> List.groupBy (fun d -> d.Item.DisplayName) @@ -1029,11 +1028,6 @@ type TypeCheckInfo getToolTipTextCache.Put(ctok, key,res) res - // GetToolTipText: return the "pop up" (or "Quick Info") text given a certain context. - member x.GetToolTipText ctok line lineStr colAtEndOfNames names = - x.GetStructuredToolTipText(ctok, line, lineStr, colAtEndOfNames, names) - |> Tooltips.ToFSharpToolTipText - member __.GetF1Keyword (ctok, line, lineStr, colAtEndOfNames, names) : string option = ErrorScope.Protect Range.range0 (fun () -> @@ -1787,34 +1781,34 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp reactorOp userOpName "GetDeclarationListSymbols" List.empty (fun ctok scope -> scope.GetDeclarationListSymbols(ctok, parseResultsOpt, line, lineStr, colAtEndOfNamesAndResidue, qualifyingNames, partialName, hasTextChangedSinceLastTypecheck)) /// Resolve the names at the given location to give a data tip - member info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName: string) = + member info.GetStructuredToolTipText(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpToolTipText [] match tokenTagToTokenId tokenTag with | TOKEN_IDENT -> - reactorOp userOpName "GetToolTipText" dflt (fun ctok scope -> scope.GetStructuredToolTipText(ctok, line, lineStr, colAtEndOfNames, names)) + reactorOp userOpName "GetStructuredToolTipText" dflt (fun ctok scope -> scope.GetStructuredToolTipText(ctok, line, lineStr, colAtEndOfNames, names)) | TOKEN_STRING | TOKEN_STRING_TEXT -> reactorOp userOpName "GetReferenceResolutionToolTipText" dflt (fun ctok scope -> scope.GetReferenceResolutionStructuredToolTipText(ctok, line, colAtEndOfNames) ) | _ -> async.Return dflt - member info.GetToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, userOpName) = - info.GetStructuredToolTipTextAlternate(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName=userOpName) + member info.GetToolTipText(line, colAtEndOfNames, lineStr, names, tokenTag, userOpName) = + info.GetStructuredToolTipText(line, colAtEndOfNames, lineStr, names, tokenTag, ?userOpName=userOpName) |> Tooltips.Map Tooltips.ToFSharpToolTipText - member info.GetF1KeywordAlternate (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + member info.GetF1Keyword (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" reactorOp userOpName "GetF1Keyword" None (fun ctok scope -> scope.GetF1Keyword (ctok, line, lineStr, colAtEndOfNames, names)) // Resolve the names at the given location to a set of methods - member info.GetMethodsAlternate(line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + member info.GetMethods(line, colAtEndOfNames, lineStr, names, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpMethodGroup("",[| |]) reactorOp userOpName "GetMethods" dflt (fun ctok scope -> scope.GetMethods (ctok, line, lineStr, colAtEndOfNames, names)) - member info.GetDeclarationLocationAlternate (line, colAtEndOfNames, lineStr, names, ?preferFlag, ?userOpName: string) = + member info.GetDeclarationLocation (line, colAtEndOfNames, lineStr, names, ?preferFlag, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" let dflt = FSharpFindDeclResult.DeclNotFound FSharpFindDeclFailureReason.Unknown reactorOp userOpName "GetDeclarationLocation" dflt (fun ctok scope -> @@ -1833,9 +1827,9 @@ type FSharpCheckFileResults(filename: string, errors: FSharpErrorInfo[], scopeOp |> Option.map (fun (symbols,denv,m) -> symbols |> List.map (fun sym -> FSharpSymbolUse(scope.TcGlobals,denv,sym,ItemOccurence.Use,m)))) - member info.GetSymbolAtLocationAlternate (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = + member info.GetSymbolAtLocation (line, colAtEndOfNames, lineStr, names, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" - reactorOp userOpName "GetSymbolUseAtLocation" None (fun ctok scope -> + reactorOp userOpName "GetSymbolAtLocation" None (fun ctok scope -> scope.GetSymbolUseAtLocation (ctok, line, lineStr, colAtEndOfNames, names) |> Option.map (fun (sym,_,_) -> sym)) @@ -2259,7 +2253,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> async.Return parseResults | _ -> - reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseFileInProject.CacheMiss", filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseFileInProject", filename, fun ctok -> cancellable { // Try the caches again - it may have been filled by the time this operation runs match parseCacheLock.AcquireLock (fun ctok -> parseFileInProjectCache.TryGet (ctok, (filename, source, options))) with @@ -2269,6 +2263,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> return parseResults | _ -> + Trace.TraceInformation("Reactor: operation {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) foregroundParseCount <- foregroundParseCount + 1 let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement @@ -2727,7 +2722,7 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke member ic.ReferenceResolver = referenceResolver - member ic.MatchBracesAlternate(filename, source, options, ?userOpName: string) = + member ic.MatchBraces(filename, source, options, ?userOpName: string) = let userOpName = defaultArg userOpName "Unknown" async { match braceMatchCache.TryGet (AssumeAnyCallerThreadWithoutEvidence(), (filename, source, options)) with @@ -2849,14 +2844,15 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke member ic.ClearCaches(?userOpName) = ic.ClearCachesAsync(?userOpName=userOpName) |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run - member ic.CheckMaxMemoryReached(?userOpName: string) = + member ic.CheckMaxMemoryReached() = if not maxMemoryReached && System.GC.GetTotalMemory(false) > int64 maxMB * 1024L * 1024L then + Trace.WriteLine("!!!!!!!! MAX MEMORY REACHED, DOWNSIZING F# COMPILER CACHES !!!!!!!!!!!!!!!") // If the maxMB limit is reached, drastic action is taken // - reduce strong cache sizes to a minimum - let userOpName = defaultArg userOpName "Unknown" + let userOpName = "MaxMemoryReached" backgroundCompiler.CompleteAllQueuedOps() maxMemoryReached <- true - braceMatchCache.Resize(AssumeAnyCallerThreadWithoutEvidence(), keepStrongly=1) + braceMatchCache.Resize(AssumeAnyCallerThreadWithoutEvidence(), keepStrongly=10) backgroundCompiler.DownsizeCaches(userOpName) |> Async.RunSynchronously maxMemEvent.Trigger( () ) diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index 0f15cc473d2..8764158a616 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -179,7 +179,7 @@ type internal FSharpCheckFileResults = /// The identifiers at the location where the information is being requested. /// Used to discriminate between 'identifiers', 'strings' and others. For strings, an attempt is made to give a tooltip for a #r "..." location. Use a value from FSharpTokenInfo.Tag, or FSharpTokenTag.Identifier, unless you have other information available. /// An optional string used for tracing compiler operations associated with this request. - member GetStructuredToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async + member GetStructuredToolTipText : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async /// Compute a formatted tooltip for the given location /// @@ -189,7 +189,7 @@ type internal FSharpCheckFileResults = /// The identifiers at the location where the information is being requested. /// Used to discriminate between 'identifiers', 'strings' and others. For strings, an attempt is made to give a tooltip for a #r "..." location. Use a value from FSharpTokenInfo.Tag, or FSharpTokenTag.Identifier, unless you have other information available. /// An optional string used for tracing compiler operations associated with this request. - member GetToolTipTextAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async + member GetToolTipText : line:int * colAtEndOfNames:int * lineText:string * names:string list * tokenTag:int * ?userOpName: string -> Async /// Compute the Visual Studio F1-help key identifier for the given location, based on name resolution results /// @@ -198,7 +198,7 @@ type internal FSharpCheckFileResults = /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. /// An optional string used for tracing compiler operations associated with this request. - member GetF1KeywordAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async + member GetF1Keyword : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?userOpName: string -> Async /// Compute a set of method overloads to show in a dialog relevant to the given code location. @@ -208,7 +208,7 @@ type internal FSharpCheckFileResults = /// The text of the line where the information is being requested. /// The identifiers at the location where the information is being requested. /// An optional string used for tracing compiler operations associated with this request. - member GetMethodsAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list option * ?userOpName: string -> Async + member GetMethods : line:int * colAtEndOfNames:int * lineText:string * names:string list option * ?userOpName: string -> Async /// Compute a set of method overloads to show in a dialog relevant to the given code location. The resulting method overloads are returned as symbols. /// The line number where the information is being requested. @@ -226,7 +226,7 @@ type internal FSharpCheckFileResults = /// The identifiers at the location where the information is being requested. /// If not given, then get the location of the symbol. If false, then prefer the location of the corresponding symbol in the implementation of the file (rather than the signature if present). If true, prefer the location of the corresponding symbol in the signature of the file (rather than the implementation). /// An optional string used for tracing compiler operations associated with this request. - member GetDeclarationLocationAlternate : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?preferFlag:bool * ?userOpName: string -> Async + member GetDeclarationLocation : line:int * colAtEndOfNames:int * lineText:string * names:string list * ?preferFlag:bool * ?userOpName: string -> Async /// Resolve the names at the given location to a use of symbol. @@ -385,7 +385,7 @@ type internal FSharpChecker = /// The full source for the file. /// The options for the project or script, used to determine active --define conditionals and other options relevant to parsing. /// An optional string used for tracing compiler operations associated with this request. - member MatchBracesAlternate : filename : string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> + member MatchBraces : filename : string * source: string * options: FSharpProjectOptions * ?userOpName: string -> Async<(range * range)[]> /// /// Parse a source code file, returning a handle that can be used for obtaining navigation bar information diff --git a/vsintegration/Utils/LanguageServiceProfiling/Program.fs b/vsintegration/Utils/LanguageServiceProfiling/Program.fs index e481878c943..26fd24e8b89 100644 --- a/vsintegration/Utils/LanguageServiceProfiling/Program.fs +++ b/vsintegration/Utils/LanguageServiceProfiling/Program.fs @@ -130,12 +130,7 @@ let main argv = let! fileResults = checkFile fileVersion match fileResults with | Some fileResults -> - let! symbolUse = - fileResults.GetSymbolUseAtLocation( - options.SymbolPos.Line, - options.SymbolPos.Column, - getLine(options.SymbolPos.Line), - [options.SymbolText]) + let! symbolUse = fileResults.GetSymbolUseAtLocation(options.SymbolPos.Line, options.SymbolPos.Column, getLine(options.SymbolPos.Line),[options.SymbolText], userOpName=userOpName) match symbolUse with | Some symbolUse -> eprintfn "Found symbol %s" symbolUse.Symbol.FullName diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 92983bc47fe..b1caf81c4aa 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -104,7 +104,7 @@ type internal FSharpAddOpenCodeFixProvider let! symbol = asyncMaybe { let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy, false) - return! checkResults.GetSymbolUseAtLocation(Line.fromZ linePos.Line, lexerSymbol.Ident.idRange.EndColumn, line.ToString(), lexerSymbol.FullIsland) + return! checkResults.GetSymbolUseAtLocation(Line.fromZ linePos.Line, lexerSymbol.Ident.idRange.EndColumn, line.ToString(), lexerSymbol.FullIsland, userOpName=userOpName) } |> liftAsync do! Option.guard symbol.IsNone diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index f587c404edf..9564ff4b27a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -121,7 +121,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let! sourceText = context.Document.GetTextAsync() |> Async.AwaitTask let getMemberByLocation(name, range: range) = let lineStr = sourceText.Lines.[range.EndLine-1].ToString() - results.GetSymbolUseAtLocation(range.EndLine, range.EndColumn, lineStr, [name]) + results.GetSymbolUseAtLocation(range.EndLine, range.EndColumn, lineStr, [name], userOpName=userOpName) let! implementedMemberSignatures = InterfaceStubGenerator.getImplementedMemberSignatures getMemberByLocation displayContext state.InterfaceData let newSourceText = applyImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode @@ -175,7 +175,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let lineContents = textLine.ToString() let! options = context.Document.GetOptionsAsync(cancellationToken) let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, lineContents, symbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, lineContents, symbol.FullIsland, userOpName=userOpName) let! entity, displayContext = match symbolUse.Symbol with | :? FSharpEntity as entity -> diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 44ed6b33153..a01fbe9efff 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -59,7 +59,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider let defines = CompilerEnvironment.GetCompilationDefinesForEditing (document.FilePath, options.OtherOptions |> Seq.toList) let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() - let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland) + let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) let symbolName = symbolUse.Symbol.DisplayName match symbolUse.Symbol with diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index e3067605807..e0fa2cd5c74 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -84,7 +84,7 @@ type internal FSharpHelpContextService let! (s,colAtEndOfNames, _) = QuickParse.GetCompleteIdentifierIsland false lineText col if check.HasFullTypeCheckInfo then let qualId = PrettyNaming.GetLongNameFromString s - return! check.GetF1KeywordAlternate(Line.fromZ line, colAtEndOfNames, lineText, qualId) + return! check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId) else return! None with e -> diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 0aa0e139d5f..923ab5672fa 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -56,7 +56,7 @@ type internal FSharpSignatureHelpProvider let lidEnd = nwpl.LongIdEndLocation // Get the methods - let! methodGroup = checkFileResults.GetMethodsAlternate(lidEnd.Line, lidEnd.Column, "", Some names) + let! methodGroup = checkFileResults.GetMethods(lidEnd.Line, lidEnd.Column, "", Some names) let methods = methodGroup.Methods diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 779c9212d4c..999e4108d51 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -99,6 +99,7 @@ type internal UnusedDeclarationsAnalyzer() = override __.AnalyzeSemanticsAsync(document, cancellationToken) = asyncMaybe { + do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start match getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) with | Some options -> let! sourceText = document.GetTextAsync() diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 2f3722a79c4..c3df4988464 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -170,6 +170,7 @@ type internal UnusedOpensDiagnosticAnalyzer() = override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) = asyncMaybe { + do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync() let checker = getChecker document diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index ef8d74e37cd..1eab782c60b 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -60,7 +60,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName = userOpName) - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=userOpName) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync return [| for symbolUse in symbolUses do diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index 8f497862d90..2661b6e3c59 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -18,7 +18,7 @@ type internal FSharpBraceMatchingService static let userOpName = "BraceMatching" static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, options, position: int) = async { - let! matchedBraces = checker.MatchBracesAlternate(fileName, sourceText.ToString(), options, userOpName = userOpName) + let! matchedBraces = checker.MatchBraces(fileName, sourceText.ToString(), options, userOpName = userOpName) let isPositionInRange range = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range).Contains(position) return matchedBraces |> Array.tryFind(fun (left, right) -> isPositionInRange left || isPositionInRange right) } diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 2691235bf9f..378255a378e 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -155,7 +155,7 @@ type internal InlineRenameService let fcsTextLineNumber = Line.fromZ textLinePos.Line let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true, userOpName = userOpName) - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName) let! declLoc = symbolUse.GetDeclarationLocation(document) return InlineRenameInfo(checker, projectInfoManager, document, sourceText, symbol, symbolUse, declLoc, checkFileResults) :> IInlineRenameInfo } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 666e288e11e..fd49e2fc753 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -40,9 +40,13 @@ type internal FSharpCheckerProvider ( analyzerService: IDiagnosticAnalyzerService ) = + + // Enabling this would mean that if devenv.exe goes above 2.3GB we do a one-off downsize of the F# Compiler Service caches + //let maxMemory = 2300 + let checker = lazy - let checker = FSharpChecker.Create(projectCacheSize = 200, keepAllBackgroundResolutions = false) + let checker = FSharpChecker.Create(projectCacheSize = 200, keepAllBackgroundResolutions = false (* , MaxMemory = 2300 *)) // This is one half of the bridge between the F# background builder and the Roslyn analysis engine. // When the F# background builder refreshes the background semantic build context for a file, diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index addc9ea37ab..da133fa4962 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -78,7 +78,7 @@ module internal SymbolHelpers = let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = textLinePos.Line + 1 - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland, userOpName=userOpName) let! declLoc = symbolUse.GetDeclarationLocation(document) let newText = textChanger originalText // defer finding all symbol uses throughout the solution diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index d2dd2bcc7a9..a0e86ce47eb 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -57,8 +57,8 @@ type internal FSharpFindUsagesService let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) - let! declaration = checkFileResults.GetDeclarationLocationAlternate (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) |> liftAsync + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, userOpName=userOpName) + let! declaration = checkFileResults.GetDeclarationLocation (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false, userOpName=userOpName) |> liftAsync let tags = GlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) let declarationRange = diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index f9505ea36f5..c8fadf89976 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -68,7 +68,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: Project let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument,projectOptions,allowStaleResults=true,sourceText=sourceText, userOpName = userOpName) let idRange = lexerSymbol.Ident.idRange - let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) let symbol = fsSymbolUse.Symbol // if the tooltip was spawned in an implementation file and we have a range targeting // a signature file, try to find the corresponding implementation file and target the @@ -221,12 +221,8 @@ type internal FSharpGoToDefinitionService let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false) let idRange = lexerSymbol.Ident.idRange - let! declarations = - checkFileResults.GetDeclarationLocationAlternate - (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, preferSignature) - |> liftAsync - let! targetSymbolUse = - checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + let! declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, preferSignature, userOpName=userOpName) |> liftAsync + let! targetSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) match declarations with | FSharpFindDeclResult.DeclFound targetRange -> @@ -248,10 +244,7 @@ type internal FSharpGoToDefinitionService let navItem = FSharpNavigableItem (implDocument, implTextSpan) return navItem else // jump from implementation to the corresponding signature - let! declarations = - checkFileResults.GetDeclarationLocationAlternate - (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, true) - |> liftAsync + let! declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, true, userOpName=userOpName) |> liftAsync match declarations with | FSharpFindDeclResult.DeclFound targetRange -> let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index fe04f655c05..5e8e613f99d 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -60,15 +60,15 @@ module private FSharpQuickInfo = let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, sourceText=extSourceText, userOpName = userOpName) let! extTooltipText = - extCheckFileResults.GetStructuredToolTipTextAlternate - (declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland, FSharpTokenTag.IDENT) |> liftAsync + extCheckFileResults.GetStructuredToolTipText + (declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland, FSharpTokenTag.IDENT, userOpName=userOpName) |> liftAsync match extTooltipText with | FSharpToolTipText [] | FSharpToolTipText [FSharpStructuredToolTipElement.None] -> return! None | extTooltipText -> let! extSymbolUse = - extCheckFileResults.GetSymbolUseAtLocation(declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland) + extCheckFileResults.GetSymbolUseAtLocation(declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland, userOpName=userOpName) return { StructuredText = extTooltipText Span = RoslynHelpers.FSharpRangeToTextSpan (extSourceText, extLexerSymbol.Range) @@ -97,14 +97,14 @@ module private FSharpQuickInfo = let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, userOpName=userOpName) /// Gets the tooltip information for the orignal target let getTargetSymbolTooltip () = asyncMaybe { let! targetTooltip = - checkFileResults.GetStructuredToolTipTextAlternate - (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, FSharpTokenTag.IDENT) |> liftAsync + checkFileResults.GetStructuredToolTipText + (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland, FSharpTokenTag.IDENT, userOpName=userOpName) |> liftAsync match targetTooltip with | FSharpToolTipText [] @@ -123,9 +123,7 @@ module private FSharpQuickInfo = return symbolUse, None, Some targetTooltipInfo else // find the declaration location of the target symbol, with a preference for signature files - let! findSigDeclarationResult = - checkFileResults.GetDeclarationLocationAlternate - (idRange.StartLine, idRange.EndColumn, lineText, lexerSymbol.FullIsland, preferFlag=true) |> liftAsync + let! findSigDeclarationResult = checkFileResults.GetDeclarationLocation (idRange.StartLine, idRange.EndColumn, lineText, lexerSymbol.FullIsland, preferFlag=true, userOpName=userOpName) |> liftAsync // it is necessary to retrieve the backup tooltip info because this acquires // the textSpan designating where we want the tooltip to appear. @@ -141,9 +139,7 @@ module private FSharpQuickInfo = // is not the corresponding module implementation file for that signature, // the doccoms from the signature will overwrite any doccoms that might be // present on the definition/implementation - let! findImplDefinitionResult = - checkFileResults.GetDeclarationLocationAlternate - (idRange.StartLine, idRange.EndColumn, lineText, lexerSymbol.FullIsland, preferFlag=false) |> liftAsync + let! findImplDefinitionResult = checkFileResults.GetDeclarationLocation (idRange.StartLine, idRange.EndColumn, lineText, lexerSymbol.FullIsland, preferFlag=false, userOpName=userOpName) |> liftAsync match findImplDefinitionResult with | FSharpFindDeclResult.DeclNotFound _ -> return symbolUse, Some sigTooltipInfo, None @@ -178,12 +174,12 @@ type internal FSharpQuickInfoProvider let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true) - let! res = checkFileResults.GetStructuredToolTipTextAlternate (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT) |> liftAsync + let! res = checkFileResults.GetStructuredToolTipText (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT, userOpName=FSharpQuickInfo.userOpName) |> liftAsync match res with | FSharpToolTipText [] | FSharpToolTipText [FSharpStructuredToolTipElement.None] -> return! None | _ -> - let! symbolUse = checkFileResults.GetSymbolUseAtLocation (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, userOpName=FSharpQuickInfo.userOpName) return res, RoslynHelpers.FSharpRangeToTextSpan (sourceText, symbol.Range), symbolUse.Symbol, symbol.Kind } diff --git a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs index cb7f6fdfa8e..dc85178fe4b 100644 --- a/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs +++ b/vsintegration/src/FSharp.LanguageService/BackgroundRequests.fs @@ -115,7 +115,7 @@ type internal FSharpLanguageServiceBackgroundRequests // Do brace matching if required if req.ResultSink.BraceMatching then // Record brace-matching - let braceMatches = interactiveChecker.MatchBracesAlternate(req.FileName,req.Text,checkOptions) |> Async.RunSynchronously + let braceMatches = interactiveChecker.MatchBraces(req.FileName,req.Text,checkOptions) |> Async.RunSynchronously let mutable pri = 0 for (b1,b2) in braceMatches do diff --git a/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs b/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs index 5ae4eb5200a..34e98472bbd 100644 --- a/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs +++ b/vsintegration/src/FSharp.LanguageService/GotoDefinition.fs @@ -63,7 +63,7 @@ module internal GotoDefinition = Strings.Errors.GotoDefinitionFailed_NotIdentifier () |> GotoDefinitionResult.MakeError else - match typedResults.GetDeclarationLocationAlternate (line+1, colIdent, lineStr, qualId, false) |> Async.RunSynchronously with + match typedResults.GetDeclarationLocation (line+1, colIdent, lineStr, qualId, false) |> Async.RunSynchronously with | FSharpFindDeclResult.DeclFound m -> let span = TextSpan (iStartLine = m.StartLine-1, iEndLine = m.StartLine-1, iStartIndex = m.StartColumn, iEndIndex = m.StartColumn) GotoDefinitionResult.MakeSuccess(m.FileName, span) diff --git a/vsintegration/src/FSharp.LanguageService/Intellisense.fs b/vsintegration/src/FSharp.LanguageService/Intellisense.fs index 92c181c055d..d2f28dd98d9 100644 --- a/vsintegration/src/FSharp.LanguageService/Intellisense.fs +++ b/vsintegration/src/FSharp.LanguageService/Intellisense.fs @@ -321,7 +321,7 @@ type internal FSharpIntellisenseInfo // This can happen e.g. if you are typing quickly and the typecheck results are stale enough that you don't have a captured resolution for // the name you just typed, but fresh enough that you do have the right name-resolution-environment to look up the name. let lidEnd = nwpl.LongIdEndLocation - let methods = typedResults.GetMethodsAlternate(lidEnd.Line, lidEnd.Column, "", Some names) |> Async.RunSynchronously + let methods = typedResults.GetMethods(lidEnd.Line, lidEnd.Column, "", Some names) |> Async.RunSynchronously // If the name is an operator ending with ">" then it is a mistake // we can't tell whether " >(" is a generic method call or an operator use @@ -445,7 +445,7 @@ type internal FSharpIntellisenseInfo // Correct the identifier (e.g. to correctly handle active pattern names that end with "BAR" token) let tokenTag = QuickParse.CorrectIdentifierToken s tokenTag - let dataTip = typedResults.GetStructuredToolTipTextAlternate(Range.Line.fromZ line, colAtEndOfNames, lineText, qualId, tokenTag) |> Async.RunSynchronously + let dataTip = typedResults.GetStructuredToolTipText(Range.Line.fromZ line, colAtEndOfNames, lineText, qualId, tokenTag) |> Async.RunSynchronously match dataTip with | FSharpStructuredToolTipText.FSharpToolTipText [] when makeSecondAttempt -> getDataTip true @@ -587,7 +587,7 @@ type internal FSharpIntellisenseInfo | Some(s,colAtEndOfNames, _) -> if typedResults.HasFullTypeCheckInfo then let qualId = PrettyNaming.GetLongNameFromString s - match typedResults.GetF1KeywordAlternate(Range.Line.fromZ line,colAtEndOfNames, lineText, qualId) |> Async.RunSynchronously with + match typedResults.GetF1Keyword(Range.Line.fromZ line,colAtEndOfNames, lineText, qualId) |> Async.RunSynchronously with | Some s -> Some s | None -> None else None diff --git a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs index 2e37987155f..f1d09bc9426 100644 --- a/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/unittests/GoToDefinitionServiceTests.fs @@ -35,6 +35,8 @@ open UnitTests.TestLib.LanguageService [][] module GoToDefinitionServiceTests = + let userOpName = "GoToDefinitionServiceTests" + let private findDefinition ( checker: FSharpChecker, @@ -51,11 +53,9 @@ module GoToDefinitionServiceTests = let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName="GoToDefinitionServiceTests") |> Async.RunSynchronously + let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true, userOpName=userOpName) |> Async.RunSynchronously - let declarations = - checkFileResults.GetDeclarationLocationAlternate - (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) |> Async.RunSynchronously + let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false, userOpName=userOpName) |> Async.RunSynchronously match declarations with | FSharpFindDeclResult.DeclFound range -> return range From fab4971ec37938e4e4f0fd5f3f1dfd32afe8cd4e Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 18:28:32 +0100 Subject: [PATCH 05/11] even cleaner diagnostics --- src/fsharp/vs/Reactor.fs | 3 ++- src/fsharp/vs/service.fs | 5 ++++- .../src/FSharp.Editor/Classification/ColorizationService.fs | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index 07f97f593b0..35ca3296d20 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -79,7 +79,8 @@ type Reactor() = time.Stop() let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- {0}.{1}, took {2} ms", userOpName, opName, span.TotalMilliseconds) + let taken = span.TotalMilliseconds + Trace.TraceInformation("Reactor: <-- {0}.{1}, took {2} ms {3}", userOpName, opName, span.TotalMilliseconds, (if taken > 10000.0 then "CATASTROPHIC" elif taken > 3000.0 then "AGONIZING" elif taken > 1000.0 then "SLOW" elif taken > 500.0 then "SPLUTTER" else "")) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> Trace.TraceInformation("Reactor: --> wait for background, remaining {0}", inbox.CurrentQueueLength) diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index aeb71b95030..9a1cc85b497 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -2263,7 +2263,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> return parseResults | _ -> - Trace.TraceInformation("Reactor: operation {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) + Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) foregroundParseCount <- foregroundParseCount + 1 let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement @@ -2404,6 +2404,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent | None -> return None | Some (_, _, Some x) -> return Some x | Some (builder, creationErrors, None) -> + Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", filename) let! tcPrior = execWithReactorAsync <| fun ctok -> DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok @@ -2432,6 +2433,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults | _ -> + Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) let! checkAnswer = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) @@ -2455,6 +2457,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, checkResults) -> return parseResults, FSharpCheckFileAnswer.Succeeded checkResults | _ -> + Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "ParseAndCheckFileInProject.CacheMiss", filename) // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, // but results with non cooperative blocking when a firts file from a project opened. let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index 032df0c7bdd..367e44040fc 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -21,7 +21,7 @@ type internal FSharpColorizationService checkerProvider: FSharpCheckerProvider, projectInfoManager: ProjectInfoManager ) = - static let userOpName = "FSharpColorizationService" + static let userOpName = "SemanticColorization" interface IEditorClassificationService with // Do not perform classification if we don't have project options (#defines matter) @@ -36,6 +36,7 @@ type internal FSharpColorizationService member this.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = asyncMaybe { + do! Async.Sleep 500 |> liftAsync // sleep a while before doing semantic colorization to get out of the way of typing - longer than Roslyn normally waits because this is a more expensive operation for F# let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) From 03bc43c6f96bc5ae7bd070ef7db73776cdfbd3dd Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 18:29:01 +0100 Subject: [PATCH 06/11] cleanup diagnostics --- src/fsharp/vs/service.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 9a1cc85b497..23889dbd07a 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -2296,7 +2296,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent ) member bc.MatchBraces(filename:string, source, options, userOpName) = - reactor.EnqueueAndAwaitOpAsync(userOpName, "MatchBraces ", filename, fun ctok -> + reactor.EnqueueAndAwaitOpAsync(userOpName, "MatchBraces", filename, fun ctok -> cancellable { let! builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement From ff5df2a769d184699b2f26604e23577533ee0c37 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 18:58:47 +0100 Subject: [PATCH 07/11] default tuning --- tests/service/EditorTests.fs | 4 ++-- vsintegration/Utils/LanguageServiceProfiling/Program.fs | 2 +- .../FSharp.Editor/Classification/ColorizationService.fs | 2 +- .../Diagnostics/SimplifyNameDiagnosticAnalyzer.fs | 4 ++-- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../Diagnostics/UnusedOpensDiagnosticAnalyzer.fs | 2 +- vsintegration/src/FSharp.Editor/Options/EditorOptions.fs | 8 ++++++++ 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 4c53ea6f96e..6bd6cf30739 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -83,13 +83,13 @@ let ``Intro test`` () = msg.Message.Contains("Missing qualification after '.'") |> shouldEqual true // Get tool tip at the specified location - let tip = typeCheckResults.GetToolTipTextAlternate(4, 7, inputLines.[1], ["foo"], identToken) |> Async.RunSynchronously + let tip = typeCheckResults.GetToolTipText(4, 7, inputLines.[1], ["foo"], identToken) |> Async.RunSynchronously // (sprintf "%A" tip).Replace("\n","") |> shouldEqual """FSharpToolTipText [Single ("val foo : unit -> unitFull name: Test.foo",None)]""" // Get declarations (autocomplete) for a location let decls = typeCheckResults.GetDeclarationListInfo(Some parseResult, 7, 23, inputLines.[6], [], "msg", (fun _ -> []), fun _ -> false)|> Async.RunSynchronously CollectionAssert.AreEquivalent(stringMethods,[ for item in decls.Items -> item.Name ]) // Get overloads of the String.Concat method - let methods = typeCheckResults.GetMethodsAlternate(5, 27, inputLines.[4], Some ["String"; "Concat"]) |> Async.RunSynchronously + let methods = typeCheckResults.GetMethods(5, 27, inputLines.[4], Some ["String"; "Concat"]) |> Async.RunSynchronously methods.MethodName |> shouldEqual "Concat" diff --git a/vsintegration/Utils/LanguageServiceProfiling/Program.fs b/vsintegration/Utils/LanguageServiceProfiling/Program.fs index 26fd24e8b89..0627d07dd2c 100644 --- a/vsintegration/Utils/LanguageServiceProfiling/Program.fs +++ b/vsintegration/Utils/LanguageServiceProfiling/Program.fs @@ -130,7 +130,7 @@ let main argv = let! fileResults = checkFile fileVersion match fileResults with | Some fileResults -> - let! symbolUse = fileResults.GetSymbolUseAtLocation(options.SymbolPos.Line, options.SymbolPos.Column, getLine(options.SymbolPos.Line),[options.SymbolText], userOpName=userOpName) + let! symbolUse = fileResults.GetSymbolUseAtLocation(options.SymbolPos.Line, options.SymbolPos.Column, getLine(options.SymbolPos.Line),[options.SymbolText]) match symbolUse with | Some symbolUse -> eprintfn "Found symbol %s" symbolUse.Symbol.FullName diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index 367e44040fc..7e064a625d7 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -36,7 +36,7 @@ type internal FSharpColorizationService member this.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = asyncMaybe { - do! Async.Sleep 500 |> liftAsync // sleep a while before doing semantic colorization to get out of the way of typing - longer than Roslyn normally waits because this is a more expensive operation for F# + do! Async.Sleep DefaultTuning.SemanticColorizationInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time let! options = projectInfoManager.TryGetOptionsForDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false, userOpName=userOpName) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index be16aeb1168..72188fc1e45 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -47,6 +47,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) = asyncMaybe { + do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync do! Option.guard Settings.CodeFixes.SimplifyName let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! textVersion = document.GetTextVersionAsync(cancellationToken) @@ -56,7 +57,6 @@ type internal SimplifyNameDiagnosticAnalyzer() = match cache.TryGetValue document.Id with | true, (oldTextVersionHash, diagnostics) when oldTextVersionHash = textVersionHash -> return diagnostics | _ -> - do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start let! sourceText = document.GetTextAsync() let checker = getChecker document let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = true, userOpName=userOpName) @@ -86,7 +86,6 @@ type internal SimplifyNameDiagnosticAnalyzer() = let getNecessaryPlid (plid: string list) : Async = let rec loop (rest: string list) (current: string list) = async { - do! Async.Sleep 10 // be less intrusive, give other work priority most of the time match rest with | [] -> return current | headIdent :: restPlid -> @@ -96,6 +95,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = } loop (List.rev plid) [] + do! Async.Sleep DefaultTuning.SimplifyNameEachItemDelay |> liftAsync // be less intrusive, give other work priority most of the time let! necessaryPlid = getNecessaryPlid plid |> liftAsync match necessaryPlid with diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 999e4108d51..fe919017ffc 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -99,7 +99,7 @@ type internal UnusedDeclarationsAnalyzer() = override __.AnalyzeSemanticsAsync(document, cancellationToken) = asyncMaybe { - do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start + do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time match getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) with | Some options -> let! sourceText = document.GetTextAsync() diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index c3df4988464..64086177a88 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -170,7 +170,7 @@ type internal UnusedOpensDiagnosticAnalyzer() = override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) = asyncMaybe { - do! Async.Sleep 1000 |> liftAsync // sleep a while before beginning work, very often cancelled before we start + do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time let! options = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync() let checker = getChecker document diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 97d7bb233c6..88d40a41c34 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -7,6 +7,14 @@ open Microsoft.VisualStudio.FSharp.UIResources open SettingsPersistence open OptionsUIHelpers + +module DefaultTuning = + let SemanticColorizationInitialDelay = 500 (* milliseconds *) + let UnusedDeclarationsAnalyzerInitialDelay = 1000 (* milliseconds *) + let UnusedOpensAnalyzerInitialDelay = 2000 (* milliseconds *) + let SimplifyNameInitialDelay = 2000 (* milliseconds *) + let SimplifyNameEachItemDelay = 5 (* milliseconds *) + // CLIMutable to make the record work also as a view model [] type IntelliSenseOptions = From a6d1fc3166744a30c4f4b6e1b2eabe5535274f93 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 21:16:06 +0100 Subject: [PATCH 08/11] adjust tuning, more logging --- src/fsharp/vs/Reactor.fs | 24 ++++++++++--------- src/fsharp/vs/Reactor.fsi | 2 +- src/fsharp/vs/service.fs | 14 +++++------ src/fsharp/vs/service.fsi | 3 ++- .../LanguageService/LanguageService.fs | 21 ++++++++-------- .../FSharp.Editor/Options/EditorOptions.fs | 2 +- 6 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index 35ca3296d20..1f3e38d2241 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -19,7 +19,7 @@ type internal IReactorOperations = [] type internal ReactorCommands = /// Kick off a build. - | SetBackgroundOp of (CompilationThreadToken -> bool) option + | SetBackgroundOp of ( (* userOpName: *) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> bool)) option /// Do some work not synchronized in the mailbox. | Op of userOpName: string * opName: string * opArg: string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) /// Finish the background building @@ -80,13 +80,14 @@ type Reactor() = let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then let taken = span.TotalMilliseconds - Trace.TraceInformation("Reactor: <-- {0}.{1}, took {2} ms {3}", userOpName, opName, span.TotalMilliseconds, (if taken > 10000.0 then "CATASTROPHIC" elif taken > 3000.0 then "AGONIZING" elif taken > 1000.0 then "SLOW" elif taken > 500.0 then "SPLUTTER" else "")) + let msg = (if taken > 10000.0 then "BAD-OP: >10s " elif taken > 3000.0 then "BAD-OP: >3s " elif taken > 1000.0 then "BAD-OP: SLOW > 1s" elif taken > 500.0 then "BAD-OP: >0.5s" else "") + Trace.TraceInformation("Reactor: {0}<-- {1}.{2}, took {3} ms", msg, userOpName, opName, span.TotalMilliseconds) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> - Trace.TraceInformation("Reactor: --> wait for background, remaining {0}", inbox.CurrentQueueLength) match bgOpOpt with | None -> () - | Some bgOp -> + | Some (bgUserOpName, bgOpName, bgOpArg, bgOp) -> + Trace.TraceInformation("Reactor: --> wait for background {0}.{1} ({2}), remaining {3}", bgUserOpName, bgOpName, bgOpArg, inbox.CurrentQueueLength) while bgOp ctok do () channel.Reply(()) @@ -97,16 +98,17 @@ type Reactor() = | None -> match bgOpOpt, onComplete with | _, Some onComplete -> onComplete.Reply() - | Some bgOp, None -> - Trace.TraceInformation("Reactor: --> background step", inbox.CurrentQueueLength) + | Some (bgUserOpName, bgOpName, bgOpArg, bgOp), None -> + Trace.TraceInformation("Reactor: --> background step {0}.{1} ({2})", bgUserOpName, bgOpName, bgOpArg) let time = Stopwatch() time.Start() let res = bgOp ctok time.Stop() - let span = time.Elapsed + let taken = time.Elapsed.TotalMilliseconds //if span.TotalMilliseconds > 100.0 then - Trace.TraceInformation("Reactor: <-- background step, took {0}ms", span.TotalMilliseconds) - return! loop ((if res then Some bgOp else None), onComplete, true) + let msg = (if taken > 10000.0 then "BAD-BG-SLICE: >10s " elif taken > 3000.0 then "BAD-BG-SLICE: >3s " elif taken > 1000.0 then "BAD-BG-SLICE: > 1s" else "") + Trace.TraceInformation("Reactor: {0}<-- background step, took {1}ms", msg, taken) + return! loop ((if res then bgOpOpt else None), onComplete, true) | None, None -> failwith "unreachable, should have used inbox.Receive" } async { @@ -118,9 +120,9 @@ type Reactor() = } // [Foreground Mailbox Accessors] ----------------------------------------------------------- - member r.SetBackgroundOp(build) = + member r.SetBackgroundOp(bgOpOpt) = Trace.TraceInformation("Reactor: enqueue start background, length {0}", builder.CurrentQueueLength) - builder.Post(SetBackgroundOp build) + builder.Post(SetBackgroundOp bgOpOpt) member r.EnqueueOp(userOpName, opName, opArg, op) = Trace.TraceInformation("Reactor: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) diff --git a/src/fsharp/vs/Reactor.fsi b/src/fsharp/vs/Reactor.fsi index 336cafcde39..bc1c1f67ce6 100755 --- a/src/fsharp/vs/Reactor.fsi +++ b/src/fsharp/vs/Reactor.fsi @@ -26,7 +26,7 @@ type internal Reactor = /// Set the background building function, which is called repeatedly /// until it returns 'false'. If None then no background operation is used. - member SetBackgroundOp : build:(CompilationThreadToken -> bool) option -> unit + member SetBackgroundOp : ( (* userOpName:*) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> bool)) option -> unit /// Block until the current implicit background build is complete. Unit test only. member WaitForBackgroundOpCompletion : unit -> unit diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 23889dbd07a..3be0477eaab 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -2594,8 +2594,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent return options, errors.Diagnostics }) - member bc.InvalidateConfiguration(options : FSharpProjectOptions, startBackgroundCompile, userOpName) = - let startBackgroundCompile = defaultArg startBackgroundCompile implicitlyStartBackgroundWork + member bc.InvalidateConfiguration(options : FSharpProjectOptions, startBackgroundCompileIfAlreadySeen, userOpName) = + let startBackgroundCompileIfAlreadySeen = defaultArg startBackgroundCompileIfAlreadySeen implicitlyStartBackgroundWork // This operation can't currently be cancelled nor awaited reactor.EnqueueOp(userOpName, "InvalidateConfiguration", options.ProjectFileName, fun ctok -> // If there was a similar entry then re-establish an empty builder . This is a somewhat arbitrary choice - it @@ -2607,9 +2607,9 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let newBuilderInfo = CreateOneIncrementalBuilder (ctok, options, userOpName) |> Cancellable.runWithoutCancellation incrementalBuildersCache.Set(ctok, options, newBuilderInfo) - // Start working on the project. Also a somewhat arbitrary choice - if startBackgroundCompile then - bc.CheckProjectInBackground(options, userOpName)) + // Start working on the project. Also a somewhat arbitrary choice + if startBackgroundCompileIfAlreadySeen then + bc.CheckProjectInBackground(options, userOpName + ".StartBackgroundCompile")) member bc.NotifyProjectCleaned (options : FSharpProjectOptions, userOpName) = reactor.EnqueueAndAwaitOpAsync(userOpName, "NotifyProjectCleaned", options.ProjectFileName, fun ctok -> @@ -2625,7 +2625,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent }) member bc.CheckProjectInBackground (options, userOpName) = - reactor.SetBackgroundOp (Some (fun ctok -> + reactor.SetBackgroundOp (Some (userOpName, "CheckProjectInBackground", options.ProjectFileName, (fun ctok -> // The creation of the background builder can't currently be cancelled let builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) |> Cancellable.runWithoutCancellation use _unwind = decrement @@ -2633,7 +2633,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent | None -> false | Some builder -> // The individual steps of the background build can't currently be cancelled - builder.Step(ctok) |> Cancellable.runWithoutCancellation)) + builder.Step(ctok) |> Cancellable.runWithoutCancellation))) member bc.StopBackgroundCompile () = reactor.SetBackgroundOp(None) diff --git a/src/fsharp/vs/service.fsi b/src/fsharp/vs/service.fsi index 8764158a616..9d6f78fcbaa 100755 --- a/src/fsharp/vs/service.fsi +++ b/src/fsharp/vs/service.fsi @@ -586,8 +586,9 @@ type internal FSharpChecker = /// This function is called when the configuration is known to have changed for reasons not encoded in the ProjectOptions. /// For example, dependent references may have been deleted or created. + /// Start a background compile of the project if a project with the same name has already been seen before. /// An optional string used for tracing compiler operations associated with this request. - member InvalidateConfiguration: options: FSharpProjectOptions * ?startBackgroundCompile: bool * ?userOpName: string -> unit + member InvalidateConfiguration: options: FSharpProjectOptions * ?startBackgroundCompileIfAlreadySeen: bool * ?userOpName: string -> unit /// Set the project to be checked in the background. Overrides any previous call to CheckProjectInBackground member CheckProjectInBackground: options: FSharpProjectOptions * ?userOpName: string -> unit diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index fd49e2fc753..9027001e321 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -129,13 +129,13 @@ type internal ProjectInfoManager } /// Update the info for a project in the project table - member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId: ProjectId, site: IProjectSite, workspace: Workspace) = + member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId: ProjectId, site: IProjectSite, workspace: Workspace, userOpName) = this.AddOrUpdateProject(projectId, (fun isRefresh -> let extraProjectInfo = Some(box workspace) let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject let referencedProjects, options = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(tryGetOptionsForReferencedProject, site, site.ProjectFileName(), extraProjectInfo, serviceProvider, true) let referencedProjectIds = referencedProjects |> Array.choose tryGetOrCreateProjectId - checkerProvider.Checker.InvalidateConfiguration(options, startBackgroundCompile = not isRefresh) + checkerProvider.Checker.InvalidateConfiguration(options, startBackgroundCompileIfAlreadySeen = not isRefresh, userOpName= userOpName + ".UpdateProjectInfo") referencedProjectIds, options)) /// Get compilation defines relevant for syntax processing. @@ -335,7 +335,7 @@ and while true do let! siteProvider = inbox.Receive() do! Async.SwitchToContext ctx - this.SetupProjectFile(siteProvider, this.Workspace) } + this.SetupProjectFile(siteProvider, this.Workspace, "SetupProjectsAfterSolutionOpen") } use _ = Events.SolutionEvents.OnAfterOpenProject |> Observable.subscribe ( fun args -> match args.Hierarchy with @@ -351,7 +351,7 @@ and theme.SetColors() /// Sync the information for the project - member this.SyncProject(project: AbstractProject, projectContext: IWorkspaceProjectContext, site: IProjectSite, workspace, forceUpdate) = + member this.SyncProject(project: AbstractProject, projectContext: IWorkspaceProjectContext, site: IProjectSite, workspace, forceUpdate, userOpName) = let wellFormedFilePathSetIgnoreCase (paths: seq) = HashSet(paths |> Seq.filter isPathWellFormed |> Seq.map (fun s -> try System.IO.Path.GetFullPath(s) with _ -> s), StringComparer.OrdinalIgnoreCase) @@ -404,9 +404,10 @@ and // update the cached options if updated then - projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, project.Id, site, project.Workspace) + projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, project.Id, site, project.Workspace, userOpName + ".SyncProject") - member this.SetupProjectFile(siteProvider: IProvideProjectSite, workspace: VisualStudioWorkspaceImpl) = + member this.SetupProjectFile(siteProvider: IProvideProjectSite, workspace: VisualStudioWorkspaceImpl, userOpName) = + let userOpName = userOpName + ".SetupProjectFile" let rec setup (site: IProjectSite) = let projectGuid = Guid(site.ProjectGuid) let projectFileName = site.ProjectFileName() @@ -414,7 +415,7 @@ and let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName) if isNull (workspace.ProjectTracker.GetProject projectId) then - projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, projectId, site, workspace) + projectInfoManager.UpdateProjectInfo(tryGetOrCreateProjectId workspace, projectId, site, workspace, userOpName) let projectContextFactory = package.ComponentModel.GetService(); let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) @@ -434,9 +435,9 @@ and let project = projectContext :?> AbstractProject - this.SyncProject(project, projectContext, site, workspace, forceUpdate=false) + this.SyncProject(project, projectContext, site, workspace, forceUpdate=false, userOpName=userOpName) site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, - AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site, workspace, forceUpdate=true))) + AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site, workspace, forceUpdate=true, userOpName="AdviseProjectSiteChanges."+userOpName))) site.AdviseProjectSiteClosed(FSharpConstants.FSharpLanguageServiceCallbackName, AdviseProjectSiteChanges(fun () -> projectInfoManager.ClearInfoForProject(project.Id) @@ -489,7 +490,7 @@ and | Some (hier, _) -> match hier with | :? IProvideProjectSite as siteProvider when not (IsScript(filename)) -> - this.SetupProjectFile(siteProvider, this.Workspace) + this.SetupProjectFile(siteProvider, this.Workspace, "SetupNewTextView") | _ -> let fileContents = VsTextLines.GetFileContents(textLines, textViewAdapter) this.SetupStandAloneFile(filename, fileContents, this.Workspace, hier) diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 88d40a41c34..1f4c2e4b0a4 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -9,7 +9,7 @@ open OptionsUIHelpers module DefaultTuning = - let SemanticColorizationInitialDelay = 500 (* milliseconds *) + let SemanticColorizationInitialDelay = 0 (* milliseconds *) let UnusedDeclarationsAnalyzerInitialDelay = 1000 (* milliseconds *) let UnusedOpensAnalyzerInitialDelay = 2000 (* milliseconds *) let SimplifyNameInitialDelay = 2000 (* milliseconds *) From f562664e2191665a5d408ac018aa6a01e84bd10e Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 16 May 2017 21:29:51 +0100 Subject: [PATCH 09/11] add test, fix whitespace --- src/fsharp/vs/Reactor.fs | 4 +- .../ProjectWithBuildErrors/App.config | 6 ++ .../ProjectWithBuildErrors/AssemblyInfo.fs | 41 +++++++++ .../ProjectWithBuildErrors/Program.fs | 16 ++++ .../ProjectWithBuildErrors.fsproj | 83 +++++++++++++++++++ .../ProjectWithBuildErrors/packages.config | 4 + 6 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/App.config create mode 100644 tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/AssemblyInfo.fs create mode 100644 tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/Program.fs create mode 100644 tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/ProjectWithBuildErrors.fsproj create mode 100644 tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/packages.config diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index 1f3e38d2241..aabe7371c6c 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -80,7 +80,7 @@ type Reactor() = let span = time.Elapsed //if span.TotalMilliseconds > 100.0 then let taken = span.TotalMilliseconds - let msg = (if taken > 10000.0 then "BAD-OP: >10s " elif taken > 3000.0 then "BAD-OP: >3s " elif taken > 1000.0 then "BAD-OP: SLOW > 1s" elif taken > 500.0 then "BAD-OP: >0.5s" else "") + let msg = (if taken > 10000.0 then "BAD-OP: >10s " elif taken > 3000.0 then "BAD-OP: >3s " elif taken > 1000.0 then "BAD-OP: > 1s " elif taken > 500.0 then "BAD-OP: >0.5s " else "") Trace.TraceInformation("Reactor: {0}<-- {1}.{2}, took {3} ms", msg, userOpName, opName, span.TotalMilliseconds) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> @@ -106,7 +106,7 @@ type Reactor() = time.Stop() let taken = time.Elapsed.TotalMilliseconds //if span.TotalMilliseconds > 100.0 then - let msg = (if taken > 10000.0 then "BAD-BG-SLICE: >10s " elif taken > 3000.0 then "BAD-BG-SLICE: >3s " elif taken > 1000.0 then "BAD-BG-SLICE: > 1s" else "") + let msg = (if taken > 10000.0 then "BAD-BG-SLICE: >10s " elif taken > 3000.0 then "BAD-BG-SLICE: >3s " elif taken > 1000.0 then "BAD-BG-SLICE: > 1s " else "") Trace.TraceInformation("Reactor: {0}<-- background step, took {1}ms", msg, taken) return! loop ((if res then bgOpOpt else None), onComplete, true) | None, None -> failwith "unreachable, should have used inbox.Receive" diff --git a/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/App.config b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/App.config new file mode 100644 index 00000000000..88fa4027bda --- /dev/null +++ b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/AssemblyInfo.fs b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/AssemblyInfo.fs new file mode 100644 index 00000000000..540132cd52e --- /dev/null +++ b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/AssemblyInfo.fs @@ -0,0 +1,41 @@ +namespace ProjectWithBuildErrors.AssemblyInfo + +open System.Reflection +open System.Runtime.CompilerServices +open System.Runtime.InteropServices + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[] +[] +[] +[] +[] +[] +[] +[] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [] +[] +[] + +do + () \ No newline at end of file diff --git a/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/Program.fs b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/Program.fs new file mode 100644 index 00000000000..e4f2e6dd7e6 --- /dev/null +++ b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/Program.fs @@ -0,0 +1,16 @@ +// Learn more about F# at http://fsharp.org +// See the 'F# Tutorial' project for more help. + +let f1 argv = + let x = 1 + 1.0 + let y = argx // this gives + printfn "%A" argv + 0 // return an integer exit code + +[] +let main argv = + //let x = 1 + 1.0 + let y = argx // this gives + printfn "%A" argv + 0 // return an integer exit code + \ No newline at end of file diff --git a/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/ProjectWithBuildErrors.fsproj b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/ProjectWithBuildErrors.fsproj new file mode 100644 index 00000000000..f5fa3cd5fd6 --- /dev/null +++ b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/ProjectWithBuildErrors.fsproj @@ -0,0 +1,83 @@ + + + + + Debug + AnyCPU + 2.0 + 9eda4075-e1e5-4f51-8908-5d608c092254 + Exe + ProjectWithBuildErrors + ProjectWithBuildErrors + v4.5.2 + true + 4.4.1.0 + ProjectWithBuildErrors + + + true + full + false + false + bin\$(Configuration)\ + DEBUG;TRACE + 3 + AnyCPU + bin\$(Configuration)\$(AssemblyName).XML + true + + + pdbonly + true + true + bin\$(Configuration)\ + TRACE + 3 + AnyCPU + bin\$(Configuration)\$(AssemblyName).XML + true + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + + + + + + FSharp.Core + FSharp.Core.dll + $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll + + + + + + ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll + + + + \ No newline at end of file diff --git a/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/packages.config b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/packages.config new file mode 100644 index 00000000000..7d629041bc5 --- /dev/null +++ b/tests/projects/misc/ProjectWithBuildErrors/ProjectWithBuildErrors/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 46b7638d6819cbc114808f1a3be99ab563a28465 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 17 May 2017 00:09:24 +0100 Subject: [PATCH 10/11] cancel background steps --- src/fsharp/vs/Reactor.fs | 55 +++++-- src/fsharp/vs/Reactor.fsi | 5 +- src/fsharp/vs/service.fs | 154 ++++++++++-------- .../FSharp.Editor/Options/EditorOptions.fs | 2 +- 4 files changed, 129 insertions(+), 87 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index aabe7371c6c..8cc27292da6 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -19,7 +19,7 @@ type internal IReactorOperations = [] type internal ReactorCommands = /// Kick off a build. - | SetBackgroundOp of ( (* userOpName: *) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> bool)) option + | SetBackgroundOp of ( (* userOpName: *) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> CancellationToken -> bool)) option /// Do some work not synchronized in the mailbox. | Op of userOpName: string * opName: string * opArg: string * CancellationToken * (CompilationThreadToken -> unit) * (unit -> unit) /// Finish the background building @@ -39,13 +39,14 @@ type Reactor() = // so that when the reactor picks up a thread from the threadpool we can set the culture let culture = new CultureInfo(CultureInfo.CurrentUICulture.Name) + let mutable bgOpCts = new CancellationTokenSource() /// Mailbox dispatch function. let builder = MailboxProcessor<_>.Start <| fun inbox -> // Async workflow which receives messages and dispatches to worker functions. let rec loop (bgOpOpt, onComplete, bg) = - async { //Trace.TraceInformation("Reactor: receiving..., remaining {0}", inbox.CurrentQueueLength) + async { //Trace.TraceInformation("FCS: receiving..., remaining {0}", inbox.CurrentQueueLength) // Explanation: The reactor thread acts as the compilation thread in hosted scenarios let ctok = AssumeCompilationThreadWithoutEvidence() @@ -68,11 +69,12 @@ type Reactor() = #endif match msg with | Some (SetBackgroundOp bgOpOpt) -> - //Trace.TraceInformation("Reactor: --> set background op, remaining {0}", inbox.CurrentQueueLength) + Trace.TraceInformation("FCS: --> set background op, remaining {0}", inbox.CurrentQueueLength) return! loop (bgOpOpt, onComplete, false) + | Some (Op (userOpName, opName, opArg, ct, op, ccont)) -> if ct.IsCancellationRequested then ccont() else - Trace.TraceInformation("Reactor: --> {0}.{1} ({2}), remaining {3}", userOpName, opName, opArg, inbox.CurrentQueueLength) + Trace.TraceInformation("FCS: --> {0}.{1} ({2}), remaining {3}", userOpName, opName, opArg, inbox.CurrentQueueLength) let time = Stopwatch() time.Start() op ctok @@ -81,33 +83,49 @@ type Reactor() = //if span.TotalMilliseconds > 100.0 then let taken = span.TotalMilliseconds let msg = (if taken > 10000.0 then "BAD-OP: >10s " elif taken > 3000.0 then "BAD-OP: >3s " elif taken > 1000.0 then "BAD-OP: > 1s " elif taken > 500.0 then "BAD-OP: >0.5s " else "") - Trace.TraceInformation("Reactor: {0}<-- {1}.{2}, took {3} ms", msg, userOpName, opName, span.TotalMilliseconds) + if msg = "" then + Trace.TraceInformation("FCS: <-- {0}.{1}, took {2} ms ({3})", userOpName, opName, span.TotalMilliseconds, opArg) + else + Trace.TraceWarning("FCS: {0}<-- {1}.{2}, took {3} ms ({4})", msg, userOpName, opName, span.TotalMilliseconds, opArg) return! loop (bgOpOpt, onComplete, false) | Some (WaitForBackgroundOpCompletion channel) -> match bgOpOpt with | None -> () | Some (bgUserOpName, bgOpName, bgOpArg, bgOp) -> - Trace.TraceInformation("Reactor: --> wait for background {0}.{1} ({2}), remaining {3}", bgUserOpName, bgOpName, bgOpArg, inbox.CurrentQueueLength) - while bgOp ctok do + Trace.TraceInformation("FCS: --> wait for background {0}.{1} ({2}), remaining {3}", bgUserOpName, bgOpName, bgOpArg, inbox.CurrentQueueLength) + bgOpCts <- new CancellationTokenSource() + while not bgOpCts.IsCancellationRequested && bgOp ctok bgOpCts.Token do () + + if bgOpCts.IsCancellationRequested then + Trace.TraceInformation("FCS: <-- wait for background was cancelled {0}.{1}", bgUserOpName, bgOpName) + channel.Reply(()) return! loop (None, onComplete, false) + | Some (CompleteAllQueuedOps channel) -> - Trace.TraceInformation("Reactor: --> stop background work and complete all queued ops, remaining {0}", inbox.CurrentQueueLength) + Trace.TraceInformation("FCS: --> stop background work and complete all queued ops, remaining {0}", inbox.CurrentQueueLength) return! loop (None, Some channel, false) + | None -> match bgOpOpt, onComplete with | _, Some onComplete -> onComplete.Reply() | Some (bgUserOpName, bgOpName, bgOpArg, bgOp), None -> - Trace.TraceInformation("Reactor: --> background step {0}.{1} ({2})", bgUserOpName, bgOpName, bgOpArg) + Trace.TraceInformation("FCS: --> background step {0}.{1} ({2})", bgUserOpName, bgOpName, bgOpArg) let time = Stopwatch() time.Start() - let res = bgOp ctok + bgOpCts <- new CancellationTokenSource() + let res = bgOp ctok bgOpCts.Token + if bgOpCts.IsCancellationRequested then + Trace.TraceInformation("FCS: <-- background step {0}.{1}, was cancelled", bgUserOpName, bgOpName) time.Stop() let taken = time.Elapsed.TotalMilliseconds //if span.TotalMilliseconds > 100.0 then let msg = (if taken > 10000.0 then "BAD-BG-SLICE: >10s " elif taken > 3000.0 then "BAD-BG-SLICE: >3s " elif taken > 1000.0 then "BAD-BG-SLICE: > 1s " else "") - Trace.TraceInformation("Reactor: {0}<-- background step, took {1}ms", msg, taken) + if msg = "" then + Trace.TraceInformation("FCS: <-- background step, took {0}ms", taken) + else + Trace.TraceWarning("FCS: {0}<-- background step, took {1}ms", msg, taken) return! loop ((if res then bgOpOpt else None), onComplete, true) | None, None -> failwith "unreachable, should have used inbox.Receive" } @@ -121,15 +139,20 @@ type Reactor() = // [Foreground Mailbox Accessors] ----------------------------------------------------------- member r.SetBackgroundOp(bgOpOpt) = - Trace.TraceInformation("Reactor: enqueue start background, length {0}", builder.CurrentQueueLength) + Trace.TraceInformation("FCS: enqueue start background, length {0}", builder.CurrentQueueLength) + bgOpCts.Cancel() builder.Post(SetBackgroundOp bgOpOpt) + member r.CancelBackgroundOp() = + Trace.TraceInformation("FCS: trying to cancel any active background work") + bgOpCts.Cancel() + member r.EnqueueOp(userOpName, opName, opArg, op) = - Trace.TraceInformation("Reactor: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) + Trace.TraceInformation("FCS: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) builder.Post(Op(userOpName, opName, opArg, CancellationToken.None, op, (fun () -> ()))) member r.EnqueueOpPrim(userOpName, opName, opArg, ct, op, ccont) = - Trace.TraceInformation("Reactor: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) + Trace.TraceInformation("FCS: enqueue {0}.{1} ({2}), length {3}", userOpName, opName, opArg, builder.CurrentQueueLength) builder.Post(Op(userOpName, opName, opArg, ct, op, ccont)) member r.CurrentQueueLength = @@ -137,12 +160,12 @@ type Reactor() = // This is for testing only member r.WaitForBackgroundOpCompletion() = - Trace.TraceInformation("Reactor: enqueue wait for background, length {0}", builder.CurrentQueueLength) + Trace.TraceInformation("FCS: enqueue wait for background, length {0}", builder.CurrentQueueLength) builder.PostAndReply WaitForBackgroundOpCompletion // This is for testing only member r.CompleteAllQueuedOps() = - Trace.TraceInformation("Reactor: enqueue wait for all ops, length {0}", builder.CurrentQueueLength) + Trace.TraceInformation("FCS: enqueue wait for all ops, length {0}", builder.CurrentQueueLength) builder.PostAndReply CompleteAllQueuedOps member r.EnqueueAndAwaitOpAsync (userOpName, opName, opArg, f) = diff --git a/src/fsharp/vs/Reactor.fsi b/src/fsharp/vs/Reactor.fsi index bc1c1f67ce6..c8aba05255e 100755 --- a/src/fsharp/vs/Reactor.fsi +++ b/src/fsharp/vs/Reactor.fsi @@ -26,7 +26,10 @@ type internal Reactor = /// Set the background building function, which is called repeatedly /// until it returns 'false'. If None then no background operation is used. - member SetBackgroundOp : ( (* userOpName:*) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> bool)) option -> unit + member SetBackgroundOp : ( (* userOpName:*) string * (* opName: *) string * (* opArg: *) string * (CompilationThreadToken -> CancellationToken -> bool)) option -> unit + + /// Cancel any work being don by the background building function. + member CancelBackgroundOp : unit -> unit /// Block until the current implicit background build is complete. Unit test only. member WaitForBackgroundOpCompletion : unit -> unit diff --git a/src/fsharp/vs/service.fs b/src/fsharp/vs/service.fs index 3be0477eaab..f99408000f3 100644 --- a/src/fsharp/vs/service.fs +++ b/src/fsharp/vs/service.fs @@ -1419,7 +1419,7 @@ module internal Parser = type TypeCheckAborted = Yes | No of TypeCheckInfo // Type check a single file against an initial context, gleaning both errors and intellisense information. - let TypeCheckOneFile + let CheckOneFile (parseResults: FSharpParseFileResults, source: string, mainInputFileName: string, @@ -1537,7 +1537,7 @@ module internal Parser = |> Eventually.repeatedlyProgressUntilDoneOrTimeShareOverOrCanceled maxTimeShareMilliseconds ct (fun ctok f -> f ctok) |> Eventually.forceAsync (fun work -> - reactorOps.EnqueueAndAwaitOpAsync(userOpName, "TypeCheckOneFile.Fragment", mainInputFileName, + reactorOps.EnqueueAndAwaitOpAsync(userOpName, "CheckOneFile.Fragment", mainInputFileName, fun ctok -> // This work is not cancellable let res = @@ -2097,12 +2097,13 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// creates an incremental builder used by the command line compiler. let CreateOneIncrementalBuilder (ctok, options:FSharpProjectOptions, userOpName) = cancellable { - + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CreateOneIncrementalBuilder", options.ProjectFileName) let projectReferences = [ for (nm,opts) in options.ReferencedProjects -> { new IProjectReference with member x.EvaluateRawContents(ctok) = cancellable { + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckProjectImpl", nm) let! r = self.ParseAndCheckProjectImpl(opts, ctok, userOpName + ".CheckReferencedProject("+nm+")") return r.RawFSharpAssemblyData } @@ -2263,7 +2264,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent match cachedResults with | Some (parseResults, _checkResults,_,_) -> return parseResults | _ -> - Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseFileInProject.CacheMiss", filename) foregroundParseCount <- foregroundParseCount + 1 let! builderOpt,creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) use _unwind = decrement @@ -2339,7 +2340,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent /// 6. Starts whole project background compilation. /// /// 7. Releases the file "lock". - member private bc.CheckOneFile + member private bc.CheckOneFileImpl (parseResults: FSharpParseFileResults, source: string, fileName: string, @@ -2353,7 +2354,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent async { let beingCheckedFileKey = fileName, options, fileVersion - let stopwatch = Diagnostics.Stopwatch.StartNew() + let stopwatch = Stopwatch.StartNew() let rec loop() = async { // results may appear while we were waiting for the lock, let's recheck if it's the case @@ -2368,8 +2369,8 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent // For scripts, this will have been recorded by GetProjectOptionsFromScript. let loadClosure = scriptClosureCacheLock.AcquireLock (fun ltok -> scriptClosureCache.TryGet (ltok, options)) let! tcErrors, tcFileResult = - Parser.TypeCheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, - tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo, userOpName) + Parser.CheckOneFile(parseResults, source, fileName, options.ProjectFileName, tcPrior.TcConfig, tcPrior.TcGlobals, tcPrior.TcImports, + tcPrior.TcState, loadClosure, tcPrior.Errors, reactorOps, (fun () -> builder.IsAlive), textSnapshotInfo, userOpName) let checkAnswer = MakeCheckFileAnswer(fileName, tcFileResult, options, builder, tcPrior.TcDependencyFiles, creationErrors, parseResults.Errors, tcErrors) bc.RecordTypeCheckFileInProjectResults(fileName, options, parseResults, fileVersion, tcPrior.TimeStamp, Some checkAnswer, source) return checkAnswer @@ -2391,6 +2392,9 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProjectAllowingStaleCachedResults ", filename, action >> cancellable.Return) async { try + if implicitlyStartBackgroundWork then + reactor.CancelBackgroundOp() // cancel the background work, since we will start new work after we're done + let! cachedResults = execWithReactorAsync <| fun ctok -> match incrementalBuildersCache.TryGetAny (ctok, options) with @@ -2404,14 +2408,14 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent | None -> return None | Some (_, _, Some x) -> return Some x | Some (builder, creationErrors, None) -> - Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", filename) + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProjectAllowingStaleCachedResults.CacheMiss", filename) let! tcPrior = execWithReactorAsync <| fun ctok -> DoesNotRequireCompilerThreadTokenAndCouldPossiblyBeMadeConcurrent ctok builder.GetCheckResultsBeforeFileInProjectEvenIfStale filename match tcPrior with | Some tcPrior -> - let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + let! checkResults = bc.CheckOneFileImpl(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) return Some checkResults | None -> return None // the incremental builder was not up to date finally @@ -2422,55 +2426,63 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent member bc.CheckFileInProject(parseResults: FSharpParseFileResults, filename, fileVersion, source, options, textSnapshotInfo, userOpName) = let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "CheckFileInProject", filename, action) async { - let! builderOpt,creationErrors, decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) - use _unwind = decrement - match builderOpt with - | None -> return FSharpCheckFileAnswer.Succeeded (MakeCheckFileResultsEmpty(filename, creationErrors)) - | Some builder -> - // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date - let cachedResults = bc.GetCachedCheckFileResult(builder, filename, source, options) + try + if implicitlyStartBackgroundWork then + reactor.CancelBackgroundOp() // cancel the background work, since we will start new work after we're done + let! builderOpt,creationErrors, decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) + use _unwind = decrement + match builderOpt with + | None -> return FSharpCheckFileAnswer.Succeeded (MakeCheckFileResultsEmpty(filename, creationErrors)) + | Some builder -> + // Check the cache. We can only use cached results when there is no work to do to bring the background builder up-to-date + let cachedResults = bc.GetCachedCheckFileResult(builder, filename, source, options) - match cachedResults with - | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults - | _ -> - Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) - let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) - let! checkAnswer = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) - bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) - return checkAnswer + match cachedResults with + | Some (_, checkResults) -> return FSharpCheckFileAnswer.Succeeded checkResults + | _ -> + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "CheckFileInProject.CacheMiss", filename) + let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) + let! checkAnswer = bc.CheckOneFileImpl(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + return checkAnswer + finally + bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) } /// Parses and checks the source file and returns untyped AST and check results. member bc.ParseAndCheckFileInProject (filename:string, fileVersion, source, options:FSharpProjectOptions, textSnapshotInfo, userOpName) = let execWithReactorAsync action = reactor.EnqueueAndAwaitOpAsync(userOpName, "ParseAndCheckFileInProject", filename, action) async { - let! builderOpt,creationErrors,decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) - use _unwind = decrement - match builderOpt with - | None -> - let parseResults = FSharpParseFileResults(List.toArray creationErrors, None, true, []) - return (parseResults, FSharpCheckFileAnswer.Aborted) + try + if implicitlyStartBackgroundWork then + reactor.CancelBackgroundOp() // cancel the background work, since we will start new work after we're done + let! builderOpt,creationErrors,decrement = execWithReactorAsync (fun ctok -> getOrCreateBuilderAndKeepAlive (ctok, options, userOpName)) + use _unwind = decrement + match builderOpt with + | None -> + let parseResults = FSharpParseFileResults(List.toArray creationErrors, None, true, []) + return (parseResults, FSharpCheckFileAnswer.Aborted) - | Some builder -> - let cachedResults = bc.GetCachedCheckFileResult(builder, filename, source, options) - - match cachedResults with - | Some (parseResults, checkResults) -> return parseResults, FSharpCheckFileAnswer.Succeeded checkResults - | _ -> - Trace.TraceInformation("Reactor: {0}.{1} ({2})", userOpName, "ParseAndCheckFileInProject.CacheMiss", filename) - // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, - // but results with non cooperative blocking when a firts file from a project opened. - let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) + | Some builder -> + let cachedResults = bc.GetCachedCheckFileResult(builder, filename, source, options) + + match cachedResults with + | Some (parseResults, checkResults) -> return parseResults, FSharpCheckFileAnswer.Succeeded checkResults + | _ -> + Trace.TraceInformation("FCS: {0}.{1} ({2})", userOpName, "ParseAndCheckFileInProject.CacheMiss", filename) + // todo this blocks the Reactor queue until all files up to the current are type checked. It's OK while editing the file, + // but results with non cooperative blocking when a firts file from a project opened. + let! tcPrior = execWithReactorAsync <| fun ctok -> builder.GetCheckResultsBeforeFileInProject (ctok, filename) - // Do the parsing. - let! parseErrors, _matchPairs, inputOpt, anyErrors = - execWithReactorAsync <| fun ctok -> - Parser.ParseOneFile (ctok, source, false, true, filename, builder.SourceFiles, builder.TcConfig) |> cancellable.Return + // Do the parsing. + let! parseErrors, _matchPairs, inputOpt, anyErrors = + execWithReactorAsync <| fun ctok -> + Parser.ParseOneFile (ctok, source, false, true, filename, builder.SourceFiles, builder.TcConfig) |> cancellable.Return - let parseResults = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, builder.AllDependenciesDeprecated) - let! checkResults = bc.CheckOneFile(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) - bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) - return parseResults, checkResults + let parseResults = FSharpParseFileResults(parseErrors, inputOpt, anyErrors, builder.AllDependenciesDeprecated) + let! checkResults = bc.CheckOneFileImpl(parseResults, source, filename, options, textSnapshotInfo, fileVersion, builder, tcPrior, creationErrors, userOpName) + return parseResults, checkResults + finally + bc.ImplicitlyStartCheckProjectInBackground(options, userOpName) } /// Fetch the check information from the background compiler (which checks w.r.t. the FileSystem API) @@ -2532,7 +2544,7 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent // NOTE: This creation of the background builder is currently run as uncancellable. Creating background builders is generally // cheap though the timestamp computations look suspicious for transitive project references. - let builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName + ".LogicalTimeStamp") |> Cancellable.runWithoutCancellation + let builderOpt,_creationErrors,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName + ".TryGetLogicalTimeStampForProject") |> Cancellable.runWithoutCancellation use _unwind = decrement match builderOpt with | None -> None @@ -2625,15 +2637,19 @@ type BackgroundCompiler(referenceResolver, projectCacheSize, keepAssemblyContent }) member bc.CheckProjectInBackground (options, userOpName) = - reactor.SetBackgroundOp (Some (userOpName, "CheckProjectInBackground", options.ProjectFileName, (fun ctok -> + reactor.SetBackgroundOp (Some (userOpName, "CheckProjectInBackground", options.ProjectFileName, (fun ctok ct -> // The creation of the background builder can't currently be cancelled - let builderOpt,_,decrement = getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) |> Cancellable.runWithoutCancellation - use _unwind = decrement - match builderOpt with - | None -> false - | Some builder -> - // The individual steps of the background build can't currently be cancelled - builder.Step(ctok) |> Cancellable.runWithoutCancellation))) + match getOrCreateBuilderAndKeepAlive (ctok, options, userOpName) |> Cancellable.run ct with + | ValueOrCancelled.Cancelled _ -> false + | ValueOrCancelled.Value (builderOpt,_,decrement) -> + use _unwind = decrement + match builderOpt with + | None -> false + | Some builder -> + // The individual steps of the background build + match builder.Step(ctok) |> Cancellable.run ct with + | ValueOrCancelled.Value v -> v + | ValueOrCancelled.Cancelled _ -> false))) member bc.StopBackgroundCompile () = reactor.SetBackgroundOp(None) @@ -2848,16 +2864,16 @@ type FSharpChecker(referenceResolver, projectCacheSize, keepAssemblyContents, ke ic.ClearCachesAsync(?userOpName=userOpName) |> Async.Start // this cache clearance is not synchronous, it will happen when the background op gets run member ic.CheckMaxMemoryReached() = - if not maxMemoryReached && System.GC.GetTotalMemory(false) > int64 maxMB * 1024L * 1024L then - Trace.WriteLine("!!!!!!!! MAX MEMORY REACHED, DOWNSIZING F# COMPILER CACHES !!!!!!!!!!!!!!!") - // If the maxMB limit is reached, drastic action is taken - // - reduce strong cache sizes to a minimum - let userOpName = "MaxMemoryReached" - backgroundCompiler.CompleteAllQueuedOps() - maxMemoryReached <- true - braceMatchCache.Resize(AssumeAnyCallerThreadWithoutEvidence(), keepStrongly=10) - backgroundCompiler.DownsizeCaches(userOpName) |> Async.RunSynchronously - maxMemEvent.Trigger( () ) + if not maxMemoryReached && System.GC.GetTotalMemory(false) > int64 maxMB * 1024L * 1024L then + Trace.TraceWarning("!!!!!!!! MAX MEMORY REACHED, DOWNSIZING F# COMPILER CACHES !!!!!!!!!!!!!!!") + // If the maxMB limit is reached, drastic action is taken + // - reduce strong cache sizes to a minimum + let userOpName = "MaxMemoryReached" + backgroundCompiler.CompleteAllQueuedOps() + maxMemoryReached <- true + braceMatchCache.Resize(AssumeAnyCallerThreadWithoutEvidence(), keepStrongly=10) + backgroundCompiler.DownsizeCaches(userOpName) |> Async.RunSynchronously + maxMemEvent.Trigger( () ) // This is for unit testing only member ic.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() = @@ -3009,7 +3025,7 @@ type FsiInteractiveChecker(referenceResolver, reactorOps: IReactorOperations, tc CompileOptions.ParseCompilerOptions (ignore, fsiCompilerOptions, [ ]) let loadClosure = LoadClosure.ComputeClosureOfSourceText(ctok, referenceResolver, defaultFSharpBinariesDir, filename, source, CodeContext.Editing, tcConfig.useSimpleResolution, tcConfig.useFsiAuxLib, new Lexhelp.LexResourceManager(), applyCompilerOptions, assumeDotNetFramework) - let! tcErrors, tcFileResult = Parser.TypeCheckOneFile(parseResults, source, filename, "project", tcConfig, tcGlobals, tcImports, tcState, Some loadClosure, backgroundDiagnostics, reactorOps, (fun () -> true), None, userOpName) + let! tcErrors, tcFileResult = Parser.CheckOneFile(parseResults, source, filename, "project", tcConfig, tcGlobals, tcImports, tcState, Some loadClosure, backgroundDiagnostics, reactorOps, (fun () -> true), None, userOpName) return match tcFileResult with diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 1f4c2e4b0a4..de08c15638b 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -10,7 +10,7 @@ open OptionsUIHelpers module DefaultTuning = let SemanticColorizationInitialDelay = 0 (* milliseconds *) - let UnusedDeclarationsAnalyzerInitialDelay = 1000 (* milliseconds *) + let UnusedDeclarationsAnalyzerInitialDelay = 0 (* milliseconds *) let UnusedOpensAnalyzerInitialDelay = 2000 (* milliseconds *) let SimplifyNameInitialDelay = 2000 (* milliseconds *) let SimplifyNameEachItemDelay = 5 (* milliseconds *) From 73772f99dcae5be740eb9b0377961c0eebea2993 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 13 Jul 2017 19:36:14 +0100 Subject: [PATCH 11/11] reduce changes --- src/fsharp/vs/Reactor.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fsharp/vs/Reactor.fs b/src/fsharp/vs/Reactor.fs index d980ccae495..87c9c5fa449 100755 --- a/src/fsharp/vs/Reactor.fs +++ b/src/fsharp/vs/Reactor.fs @@ -46,7 +46,7 @@ type Reactor() = // Async workflow which receives messages and dispatches to worker functions. let rec loop (bgOpOpt, onComplete, bg) = - async { //Trace.TraceInformation("FCS: receiving..., remaining {0}", inbox.CurrentQueueLength) + async { //Trace.TraceInformation("Reactor: receiving..., remaining {0}", inbox.CurrentQueueLength) // Explanation: The reactor thread acts as the compilation thread in hosted scenarios let ctok = AssumeCompilationThreadWithoutEvidence() @@ -73,7 +73,7 @@ type Reactor() = #endif match msg with | Some (SetBackgroundOp bgOpOpt) -> - Trace.TraceInformation("FCS: --> set background op, remaining {0}", inbox.CurrentQueueLength) + //Trace.TraceInformation("Reactor: --> set background op, remaining {0}", inbox.CurrentQueueLength) return! loop (bgOpOpt, onComplete, false) | Some (Op (userOpName, opName, opArg, ct, op, ccont)) ->