F# Compiler Services


Compiler Services: Working with symbols

This tutorial demonstrates how to work with symbols provided by the F# compiler. See also project wide analysis for information on symbol references.

NOTE: The FSharp.Compiler.Service API is subject to change when later versions of the nuget package are published.

As usual we start by referencing FSharp.Compiler.Service.dll, opening the relevant namespace and creating an instance of FSharpChecker:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
// Reference F# compiler API
#r "FSharp.Compiler.Service.dll"

open System
open System.IO
open Microsoft.FSharp.Compiler.SourceCodeServices

// Create an interactive checker instance 
let checker = FSharpChecker.Create()

We now perform type checking on the specified input:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
let parseAndTypeCheckSingleFile (file, input) = 
    // Get context representing a stand-alone (script) file
    let projOptions = 
        checker.GetProjectOptionsFromScript(file, input)
        |> Async.RunSynchronously

    let parseFileResults, checkFileResults = 
        checker.ParseAndCheckFileInProject(file, 0, input, projOptions) 
        |> Async.RunSynchronously

    // Wait until type checking succeeds (or 100 attempts)
    match checkFileResults with
    | FSharpCheckFileAnswer.Succeeded(res) -> parseFileResults, res
    | res -> failwithf "Parsing did not finish... (%A)" res

let file = "/home/user/Test.fsx"

Getting resolved signature information about the file

After type checking a file, you can access the inferred signature of a project up to and including the checking of the given file through the PartialAssemblySignature property of the TypeCheckResults.

The full signature information is available for modules, types, attributes, members, values, functions, union cases, record types, units of measure and other F# language constructs.

The typed expression trees are also available, see typed tree tutorial.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
let input2 = 
      """
[<System.CLSCompliant(true)>]
let foo(x, y) = 
    let msg = String.Concat("Hello"," ","world")
    if true then 
        printfn "x = %d, y = %d" x y 
        printfn "%s" msg

type C() = 
    member x.P = 1
      """
let parseFileResults, checkFileResults = 
    parseAndTypeCheckSingleFile(file, input2)

Now get the partial assembly signature for the code:

1: 
2: 
3: 
4: 
let partialAssemblySignature = checkFileResults.PartialAssemblySignature
    
partialAssemblySignature.Entities.Count = 1  // one entity
    

Now get the entity that corresponds to the module containing the code:

1: 
2: 
3: 
let moduleEntity = partialAssemblySignature.Entities.[0]

moduleEntity.DisplayName = "Test"

Now get the entity that corresponds to the type definition in the code:

1: 
let classEntity = moduleEntity.NestedEntities.[0]

Now get the value that corresponds to the function defined in the code:

1: 
let fnVal = moduleEntity.MembersFunctionsAndValues.[0]

Now look around at the properties describing the function value. All fo the following evaluate to true:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
fnVal.Attributes.Count = 1
fnVal.CurriedParameterGroups.Count // 1
fnVal.CurriedParameterGroups.[0].Count // 2
fnVal.CurriedParameterGroups.[0].[0].Name // "x"
fnVal.CurriedParameterGroups.[0].[1].Name // "y"
fnVal.DeclarationLocation.StartLine // 3
fnVal.DisplayName // "foo"
fnVal.EnclosingEntity.DisplayName // "Test"
fnVal.EnclosingEntity.DeclarationLocation.StartLine // 1
fnVal.GenericParameters.Count // 0
fnVal.InlineAnnotation // FSharpInlineAnnotation.OptionalInline
fnVal.IsActivePattern // false
fnVal.IsCompilerGenerated // false
fnVal.IsDispatchSlot // false
fnVal.IsExtensionMember // false
fnVal.IsPropertyGetterMethod // false
fnVal.IsImplicitConstructor // false
fnVal.IsInstanceMember // false
fnVal.IsMember // false
fnVal.IsModuleValueOrMember // true
fnVal.IsMutable // false
fnVal.IsPropertySetterMethod // false
fnVal.IsTypeFunction // false

Now look at the type of the function if used as a first class value. (Aside: the CurriedParameterGroups property contains more information like the names of the arguments.)

1: 
2: 
3: 
4: 
5: 
6: 
7: 
fnVal.FullType // int * int -> unit
fnVal.FullType.IsFunctionType // int * int -> unit
fnVal.FullType.GenericArguments.[0] // int * int 
fnVal.FullType.GenericArguments.[0].IsTupleType // int * int 
let argTy1 = fnVal.FullType.GenericArguments.[0].GenericArguments.[0]

argTy1.TypeDefinition.DisplayName // int

OK, so we got an object representation of the type int * int -> unit, and we have seen the first 'int'. We can find out more about the type 'int' as follows, determining that it is a named type, which is an F# type abbreviation, type int = int32:

