Header menu logo F# Compiler Guide

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:

// Reference F# compiler API
#r "FSharp.Compiler.Service.dll"

open System
open System.IO
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Symbols
open FSharp.Compiler.Text

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

We now perform type checking on the specified input:

let parseAndTypeCheckSingleFile (file, input) = 
    // Get context representing a stand-alone (script) file
    let projOptions, errors = 
        checker.GetProjectOptionsFromScript(file, input, assumeDotNetFramework=false)
        |> 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.

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, SourceText.ofString input2)

Now get the partial assembly signature for the code:

let partialAssemblySignature = checkFileResults.PartialAssemblySignature
    
partialAssemblySignature.Entities.Count = 1  // one entity
    

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

let moduleEntity = partialAssemblySignature.Entities.[0]

moduleEntity.DisplayName = "Test"

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

let classEntity = moduleEntity.NestedEntities.[0]

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

let fnVal = moduleEntity.MembersFunctionsAndValues.[0]

Now look around at the properties describing the function value:

fnVal.Attributes.Count // 1
fnVal.CurriedParameterGroups.Count // 1
fnVal.CurriedParameterGroups.[0].Count // 2
fnVal.CurriedParameterGroups.[0].[0].Name // Some "x"
fnVal.CurriedParameterGroups.[0].[1].Name // Some "y"
fnVal.DeclarationLocation.StartLine // 3
fnVal.DisplayName // "foo"
fnVal.DeclaringEntity.Value.DisplayName // "Test"
fnVal.DeclaringEntity.Value.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.)

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:

argTy1.HasTypeDefinition
argTy1.TypeDefinition.IsFSharpAbbreviation // "int"

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

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:

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:

let projectContext = checkFileResults.ProjectContext
    
for assembly in projectContext.GetReferencedAssemblies() do
    match assembly.FileName with 
    | None -> printfn "compilation referenced an assembly without a file" 
    | Some s -> printfn "compilation references assembly '%s'" s
    

Notes:

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.

let parseAndCheckScript (file, input) = 
    let projOptions, errors = 
        checker.GetProjectOptionsFromScript(file, input, assumeDotNetFramework=false)
        |> Async.RunSynchronously

    checker.ParseAndCheckProject(projOptions) |> Async.RunSynchronously

Now do it for a particular input:

let tmpFile = Path.ChangeExtension(System.IO.Path.GetTempFileName() , "fs")
File.WriteAllText(tmpFile, input2)

let projectResults = parseAndCheckScript(tmpFile, SourceText.ofString input2)

Now look at the results:

let assemblySig = projectResults.AssemblySignature
    
printfn $"#entities = {assemblySig.Entities.Count}" // 1
printfn $"namespace = {assemblySig.Entities.[0].Namespace}"  // one entity
printfn $"entity name = {assemblySig.Entities.[0].DisplayName}" // "Tmp28D0"
printfn $"#members = {assemblySig.Entities.[0].MembersFunctionsAndValues.Count}" // 1 
printfn $"member name = {assemblySig.Entities.[0].MembersFunctionsAndValues.[0].DisplayName}" // "foo" 
    
namespace System
namespace System.IO
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Compiler
namespace FSharp.Compiler.CodeAnalysis
namespace FSharp.Compiler.Symbols
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp

