F# Compiler Services


Compiler Services: Processing untyped syntax tree

This tutorial demonstrates how to get the untyped abstract syntax tree (AST) for F# code and how to walk over the tree. This can be used for creating tools such as code formatter, basic refactoring or code navigation tools. The untyped syntax tree contains information about the code structure, but does not contain types and there are some ambiguities that are resolved only later by the type checker. You can also combine the untyped AST information with the API available from editor services.

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

Getting the untyped AST

To access the untyped AST, you need to create an instance of FSharpChecker. This type represents a context for type checking and parsing and corresponds either to a stand-alone F# script file (e.g. opened in Visual Studio) or to a loaded project file with multiple files. Once you have an instance of FSharpChecker, you can use it to perform "untyped parse" which is the first step of type-checking. The second phase is "typed parse" and is used by editor services.

To use the interactive checker, reference FSharp.Compiler.Service.dll and open the SourceCodeServices namespace:

1: 
2: 
3: 
#r "FSharp.Compiler.Service.dll"
open System
open Microsoft.FSharp.Compiler.SourceCodeServices

Performing untyped parse

The untyped parse operation is very fast (compared to type checking, which can take notable amount of time) and so we can perform it synchronously. First, we need to create FSharpChecker - the constructor takes an argument that can be used to notify the checker about file changes (which we ignore).

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

To get the AST, we define a function that takes file name and the source code (the file is only used for location information and does not have to exist). We first need to get "interactive checker options" which represents the context. For simple tasks, you can use GetProjectOptionsFromScriptRoot which infers the context for a script file. Then we use the ParseFileInProject method and return the ParseTree property:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
/// Get untyped tree for a specified input
let getUntypedTree (file, input) = 
  // Get compiler options for the 'project' implied by a single script file
  let projOptions = 
      checker.GetProjectOptionsFromScript(file, input)
      |> Async.RunSynchronously

  // Run the first phase (untyped parsing) of the compiler
  let parseFileResults = 
      checker.ParseFileInProject(file, input, projOptions) 
      |> Async.RunSynchronously

  match parseFileResults.ParseTree with
  | Some tree -> tree
  | None -> failwith "Something went wrong during parsing!"

Walking over the AST

The abstract syntax tree is defined as a number of discriminated unions that represent different syntactical elements (such as expressions, patterns, declarations etc.). The best way to understand the AST is to look at the definitions in ast.fs in the source code.

The relevant parts are in the following namespace:

1: 
open Microsoft.FSharp.Compiler.Ast

When processing the AST, you will typically write a number of mutually recursive functions that pattern match on the different syntactical elements. There is a number of elements that need to be supported - the top-level element is module or namespace declaration, containing declarations inside a module (let bindings, types etc.). A let declaration inside a module then contains expression, which can contain patterns.

Walking over patterns and expressions

We start by looking at functions that walk over expressions and patterns - as we walk, we print information about the visited elements. For patterns, the input is of type SynPat and has a number of cases including Wild (for _ pattern), Named (for <pat> as name) and LongIdent (for a Foo.Bar name). Note that the parsed pattern is occasionally more complex than what is in the source code (in particular, Named is used more often):

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
/// Walk over a pattern - this is for example used in 
/// let <pat> = <expr> or in the 'match' expression
let rec visitPattern = function
  | SynPat.Wild(_) -> 
      printfn "  .. underscore pattern"
  | SynPat.Named(pat, name, _, _, _) ->
      visitPattern pat
      printfn "  .. named as '%s'" name.idText
  | SynPat.LongIdent(LongIdentWithDots(ident, _), _, _, _, _, _) ->
      let names = String.concat "." [ for i in ident -> i.idText ]
      printfn "  .. identifier: %s" names
  | pat -> printfn "  .. other pattern: %A" pat

The function is recursive (for nested patterns such as (foo, _) as bar), but it does not call any of the functions defined later (because patterns cannot contain other syntactical elements).