1: 
2: 
argTy1.HasTypeDefinition
argTy1.TypeDefinition.IsFSharpAbbreviation // "int"

We can now look at the right-hand-side of the type abbreviation, which is the type int32:

1: 
2: 
3: 
let argTy1b = argTy1.TypeDefinition.AbbreviatedType
argTy1b.TypeDefinition.Namespace // Some "Microsoft.FSharp.Core" 
argTy1b.TypeDefinition.CompiledName // "int32" 

Again we can now look through the type abbreviation type int32 = System.Int32 to get the full information about the type:

1: 
2: 
3: 
let argTy1c = argTy1b.TypeDefinition.AbbreviatedType
argTy1c.TypeDefinition.Namespace // Some "SystemCore" 
argTy1c.TypeDefinition.CompiledName // "Int32" 

The type checking results for a file also contain information extracted from the project (or script) options used in the compilation, called the ProjectContext:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let projectContext = checkFileResults.ProjectContext
    
for ass in projectContext.GetReferencedAssemblies() do
    match ass.FileName with 
    | None -> printfn "compilation referenced an assembly without a file" 
    | Some s -> printfn "compilation references assembly '%s'" s
    

Notes:

  • If incomplete code is present, some or all of the attirbutes may not be quite as expected.
  • If some assembly references are missing (which is actually very, very common), then 'IsUnresolved' may be true on values, members and/or entites related to external assemblies. You should be sure to make your code robust against IsUnresolved exceptions.

Getting symbolic information about whole projects

To check whole projects, create a checker, then call parseAndCheckScript. In this case, we just check the project for a single script. By specifying a different "projOptions" you can create a specification of a larger project.

1: 
2: 
3: 
4: 
5: 
6: 
let parseAndCheckScript (file, input) = 
    let projOptions = 
        checker.GetProjectOptionsFromScript(file, input)
        |> Async.RunSynchronously

    checker.ParseAndCheckProject(projOptions) |> Async.RunSynchronously

Now do it for a particular input:

1: 
2: 
3: 
4: 
let tmpFile = Path.ChangeExtension(System.IO.Path.GetTempFileName() , "fs")
File.WriteAllText(tmpFile, input2)

let projectResults = parseAndCheckScript(tmpFile, input2)

Now look at the results:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let assemblySig = projectResults.AssemblySignature
    
assemblySig.Entities.Count = 1  // one entity
assemblySig.Entities.[0].Namespace  // one entity
assemblySig.Entities.[0].DisplayName // "Tmp28D0"
assemblySig.Entities.[0].MembersFunctionsAndValues.Count // 1 
assemblySig.Entities.[0].MembersFunctionsAndValues.[0].DisplayName // "foo" 
    
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Compiler
namespace Microsoft.FSharp.Compiler.SourceCodeServices
val checker : FSharpChecker