--------------------
type FSharpAttribute = member Format: context: FSharpDisplayContext -> string member IsAttribute: unit -> bool member AttributeType: FSharpEntity member ConstructorArguments: IList<FSharpType * obj> member IsUnresolved: bool member NamedArguments: IList<FSharpType * string * bool * obj> member Range: range
<summary> Represents a custom attribute attached to F# source code or a compiler .NET component </summary>
namespace FSharp.Compiler.Text
val checker: FSharpChecker
type FSharpChecker = member CheckFileInProject: parseResults: FSharpParseFileResults * fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpCheckFileAnswer> member ClearCache: options: FSharpProjectOptions seq * ?userOpName: string -> unit + 1 overload member ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients: unit -> unit member Compile: argv: string array * ?userOpName: string -> Async<FSharpDiagnostic array * int> member FindBackgroundReferencesInFile: fileName: string * options: FSharpProjectOptions * symbol: FSharpSymbol * ?canInvalidateProject: bool * [<Experimental ("This FCS API is experimental and subject to change.")>] ?fastCheck: bool * ?userOpName: string -> Async<range seq> + 1 overload member GetBackgroundCheckResultsForFileInProject: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileResults> member GetBackgroundParseResultsForFileInProject: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults> member GetBackgroundSemanticClassificationForFile: fileName: string * options: FSharpProjectOptions * ?userOpName: string -> Async<SemanticClassificationView option> + 1 overload member GetParsingOptionsFromCommandLineArgs: sourceFiles: string list * argv: string list * ?isInteractive: bool * ?isEditing: bool -> FSharpParsingOptions * FSharpDiagnostic list + 1 overload member GetParsingOptionsFromProjectOptions: options: FSharpProjectOptions -> FSharpParsingOptions * FSharpDiagnostic list ...
<summary> Used to parse and check F# source code. </summary>
static member FSharpChecker.Create: ?projectCacheSize: int * ?keepAssemblyContents: bool * ?keepAllBackgroundResolutions: bool * ?legacyReferenceResolver: LegacyReferenceResolver * ?tryGetMetadataSnapshot: FSharp.Compiler.AbstractIL.ILBinaryReader.ILReaderTryGetMetadataSnapshot * ?suggestNamesForErrors: bool * ?keepAllBackgroundSymbolUses: bool * ?enableBackgroundItemKeyStoreAndSemanticClassification: bool * ?enablePartialTypeChecking: bool * ?parallelReferenceResolution: bool * ?captureIdentifiersWhenParsing: bool * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?documentSource: DocumentSource * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?useTransparentCompiler: bool -> FSharpChecker
val parseAndTypeCheckSingleFile: file: string * input: ISourceText -> FSharpParseFileResults * FSharpCheckFileResults
val file: string
val input: ISourceText
val projOptions: FSharpProjectOptions
val errors: FSharp.Compiler.Diagnostics.FSharpDiagnostic list
member FSharpChecker.GetProjectOptionsFromScript: fileName: string * source: ISourceText * ?previewEnabled: bool * ?loadedTimeStamp: DateTime * ?otherFlags: string array * ?useFsiAuxLib: bool * ?useSdkRefs: bool * ?assumeDotNetFramework: bool * ?sdkDirOverride: string * ?optionsStamp: int64 * ?userOpName: string -> Async<FSharpProjectOptions * FSharp.Compiler.Diagnostics.FSharpDiagnostic list>
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<'T> -> Async<'T> + 1 overload 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 Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
static member Async.RunSynchronously: computation: Async<'T> * ?timeout: int * ?cancellationToken: Threading.CancellationToken -> 'T
val parseFileResults: FSharpParseFileResults
val checkFileResults: FSharpCheckFileAnswer
member FSharpChecker.ParseAndCheckFileInProject: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
member FSharpChecker.ParseAndCheckFileInProject: fileName: string * fileVersion: int * sourceText: ISourceText * options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
type FSharpCheckFileAnswer = | Aborted | Succeeded of FSharpCheckFileResults member Equals: FSharpCheckFileAnswer * IEqualityComparer -> bool
<summary> The result of calling TypeCheckResult including the possibility of abort and background compiler not caught up. </summary>
union case FSharpCheckFileAnswer.Succeeded: FSharpCheckFileResults -> FSharpCheckFileAnswer
<summary> Success </summary>
val res: FSharpCheckFileResults
val res: FSharpCheckFileAnswer
val failwithf: format: Printf.StringFormat<'T,'Result> -> 'T
val input2: string
val checkFileResults: FSharpCheckFileResults
module SourceText from FSharp.Compiler.Text
<summary> Functions related to ISourceText objects </summary>
val ofString: string -> ISourceText
<summary> Creates an ISourceText object from the given string </summary>
val partialAssemblySignature: FSharpAssemblySignature
property FSharpCheckFileResults.PartialAssemblySignature: FSharpAssemblySignature with get
<summary> Get a view of the contents of the assembly up to and including the file just checked </summary>
property FSharpAssemblySignature.Entities: Collections.Generic.IList<FSharpEntity> with get
<summary> The (non-nested) module and type definitions in this signature </summary>
property Collections.Generic.ICollection.Count: int with get
val moduleEntity: FSharpEntity
property FSharpEntity.DisplayName: string with get
<summary> Get the name of the type or module as displayed in F# code </summary>
val classEntity: FSharpEntity
property FSharpEntity.NestedEntities: Collections.Generic.IList<FSharpEntity> with get
<summary> Get the modules and types defined in a module, or the nested types of a type </summary>
val fnVal: FSharpMemberOrFunctionOrValue
property FSharpEntity.MembersFunctionsAndValues: Collections.Generic.IList<FSharpMemberOrFunctionOrValue> with get
<summary> Get the properties, events and methods of a type definitions, or the functions and values of a module </summary>
property FSharpMemberOrFunctionOrValue.Attributes: Collections.Generic.IList<FSharpAttribute> with get
<summary> Custom attributes attached to the value. These contain references to other values (i.e. constructors in types). Mutable to fixup these value references after copying a collection of values. </summary>
property FSharpMemberOrFunctionOrValue.CurriedParameterGroups: Collections.Generic.IList<Collections.Generic.IList<FSharpParameter>> with get
<summary>List of list of parameters, where each nested item represents a defined parameter</summary>
<remarks> Typically, there is only one nested list. However, code such as 'f (a, b) (c, d)' contains two groups, each with two parameters. In that example, there is a list made up of two lists, each with a parameter. </remarks>
property FSharpMemberOrFunctionOrValue.DeclarationLocation: range with get
<summary> Get the declaration location of the member, function or value </summary>
property Range.StartLine: int with get
<summary> The start line of the range </summary>
property FSharpMemberOrFunctionOrValue.DisplayName: string with get
<summary> Get the name as presented in F# error messages and documentation </summary>
property FSharpMemberOrFunctionOrValue.DeclaringEntity: FSharpEntity option with get
<summary> Get the enclosing entity for the definition </summary>
property Option.Value: FSharpEntity with get
property FSharpEntity.DeclarationLocation: range with get
<summary> Get the declaration location for the type constructor </summary>
property FSharpMemberOrFunctionOrValue.GenericParameters: Collections.Generic.IList<FSharpGenericParameter> with get
<summary> Get the typars of the member, function or value </summary>
property FSharpMemberOrFunctionOrValue.InlineAnnotation: FSharpInlineAnnotation with get
<summary> Get a result indicating if this is a must-inline value </summary>
property FSharpMemberOrFunctionOrValue.IsActivePattern: bool with get
<summary> Indicates if this value or member is an F# active pattern </summary>
property FSharpMemberOrFunctionOrValue.IsCompilerGenerated: bool with get
<summary> Indicates if this is a compiler generated value </summary>
property FSharpMemberOrFunctionOrValue.IsDispatchSlot: bool with get
<summary> Indicates if this is an abstract member? </summary>
property FSharpMemberOrFunctionOrValue.IsExtensionMember: bool with get
<summary> Indicates if this is an extension member? </summary>
property FSharpMemberOrFunctionOrValue.IsPropertyGetterMethod: bool with get
<summary> Indicates if this is a getter method for a property, or a use of a property in getter mode </summary>
property FSharpMemberOrFunctionOrValue.IsImplicitConstructor: bool with get
<summary> Indicates if this is an implicit constructor? </summary>
property FSharpMemberOrFunctionOrValue.IsInstanceMember: bool with get
<summary> Indicates if this is an instance member, when seen from F#? </summary>
property FSharpMemberOrFunctionOrValue.IsMember: bool with get
<summary> Indicates if this is a member, including extension members? </summary>
property FSharpMemberOrFunctionOrValue.IsModuleValueOrMember: bool with get
<summary> Indicates if this is a module or member value </summary>
property FSharpMemberOrFunctionOrValue.IsMutable: bool with get
<summary> Indicates if this is a mutable value </summary>
property FSharpMemberOrFunctionOrValue.IsPropertySetterMethod: bool with get
<summary> Indicates if this is a setter method for a property, or a use of a property in setter mode </summary>
property FSharpMemberOrFunctionOrValue.IsTypeFunction: bool with get
<summary> Indicates if this is an F# type function </summary>
property FSharpMemberOrFunctionOrValue.FullType: FSharpType with get
<summary> Get the full type of the member, function or value when used as a first class value </summary>
property FSharpType.IsFunctionType: bool with get
<summary> Indicates if the type is a function type. The GenericArguments property returns the domain and range of the function type. </summary>
property FSharpType.GenericArguments: Collections.Generic.IList<FSharpType> with get
<summary> Get the generic arguments for a tuple type, a function type or a type constructed using a named entity </summary>
val argTy1: FSharpType
property FSharpType.TypeDefinition: FSharpEntity with get
<summary> Get the type definition for a type </summary>
property FSharpType.HasTypeDefinition: bool with get
<summary> Indicates if the type is constructed using a named entity, including array and byref types </summary>
property FSharpEntity.IsFSharpAbbreviation: bool with get
<summary> Indicates if the entity is a measure, type or exception abbreviation </summary>
val argTy1b: FSharpType
property FSharpEntity.AbbreviatedType: FSharpType with get
<summary> Get the type abbreviated by an F# type abbreviation </summary>
property FSharpEntity.Namespace: string option with get
<summary> Get the namespace containing the type or module, if any. Use 'None' for item not in a namespace. </summary>
property FSharpEntity.CompiledName: string with get
<summary> Get the compiled name of the type or module, possibly with `n mangling. This is identical to LogicalName unless the CompiledName attribute is used. </summary>
val argTy1c: FSharpType
val projectContext: FSharpProjectContext
property FSharpCheckFileResults.ProjectContext: FSharpProjectContext with get
<summary> Get the resolution of the ProjectOptions </summary>
val assembly: FSharpAssembly
member FSharpProjectContext.GetReferencedAssemblies: unit -> FSharpAssembly list
property FSharpAssembly.FileName: string option with get
<summary> The file name for the assembly, if any </summary>
union case Option.None: Option<'T>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
union case Option.Some: Value: 'T -> Option<'T>
val s: string
val parseAndCheckScript: file: string * input: ISourceText -> FSharpCheckProjectResults
member FSharpChecker.ParseAndCheckProject: projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async<FSharpCheckProjectResults>
member FSharpChecker.ParseAndCheckProject: options: FSharpProjectOptions * ?userOpName: string -> Async<FSharpCheckProjectResults>
val tmpFile: string
type Path = static member ChangeExtension: path: string * extension: string -> string static member Combine: path1: string * path2: string -> string + 4 overloads static member EndsInDirectorySeparator: path: ReadOnlySpan<char> -> bool + 1 overload static member Exists: path: string -> bool static member GetDirectoryName: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetExtension: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFileName: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFileNameWithoutExtension: path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFullPath: path: string -> string + 1 overload static member GetInvalidFileNameChars: unit -> char array ...
Path.ChangeExtension(path: string, extension: string) : string
Path.GetTempFileName() : string
type File = static member AppendAllBytes: path: string * bytes: byte array -> unit + 1 overload static member AppendAllBytesAsync: path: string * bytes: byte array * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllLines: path: string * contents: IEnumerable<string> -> unit + 1 overload static member AppendAllLinesAsync: path: string * contents: IEnumerable<string> * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllText: path: string * contents: string -> unit + 3 overloads static member AppendAllTextAsync: path: string * contents: string * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 3 overloads static member AppendText: path: string -> StreamWriter static member Copy: sourceFileName: string * destFileName: string -> unit + 1 overload static member Create: path: string -> FileStream + 2 overloads static member CreateSymbolicLink: path: string * pathToTarget: string -> FileSystemInfo ...
File.WriteAllText(path: string, contents: ReadOnlySpan<char>) : unit
File.WriteAllText(path: string, contents: string) : unit
File.WriteAllText(path: string, contents: ReadOnlySpan<char>, encoding: Text.Encoding) : unit
File.WriteAllText(path: string, contents: string, encoding: Text.Encoding) : unit
val projectResults: FSharpCheckProjectResults
val assemblySig: FSharpAssemblySignature
property FSharpCheckProjectResults.AssemblySignature: FSharpAssemblySignature with get
<summary> Get a view of the overall signature of the assembly. Only valid to use if HasCriticalErrors is false. </summary>

Type something to start searching.