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"
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:
- If incomplete code is present, some or all of the attributes 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 entities related to external assemblies. You should be sure to make your
code robust against IsUnresolved exceptions.
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:seq<FSharpProjectOptions> * ?userOpName:string -> unit
member ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients : unit -> unit
member Compile : argv:string [] * ?userOpName:string -> Async<FSharpDiagnostic [] * int> + 1 overload
member CompileToDynamicAssembly : otherFlags:string [] * execute:(TextWriter * TextWriter) option * ?userOpName:string -> Async<FSharpDiagnostic [] * int * Assembly option> + 1 overload
member FindBackgroundReferencesInFile : fileName:string * options:FSharpProjectOptions * symbol:FSharpSymbol * ?canInvalidateProject:bool * ?userOpName:string -> Async<seq<range>>
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>
member GetParsingOptionsFromCommandLineArgs : sourceFiles:string list * argv:string list * ?isInteractive:bool * ?isEditing:bool -> FSharpParsingOptions * FSharpDiagnostic list + 1 overload
...
<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 -> 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 [] * ?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:seq<Async<'T option>> -> 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>
...
<summary>Holds static members for creating and manipulating asynchronous computations.</summary>
<remarks>
See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>.
</remarks>
<category index="1">Async Programming</category>
--------------------
type Async<'T> =
<summary>
An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception.
</summary>
<remarks>
This type has no members. Asynchronous computations are normally specified either by using an async expression
or the static methods in the <see cref="T:Microsoft.FSharp.Control.Async" /> type.
See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>.
</remarks>
<namespacedoc><summary>
Library functionality for asynchronous programming, events and agents. See also
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">Asynchronous Programming</a>,
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/members/events">Events</a> and
<a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/lazy-expressions">Lazy Expressions</a> in the
F# Language Guide.
</summary></namespacedoc>
<category index="1">Async Programming</category>
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 * sourceText:ISourceText * options:FSharpProjectOptions * ?userOpName:string -> Async<FSharpParseFileResults * FSharpCheckFileAnswer>
type FSharpCheckFileAnswer =
| Aborted
| Succeeded of FSharpCheckFileResults
<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
<summary>Print to a string buffer and raise an exception with the given
result. Helper printers must return strings.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
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
<summary>Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.</summary>
<returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1" />.</returns>
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
<summary>Get the value of a 'Some' option. A NullReferenceException is raised if the option is 'None'.</summary>
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>
<summary>The representation of "No value"</summary>
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
<summary>Print to <c>stdout</c> using the given format, and add a newline.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
union case Option.Some: Value: 'T -> Option<'T>
<summary>The representation of "Value of type 'T"</summary>
<param name="Value">The input value.</param>
<returns>An option representing the value.</returns>
val s : string
val parseAndCheckScript : file:string * input:ISourceText -> 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 + 3 overloads
static member EndsInDirectorySeparator : path: ReadOnlySpan<char> -> bool + 1 overload
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 []
static member GetInvalidPathChars : unit -> char []
...
<summary>Performs operations on <see cref="T:System.String" /> instances that contain file or directory path information. These operations are performed in a cross-platform manner.</summary>
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 AppendAllLinesAsync : path: string * contents: IEnumerable<string> * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload
static member AppendAllText : path: string * contents: string -> unit + 1 overload
static member AppendAllTextAsync : path: string * contents: string * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload
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 CreateText : path: string -> StreamWriter
static member Decrypt : path: string -> unit
static member Delete : path: string -> unit
...
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
File.WriteAllText(path: string, contents: string) : 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>