The next function iterates over expressions - this is where most of the work would be and there are around 20 cases to cover (type SynExpr. and you'll get completion with other options). In the following, we only show how to handle if .. then .. and let .. = ...:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
/// Walk over an expression - if expression contains two or three 
/// sub-expressions (two if the 'else' branch is missing), let expression
/// contains pattern and two sub-expressions
let rec visitExpression = function
  | SynExpr.IfThenElse(cond, trueBranch, falseBranchOpt, _, _, _, _) ->
      // Visit all sub-expressions
      printfn "Conditional:"
      visitExpression cond
      visitExpression trueBranch
      falseBranchOpt |> Option.iter visitExpression 

  | SynExpr.LetOrUse(_, _, bindings, body, _) ->
      // Visit bindings (there may be multiple 
      // for 'let .. = .. and .. = .. in ...'
      printfn "LetOrUse with the following bindings:"
      for binding in bindings do
        let (Binding(access, kind, inlin, mutabl, attrs, xmlDoc, 
                     data, pat, retInfo, init, m, sp)) = binding
        visitPattern pat 
        visitExpression init
      // Visit the body expression
      printfn "And the following body:"
      visitExpression body
  | expr -> printfn " - not supported expression: %A" expr

The visitExpression function will be called from a function that visits all top-level declarations inside a module. In this tutorial, we ignore types and members, but that would be another source of calls to visitExpression.

Walking over declarations

As mentioned earlier, the AST of a file contains a number of module or namespace declarations (top-level node) that contain declarations inside a module (let bindings or types) or inisde a namespace (just types). The following functions walks over declarations - we ignore types, nested modules and all other elements and look only at top-level let bindings (values and functions):

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
/// Walk over a list of declarations in a module. This is anything
/// that you can write as a top-level inside module (let bindings,
/// nested modules, type declarations etc.)
let visitDeclarations decls = 
  for declaration in decls do
    match declaration with
    | SynModuleDecl.Let(isRec, bindings, range) ->
        // Let binding as a declaration is similar to let binding
        // as an expression (in visitExpression), but has no body
        for binding in bindings do
          let (Binding(access, kind, inlin, mutabl, attrs, xmlDoc, 
                       data, pat, retInfo, body, m, sp)) = binding
          visitPattern pat 
          visitExpression body         
    | _ -> printfn " - not supported declaration: %A" declaration

The visitDeclarations function will be called from a function that walks over a sequence of module or namespace declarations. This corresponds, for example, to a file with multiple namespace Foo declarations:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
/// Walk over all module or namespace declarations 
/// (basically 'module Foo =' or 'namespace Foo.Bar')
/// Note that there is one implicitly, even if the file
/// does not explicitly define it..
let visitModulesAndNamespaces modulesOrNss =
  for moduleOrNs in modulesOrNss do
    let (SynModuleOrNamespace(lid, isMod, decls, xml, attrs, _, m)) = moduleOrNs
    printfn "Namespace or module: %A" lid
    visitDeclarations decls

Now that we have functions that walk over the elements of the AST (starting from declaration, down to expressions and patterns), we can get AST of a sample input and run the above function.

Putting things together

As already discussed, the getUntypedTree function uses FSharpChecker to run the first phase (parsing) on the AST and get back the tree. The function requires F# source code together with location of the file. The location does not have to exist (it is used only for location information) and it can be in both Unix and Windows formats:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
// Sample input for the compiler service
let input = """
  let foo() = 
    let msg = "Hello world"
    if true then 
      printfn "%s" msg """
// File name in Unix format
let file = "/home/user/Test.fsx"

// Get the AST of sample F# code
let tree = getUntypedTree(file, input) 

When you run the code in F# interactive, you can enter tree;; in the interactive console and see pretty printed representation of the data structure - the tree contains a lot of information, so this is not particularly readable, but it gives you good idea about how the tree looks.

The returned tree value is again a discriminated union that can be two different cases - one case is ParsedInput.SigFile which represents F# signature file (*.fsi) and the other one is ParsedInput.ImplFile representing regular source code (*.fsx or *.fs). The implementation file contains a sequence of modules or namespaces that we can pass to the function implemented in the previous step:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
// Extract implementation file details
match tree with
| ParsedInput.ImplFile(implFile) ->
    // Extract declarations and walk over them
    let (ParsedImplFileInput(fn, script, name, _, _, modules, _)) = implFile
    visitModulesAndNamespaces modules
| _ -> failwith "F# Interface file (*.fsi) not supported."

Summary

In this tutorial, we looked at basic of working with the untyped abstract syntax tree. This is a comprehensive topic, so it is not possible to explain everything in a single article. The Fantomas project is a good example of tool based on the untyped AST that can help you understand more. In practice, it is also useful to combine the information here with some information you can obtain from the editor services discussed in the next tutorial.

namespace System
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Compiler
namespace Microsoft.FSharp.Compiler.SourceCodeServices
val checker : FSharpChecker

Full name: Untypedtree.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 getUntypedTree : file:string * input:string -> Compiler.Ast.ParsedInput

Full name: Untypedtree.getUntypedTree


 Get untyped tree for a specified input
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
member FSharpChecker.ParseFileInProject : filename:string * source:string * options:FSharpProjectOptions -> Async<FSharpParseFileResults>
property FSharpParseFileResults.ParseTree: Compiler.Ast.ParsedInput option
union case Option.Some: Value: 'T -> Option<'T>
val tree : Compiler.Ast.ParsedInput
union case Option.None: Option<'T>
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
module Ast

from Microsoft.FSharp.Compiler
val visitPattern : _arg1:SynPat -> unit

Full name: Untypedtree.visitPattern


 Walk over a pattern - this is for example used in
 let <pat> = <expr> or in the 'match' expression
type SynPat =
  | Const of constant: SynConst * range: range
  | Wild of range: range
  | Named of SynPat * id: Ident * isThisVar: bool * accessiblity: SynAccess option * range: range
  | Typed of SynPat * typeName: SynType * range: range
  | Attrib of SynPat * attributes: SynAttributes * range: range
  | Or of SynPat * SynPat * range: range
  | Ands of SynPat list * range: range
  | LongIdent of dotId: LongIdentWithDots * Ident option * SynValTyparDecls option * SynConstructorArgs * SynAccess option * range: range
  | Tuple of SynPat list * range: range
  | StructTuple of SynPat list * range: range
  ...
  member Range : range

Full name: Microsoft.FSharp.Compiler.Ast.SynPat
union case SynPat.Wild: range: Compiler.Range.range -> SynPat
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
union case SynPat.Named: SynPat * id: Ident * isThisVar: bool * accessiblity: SynAccess option * range: Compiler.Range.range -> SynPat
val pat : SynPat
val name : Ident
property Ident.idText: string
union case SynPat.LongIdent: dotId: LongIdentWithDots * Ident option * SynValTyparDecls option * SynConstructorArgs * SynAccess option * range: Compiler.Range.range -> SynPat
Multiple items
union case LongIdentWithDots.LongIdentWithDots: id: LongIdent * dotms: Compiler.Range.range list -> LongIdentWithDots

--------------------
type LongIdentWithDots =
  | LongIdentWithDots of id: LongIdent * dotms: range list
  member Lid : LongIdent
  member Range : range
  member RangeSansAnyExtraDot : range
  member ThereIsAnExtraDotAtTheEnd : bool

Full name: Microsoft.FSharp.Compiler.Ast.LongIdentWithDots
val ident : LongIdent
val names : string
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
val concat : sep:string -> strings:seq<string> -> string

Full name: Microsoft.FSharp.Core.String.concat
val i : Ident
val visitExpression : _arg1:SynExpr -> unit

Full name: Untypedtree.visitExpression


 Walk over an expression - if expression contains two or three
 sub-expressions (two if the 'else' branch is missing), let expression
 contains pattern and two sub-expressions
type SynExpr =
  | Paren of expr: SynExpr * leftParenRange: range * rightParenRange: range option * range: range
  | Quote of operator: SynExpr * isRaw: bool * quotedSynExpr: SynExpr * isFromQueryExpression: bool * range: range
  | Const of constant: SynConst * range: range
  | Typed of expr: SynExpr * typeSig: SynType * range: range
  | Tuple of exprs: SynExpr list * commaRanges: range list * range: range
  | StructTuple of SynExpr list * range list * range: range
  | ArrayOrList of isList: bool * exprs: SynExpr list * range: range
  | Record of baseInfo: (SynType * SynExpr * range * BlockSeparator option * range) option * copyInfo: (SynExpr * BlockSeparator) option * recordFields: (RecordFieldName * SynExpr option * BlockSeparator option) list * range: range
  | New of isProtected: bool * typeName: SynType * expr: SynExpr * range: range
  | ObjExpr of objType: SynType * argOpt: (SynExpr * Ident option) option * bindings: SynBinding list * extraImpls: SynInterfaceImpl list * newPos: range * range: range
  ...
  member IsArbExprAndThusAlreadyReportedError : bool
  member Range : range
  member RangeOfFirstPortion : range
  member RangeSansAnyExtraDot : range

Full name: Microsoft.FSharp.Compiler.Ast.SynExpr
union case SynExpr.IfThenElse: exprGuard: SynExpr * exprThen: SynExpr * optionalExprElse: SynExpr option * spIfToThen: SequencePointInfoForBinding * isFromErrorRecovery: bool * ifToThen: Compiler.Range.range * range: Compiler.Range.range -> SynExpr
val cond : SynExpr
val trueBranch : SynExpr
val falseBranchOpt : SynExpr option
module Option

from Microsoft.FSharp.Core
val iter : action:('T -> unit) -> option:'T option -> unit

Full name: Microsoft.FSharp.Core.Option.iter
union case SynExpr.LetOrUse: isRecursive: bool * isUse: bool * bindings: SynBinding list * exprBody: SynExpr * range: Compiler.Range.range -> SynExpr
val bindings : SynBinding list
val body : SynExpr
val binding : SynBinding
union case SynBinding.Binding: access: SynAccess option * bindingKind: SynBindingKind * mustInline: bool * isMutable: bool * attributes: SynAttributes * xmlDoc: PreXmlDoc * SynValData * headPat: SynPat * SynBindingReturnInfo option * expr: SynExpr * lhsRange: Compiler.Range.range * spBind: SequencePointInfoForBinding -> SynBinding
val access : SynAccess option
val kind : SynBindingKind
val inlin : bool
val mutabl : bool
val attrs : SynAttributes
val xmlDoc : PreXmlDoc
val data : SynValData
val retInfo : SynBindingReturnInfo option
val init : SynExpr
val m : Compiler.Range.range
val sp : SequencePointInfoForBinding
val expr : SynExpr
val visitDeclarations : decls:seq<SynModuleDecl> -> unit

Full name: Untypedtree.visitDeclarations


 Walk over a list of declarations in a module. This is anything
 that you can write as a top-level inside module (let bindings,
 nested modules, type declarations etc.)
val decls : seq<SynModuleDecl>
val declaration : SynModuleDecl
type SynModuleDecl =
  | ModuleAbbrev of Ident * LongIdent * range
  | NestedModule of SynComponentInfo * isRec: bool * SynModuleDecls * bool * range
  | Let of bool * SynBinding list * range
  | DoExpr of SequencePointInfoForBinding * SynExpr * range
  | Types of SynTypeDefn list * range
  | Exception of SynExceptionDefn * range
  | Open of LongIdentWithDots * range
  | Attributes of SynAttributes * range
  | HashDirective of ParsedHashDirective * range
  | NamespaceFragment of SynModuleOrNamespace
  member Range : range

Full name: Microsoft.FSharp.Compiler.Ast.SynModuleDecl
union case SynModuleDecl.Let: bool * SynBinding list * Compiler.Range.range -> SynModuleDecl
val isRec : bool
val range : Compiler.Range.range
val visitModulesAndNamespaces : modulesOrNss:seq<'a> -> unit

Full name: Untypedtree.visitModulesAndNamespaces


 Walk over all module or namespace declarations
 (basically 'module Foo =' or 'namespace Foo.Bar')
 Note that there is one implicitly, even if the file
 does not explicitly define it..
val modulesOrNss : seq<'a>
val moduleOrNs : 'a
Multiple items
union case SynModuleOrNamespace.SynModuleOrNamespace: id: LongIdent * isRec: bool * isModule: bool * decls: SynModuleDecls * xmlDoc: PreXmlDoc * attributes: SynAttributes * access: SynAccess option * range: Compiler.Range.range -> SynModuleOrNamespace

--------------------
type SynModuleOrNamespace =
  | SynModuleOrNamespace of id: LongIdent * isRec: bool * isModule: bool * decls: SynModuleDecls * xmlDoc: PreXmlDoc * attributes: SynAttributes * access: SynAccess option * range: range
  member Range : range

Full name: Microsoft.FSharp.Compiler.Ast.SynModuleOrNamespace
val input : string

Full name: Untypedtree.input
val file : string

Full name: Untypedtree.file
val tree : ParsedInput

Full name: Untypedtree.tree
val getUntypedTree : file:string * input:string -> ParsedInput

Full name: Untypedtree.getUntypedTree


 Get untyped tree for a specified input
type ParsedInput =
  | ImplFile of ParsedImplFileInput
  | SigFile of ParsedSigFileInput
  member Range : range

Full name: Microsoft.FSharp.Compiler.Ast.ParsedInput
union case ParsedInput.ImplFile: ParsedImplFileInput -> ParsedInput
val implFile : ParsedImplFileInput
Multiple items
union case ParsedImplFileInput.ParsedImplFileInput: filename: string * isScript: bool * QualifiedNameOfFile * ScopedPragma list * ParsedHashDirective list * SynModuleOrNamespace list * (bool * bool) -> ParsedImplFileInput

--------------------
type ParsedImplFileInput = | ParsedImplFileInput of filename: string * isScript: bool * QualifiedNameOfFile * ScopedPragma list * ParsedHashDirective list * SynModuleOrNamespace list * (bool * bool)

Full name: Microsoft.FSharp.Compiler.Ast.ParsedImplFileInput
val fn : string
val script : bool
val name : QualifiedNameOfFile
val modules : SynModuleOrNamespace list
Fork me on GitHub