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: FSharpProjectOptions seq * ?userOpName: string -> unit
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>
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
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.")>] ?useSyntaxTreeCache: 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 * 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
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
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: 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 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
...
<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 CreateSymbolicLink: path: string * pathToTarget: string -> FileSystemInfo
static member CreateText: path: string -> StreamWriter
static member Decrypt: 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>