Full name: Symbols.checker
type FSharpChecker
member CheckFileInProject : parsed:FSharpParseFileResults * filename:string * fileversion:int * source:string * options:FSharpProjectOptions * ?isResultObsolete:IsResultObsolete * ?textSnapshotInfo:obj -> Async<FSharpCheckFileAnswer>
member CheckFileInProjectIfReady : parsed:FSharpParseFileResults * filename:string * fileversion:int * source:string * options:FSharpProjectOptions * ?isResultObsolete:IsResultObsolete * ?textSnapshotInfo:obj -> Async<FSharpCheckFileAnswer option>
member CheckProjectInBackground : options:FSharpProjectOptions -> unit
member ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients : unit -> unit
member Compile : argv:string [] -> FSharpErrorInfo [] * int
member Compile : ast:ParsedInput list * assemblyName:string * outFile:string * dependencies:string list * ?pdbFile:string * ?executable:bool * ?noframework:bool -> FSharpErrorInfo [] * int
member CompileToDynamicAssembly : otherFlags:string [] * execute:(TextWriter * TextWriter) option -> FSharpErrorInfo [] * int * Assembly option
member CompileToDynamicAssembly : ast:ParsedInput list * assemblyName:string * dependencies:string list * execute:(TextWriter * TextWriter) option * ?debug:bool * ?noframework:bool -> FSharpErrorInfo [] * int * Assembly option
member GetBackgroundCheckResultsForFileInProject : filename:string * options:FSharpProjectOptions -> Async<FSharpParseFileResults * FSharpCheckFileResults>
member GetBackgroundParseResultsForFileInProject : filename:string * options:FSharpProjectOptions -> Async<FSharpParseFileResults>
member GetProjectOptionsFromCommandLineArgs : projectFileName:string * argv:string [] * ?loadedTimeStamp:DateTime -> FSharpProjectOptions
member GetProjectOptionsFromScript : filename:string * source:string * ?loadedTimeStamp:DateTime * ?otherFlags:string [] * ?useFsiAuxLib:bool -> Async<FSharpProjectOptions>
member InvalidateAll : unit -> unit
member InvalidateConfiguration : options:FSharpProjectOptions -> unit
member KeepProjectAlive : options:FSharpProjectOptions -> Async<IDisposable>
member MatchBracesAlternate : filename:string * source:string * options:FSharpProjectOptions -> Async<(range * range) []>
member NotifyProjectCleaned : options:FSharpProjectOptions -> unit
member ParseAndCheckFileInProject : filename:string * fileversion:int * source:string * options:FSharpProjectOptions * ?isResultObsolete:IsResultObsolete * ?textSnapshotInfo:obj -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
member ParseAndCheckProject : options:FSharpProjectOptions -> Async<FSharpCheckProjectResults>
member ParseFileInProject : filename:string * source:string * options:FSharpProjectOptions -> Async<FSharpParseFileResults>
member TryGetRecentCheckResultsForFile : filename:string * options:FSharpProjectOptions * ?source:string -> (FSharpParseFileResults * FSharpCheckFileResults * int) option
member BeforeBackgroundFileCheck : IEvent<string>
member CurrentQueueLength : int
member FileChecked : IEvent<string>
member FileParsed : IEvent<string>
member private FrameworkImportsCache : FrameworkImportsCache
member ImplicitlyStartBackgroundWork : bool
member MaxMemory : int
member MaxMemoryReached : IEvent<unit>
member PauseBeforeBackgroundWork : int
member ProjectChecked : IEvent<string>
member private ReactorOps : IReactorOperations
member private ReferenceResolver : Resolver
member ImplicitlyStartBackgroundWork : bool with set
member MaxMemory : int with set
member PauseBeforeBackgroundWork : int with set
static member Create : ?projectCacheSize:int * ?keepAssemblyContents:bool * ?keepAllBackgroundResolutions:bool * ?msbuildEnabled:bool -> FSharpChecker
static member GlobalForegroundParseCountStatistic : int
static member GlobalForegroundTypeCheckCountStatistic : int

Full name: Microsoft.FSharp.Compiler.SourceCodeServices.FSharpChecker
static member FSharpChecker.Create : ?projectCacheSize:int * ?keepAssemblyContents:bool * ?keepAllBackgroundResolutions:bool * ?msbuildEnabled:bool -> FSharpChecker
val parseAndTypeCheckSingleFile : file:string * input:string -> FSharpParseFileResults * FSharpCheckFileResults

Full name: Symbols.parseAndTypeCheckSingleFile
val file : string
val input : string
val projOptions : FSharpProjectOptions
member FSharpChecker.GetProjectOptionsFromScript : filename:string * source:string * ?loadedTimeStamp:DateTime * ?otherFlags:string [] * ?useFsiAuxLib:bool -> Async<FSharpProjectOptions>
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
val parseFileResults : FSharpParseFileResults
val checkFileResults : FSharpCheckFileAnswer
member FSharpChecker.ParseAndCheckFileInProject : filename:string * fileversion:int * source:string * options:FSharpProjectOptions * ?isResultObsolete:IsResultObsolete * ?textSnapshotInfo:obj -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
type FSharpCheckFileAnswer =
  | Aborted
  | Succeeded of FSharpCheckFileResults

Full name: Microsoft.FSharp.Compiler.SourceCodeServices.FSharpCheckFileAnswer
union case FSharpCheckFileAnswer.Succeeded: FSharpCheckFileResults -> FSharpCheckFileAnswer
val res : FSharpCheckFileResults
val res : FSharpCheckFileAnswer
val failwithf : format:Printf.StringFormat<'T,'Result> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.failwithf
val file : string

Full name: Symbols.file
val input2 : string

Full name: Symbols.input2
val parseFileResults : FSharpParseFileResults

Full name: Symbols.parseFileResults
val checkFileResults : FSharpCheckFileResults

Full name: Symbols.checkFileResults
val partialAssemblySignature : FSharpAssemblySignature

Full name: Symbols.partialAssemblySignature
property FSharpCheckFileResults.PartialAssemblySignature: FSharpAssemblySignature
property FSharpAssemblySignature.Entities: Collections.Generic.IList<FSharpEntity>
property Collections.Generic.ICollection.Count: int
val moduleEntity : FSharpEntity

Full name: Symbols.moduleEntity
property FSharpEntity.DisplayName: string
val classEntity : FSharpEntity

Full name: Symbols.classEntity
property FSharpEntity.NestedEntities: Collections.Generic.IList<FSharpEntity>
val fnVal : FSharpMemberOrFunctionOrValue

Full name: Symbols.fnVal
property FSharpEntity.MembersFunctionsAndValues: Collections.Generic.IList<FSharpMemberOrFunctionOrValue>
property FSharpMemberOrFunctionOrValue.Attributes: Collections.Generic.IList<FSharpAttribute>
property FSharpMemberOrFunctionOrValue.CurriedParameterGroups: Collections.Generic.IList<Collections.Generic.IList<FSharpParameter>>
property FSharpMemberOrFunctionOrValue.DeclarationLocation: Compiler.Range.range
property Compiler.Range.range.StartLine: int
property FSharpMemberOrFunctionOrValue.DisplayName: string
property FSharpMemberOrFunctionOrValue.EnclosingEntity: FSharpEntity
property FSharpEntity.DeclarationLocation: Compiler.Range.range
property FSharpMemberOrFunctionOrValue.GenericParameters: Collections.Generic.IList<FSharpGenericParameter>
property FSharpMemberOrFunctionOrValue.InlineAnnotation: FSharpInlineAnnotation
property FSharpMemberOrFunctionOrValue.IsActivePattern: bool
property FSharpMemberOrFunctionOrValue.IsCompilerGenerated: bool
property FSharpMemberOrFunctionOrValue.IsDispatchSlot: bool
property FSharpMemberOrFunctionOrValue.IsExtensionMember: bool
property FSharpMemberOrFunctionOrValue.IsPropertyGetterMethod: bool
property FSharpMemberOrFunctionOrValue.IsImplicitConstructor: bool
property FSharpMemberOrFunctionOrValue.IsInstanceMember: bool
property FSharpMemberOrFunctionOrValue.IsMember: bool
property FSharpMemberOrFunctionOrValue.IsModuleValueOrMember: bool
property FSharpMemberOrFunctionOrValue.IsMutable: bool
property FSharpMemberOrFunctionOrValue.IsPropertySetterMethod: bool
property FSharpMemberOrFunctionOrValue.IsTypeFunction: bool
property FSharpMemberOrFunctionOrValue.FullType: FSharpType
property FSharpType.IsFunctionType: bool
property FSharpType.GenericArguments: Collections.Generic.IList<FSharpType>
val argTy1 : FSharpType

Full name: Symbols.argTy1
property FSharpType.TypeDefinition: FSharpEntity
property FSharpType.HasTypeDefinition: bool
property FSharpEntity.IsFSharpAbbreviation: bool
val argTy1b : FSharpType

Full name: Symbols.argTy1b
property FSharpEntity.AbbreviatedType: FSharpType
property FSharpEntity.Namespace: string option
property FSharpEntity.CompiledName: string
val argTy1c : FSharpType

Full name: Symbols.argTy1c
val projectContext : FSharpProjectContext

Full name: Symbols.projectContext
property FSharpCheckFileResults.ProjectContext: FSharpProjectContext
val ass : FSharpAssembly
member FSharpProjectContext.GetReferencedAssemblies : unit -> FSharpAssembly list
property FSharpAssembly.FileName: string option
union case Option.None: Option<'T>
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
union case Option.Some: Value: 'T -> Option<'T>
val s : string
val parseAndCheckScript : file:string * input:string -> FSharpCheckProjectResults

Full name: Symbols.parseAndCheckScript
member FSharpChecker.ParseAndCheckProject : options:FSharpProjectOptions -> Async<FSharpCheckProjectResults>
val tmpFile : string

Full name: Symbols.tmpFile
type Path =
  static val DirectorySeparatorChar : char
  static val AltDirectorySeparatorChar : char
  static val VolumeSeparatorChar : char
  static val InvalidPathChars : char[]
  static val PathSeparator : char
  static member ChangeExtension : path:string * extension:string -> string
  static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
  static member GetDirectoryName : path:string -> string
  static member GetExtension : path:string -> string
  static member GetFileName : path:string -> string
  ...

Full name: System.IO.Path
Path.ChangeExtension(path: string, extension: string) : string
Path.GetTempFileName() : string
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  static member Encrypt : path:string -> unit
  static member Exists : path:string -> bool
  ...

Full name: System.IO.File
File.WriteAllText(path: string, contents: string) : unit
File.WriteAllText(path: string, contents: string, encoding: Text.Encoding) : unit
val projectResults : FSharpCheckProjectResults

Full name: Symbols.projectResults
val assemblySig : FSharpAssemblySignature

Full name: Symbols.assemblySig
property FSharpCheckProjectResults.AssemblySignature: FSharpAssemblySignature
Fork me on GitHub