Header menu logo F# Compiler Guide

Compiler Services: APIs for the untyped AST

The ParsedInput module

As established in Tutorial: Expressions, the AST held in a ParsedInput value can be traversed by a set of recursive functions. It can be tedious and error-prone to write these functions from scratch every time, though, so the ParsedInput module exposes a number of functions to make common operations easier.

For example:

SyntaxVisitorBase & SyntaxTraversal.Traverse

While the ParsedInput module functions are usually the simplest way to meet most needs, there is also a SyntaxVisitorBase-based API that can provide somewhat more fine-grained control over syntax traversal for a subset of use-cases at the expense of a bit more ceremony and complexity.

Examples

Let's start by introducing a helper function for constructing an AST from source code so we can run through some real examples:

#r "FSharp.Compiler.Service.dll"
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Text
open FSharp.Compiler.Syntax

let checker = FSharpChecker.Create()

/// A helper for constructing a `ParsedInput` from a code snippet.
let mkTree codeSample =
    let parseFileResults =
        checker.ParseFile(
            "FileName.fs",
            SourceText.ofString codeSample,
            { FSharpParsingOptions.Default with SourceFiles = [| "FileName.fs" |] }
        )
        |> Async.RunSynchronously

    parseFileResults.ParseTree

ParsedInput.exists

Now consider the following code sample:

let brokenTypeDefn = """
module Lib

// Whoops, we forgot the equals sign.
type T { A: int; B: int }  
"""

Let's say we have a code fix for adding an equals sign to a type definition that's missing one—like the one above. We want to offer the fix when the user's cursor is inside of—or just after—the broken type definition.

We can determine this by using ParsedInput.exists and passing in the position of the user's cursor:

// type T { A: int; B: int } 
// ···········↑
let posInMiddleOfTypeDefn = Position.mkPos 5 12

Given that cursor position, all we need to do is find a SynTypeDefn node:

let isPosInTypeDefn = // true.
    (posInMiddleOfTypeDefn, mkTree brokenTypeDefn)
    ||> ParsedInput.exists (fun _path node ->
        match node with
        | SyntaxNode.SynTypeDefn _ -> true
        | _ -> false)

If the position passed into ParsedInput.exists is not contained in any node in the given AST, but rather is below or to the right of all nodes, ParsedInput.exists will fall back to exploring the nearest branch above and/or to the left. This is useful because the user's cursor may lie beyond the range of all nodes.

// type T { A: int; B: int }  
// ··························↑
let posAfterTypeDefn = Position.mkPos 5 28

Our function still returns true if the cursor is past the end of the type definition node itself:

let isPosInTypeDefn' = // Still true.
    (posAfterTypeDefn, mkTree brokenTypeDefn)
    ||> ParsedInput.exists (fun _path node ->
        match node with
        | SyntaxNode.SynTypeDefn _ -> true
        | _ -> false)

ParsedInput.fold

ParsedInput.fold can be useful when writing an analyzer to collect diagnostics from entire input files.

Take this code that has unnecessary parentheses in both patterns and expressions:

let unnecessaryParentheses = """
let (x) = (id (3))
"""

We can gather the ranges of all unnecessary parentheses like this:

open System.Collections.Generic

module HashSet =
    let add item (set: HashSet<_>) =
        ignore (set.Add item)
        set

let unnecessaryParenthesesRanges =
   (HashSet Range.comparer, mkTree unnecessaryParentheses) ||> ParsedInput.fold (fun ranges path node ->
       match node with
       | SyntaxNode.SynExpr(SynExpr.Paren(expr = inner; rightParenRange = Some _; range = range)) when
           not (SynExpr.shouldBeParenthesizedInContext getLineStr path inner)
           ->
           ranges |> HashSet.add range

       | SyntaxNode.SynPat(SynPat.Paren(inner, range)) when
           not (SynPat.shouldBeParenthesizedInContext path inner)
           ->
           ranges |> HashSet.add range

       | _ ->
           ranges)

ParsedInput.tryNode

Sometimes, we might just want to get whatever node is directly at a given position—for example, if the user's cursor is on an argument of a function being applied, we can find the node representing the argument and use its path to backtrack and find the function's name.

let functionApplication = """
f x y  
"""

If we have our cursor on y:

// f x y
// ·····↑
let posOnY = Position.mkPos 2 5

The syntax node representing the function f technically contains the cursor's position, but ParsedInput.tryNode will keep diving until it finds the deepest node containing the position.

We can thus get the node representing y and its ancestors (the path) like this:

let yAndPath = // Some (SynExpr (Ident y), [SynExpr (App …); …])
    mkTree functionApplication
    |> ParsedInput.tryNode posOnY

Note that, unlike ParsedInput.exists, ParsedInput.tryPick, and ParsedInput.tryPickLast, ParsedInput.tryNode does not fall back to the nearest branch above or to the left.

// f x y
// ······↑
let posAfterY = Position.mkPos 2 8

If we take the same code snippet but pass in a position after y, we get no node:

let nope = // None.
    mkTree functionApplication
    |> ParsedInput.tryNode posAfterY

ParsedInput.tryPick

Now imagine that we have a code fix for converting a record construction expression into an anonymous record construction expression when there is no record type in scope whose fields match.

let recordExpr = """
let r = { A = 1; B = 2 }
"""

We can offer this fix when the user's cursor is inside of a record expression by using ParsedInput.tryPick to return the surrounding record expression's range, if any.

// let r = { A = 1; B = 2 }
// ······················↑
let posInRecordExpr = Position.mkPos 2 25

Here, even though ParsedInput.tryPick will try to cleave to the given position by default, we want to verify that the record expression node that we've come across actually contains the position, since, like ParsedInput.exists, ParsedInput.tryPick will also fall back to the nearest branch above and/or to the left if no node actually contains the position. In this case, we don't want to offer the code fix if the user's cursor isn't actually inside of the record expression.

let recordExprRange = // Some (2,8--2,24).
    (posInRecordExpr, mkTree recordExpr)
    ||> ParsedInput.tryPick (fun _path node ->
        match node with
        | SyntaxNode.SynExpr(SynExpr.Record(range = range)) when
            Range.rangeContainsPos range posInRecordExpr
            -> Some range
        | _ -> None)

We might also sometimes want to make use of the path parameter. Take this simple function definition:

let myFunction = """
module Lib

let myFunction paramOne paramTwo =
    ()
"""

Imagine we want to grab the myFunction name from the headPat in the SynBinding.

We can write a function to match the node we're looking for—and not match anything we're not looking for (like the argument patterns)—by taking its path into account:

let myFunctionId = // Some "myFunction".
    (Position.pos0, mkTree myFunction)
    ||> ParsedInput.tryPick (fun path node ->
        // Match on the node and the path (the node's ancestors) to see whether:
        //   1. The node is a pattern.
        //   2. The pattern is a long identifier pattern.
        //   3. The pattern's parent node (the head of the path) is a binding.
        match node, path with
        | SyntaxNode.SynPat(SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ]))),
          SyntaxNode.SynBinding _ :: _ ->
            // We have found what we're looking for.
            Some ident.idText
        | _ ->
            // If the node or its context don't match,
            // we continue.
            None)

Instead of traversing manually from ParsedInput to SynModuleOrNamespace to SynModuleDecl.Let to SynBinding to SynPat, we leverage the default navigation that happens in ParsedInput.tryPick.
ParsedInput.tryPick will short-circuit once we have indicated that we have found what we're looking for by returning Some value.

Our code sample of course only had one let-binding and thus we didn't need to specify any further logic to differentiate between bindings.

Let's consider a second example involving multiple let-bindings:

let multipleLetsInModule = """
module X

let a = 0
let b = 1
let c = 2
"""

In this case, we know the user's cursor inside an IDE is placed after c, and we are interested in the body expression of the last let-binding.

// …
// let c = 2
// ·····↑
let posInLastLet = Position.mkPos 6 5

Thanks to the cursor position we passed in, we do not need to write any code to exclude the expressions of the sibling let-bindings. ParsedInput.tryPick will check whether the current position is inside any given syntax node before drilling deeper.

let bodyOfLetContainingPos = // Some (Const (Int32 2, (6,8--6,9))).
    (posInLastLet, mkTree multipleLetsInModule)
    ||> ParsedInput.tryPick (fun _path node ->
        match node with
        | SyntaxNode.SynBinding(SynBinding(expr = e)) -> Some e
        | _ -> None)

As noted above, ParsedInput.tryPick will short-circuit at the first matching node. ParsedInput.tryPickLast can be used to get the last matching node that contains a given position.

Take this example of multiple nested modules:

let nestedModules = """
module M

module N =
    module O =
        module P = begin end
"""

By using ParsedInput.tryPick, we'll get the name of the outermost nested module even if we pass in a position inside the innermost, since the innermost is contained within the outermost.

This position is inside module P, which is nested inside of module O, which is nested inside of module N, which is nested inside of top-level module M:

// module M
//
// module N =
//     module O =
//         module P = begin end
// ···························↑
let posInsideOfInnermostNestedModule = Position.mkPos 6 28

ParsedInput.tryPick short-circuits on the first match, and since module N is the first nested module whose range contains position (6, 28), that's the result we get.

let outermostNestedModule = // Some ["N"].
    (posInsideOfInnermostNestedModule, mkTree nestedModules)
    ||> ParsedInput.tryPick (fun _path node ->
        match node with
        | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) ->
            Some [for ident in longId -> ident.idText]
        | _ -> None)

ParsedInput.tryPickLast

If however we use the same code snippet and pass the same position into ParsedInput.tryPickLast, we can get the name of the last (deepest or innermost) matching node:

let innermostNestedModule = // Some ["P"].
    (posInsideOfInnermostNestedModule, mkTree nestedModules)
    ||> ParsedInput.tryPickLast (fun _path node ->
        match node with
        | SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) ->
            Some [for ident in longId -> ident.idText]
        | _ -> None)

If we want the next-to-innermost nested module, we can do likewise but make use of the path parameter:

let nextToInnermostNestedModule = // Some ["O"].
    (posInsideOfInnermostNestedModule, mkTree nestedModules)
    ||> ParsedInput.tryPickLast (fun path node ->
        match node, path with
        | SyntaxNode.SynModule(SynModuleDecl.NestedModule _),
          SyntaxNode.SynModule(SynModuleDecl.NestedModule(moduleInfo = SynComponentInfo(longId = longId))) :: _ ->
            Some [for ident in longId -> ident.idText]
        | _ -> None)

SyntaxTraversal.Traverse

Consider again the following code sample:

let codeSample = """
module Lib

let myFunction paramOne paramTwo =
    ()
"""

Imagine we wish to grab the myFunction name from the headPat in the SynBinding.

We can create a visitor to traverse the tree and find the function name:

let visitor =
    { new SyntaxVisitorBase<string>() with
        override this.VisitPat(path, defaultTraverse, synPat) =
            // First check if the pattern is what we are looking for.
            match synPat with
            | SynPat.LongIdent(longDotId = SynLongIdent(id = [ ident ])) ->
                // Next we can check if the current path of visited nodes, matches our expectations.
                // The path will contain all the ancestors of the current node.
                match path with
                // The parent node of `synPat` should be a `SynBinding`.
                | SyntaxNode.SynBinding _ :: _ ->
                    // We return a `Some` option to indicate we found what we are looking for.
                    Some ident.idText
                // If the parent is something else, we can skip it here.
                | _ -> None
            | _ -> None }

let result = SyntaxTraversal.Traverse(Position.pos0, mkTree codeSample, visitor) // Some "myFunction"

Instead of traversing manually from ParsedInput to SynModuleOrNamespace to SynModuleDecl.Let to SynBinding to SynPat, we leverage the default navigation that happens in SyntaxTraversal.Traverse.
A SyntaxVisitorBase will shortcut all other code paths once a single VisitXYZ override has found anything.

Our code sample of course only had one let binding and thus we didn't need to specify any further logic whether to differentiate between multiple bindings.

SyntaxTraversal.Traverse: using position

Let's now consider a second example where we know the user's cursor inside an IDE is placed after c and we are interested in the body expression of the let binding.

let secondCodeSample = """
module X

let a = 0
let b = 1
let c = 2
"""

let secondVisitor =
    { new SyntaxVisitorBase<SynExpr>() with
        override this.VisitBinding(path, defaultTraverse, binding) =
            match binding with
            | SynBinding(expr = e) -> Some e }

let cursorPos = Position.mkPos 6 5

let secondResult =
    SyntaxTraversal.Traverse(cursorPos, mkTree secondCodeSample, secondVisitor) // Some (Const (Int32 2, (6,8--6,9)))

Due to our passed cursor position, we did not need to write any code to exclude the expressions of the other let bindings. SyntaxTraversal.Traverse will check whether the current position is inside any syntax node before drilling deeper.

SyntaxTraversal.Traverse: using defaultTraverse

Lastly, some VisitXYZ overrides can contain a defaultTraverse. This helper allows you to continue the default traversal when you currently hit a node that is not of interest. Consider 1 + 2 + 3 + 4, this will be reflected in a nested infix application expression. If the cursor is at the end of the entire expression, we can grab the value of 4 using the following visitor:

let thirdCodeSample = "let sum = 1 + 2 + 3 + 4"

(*
AST will look like:

Let
 (false,
  [SynBinding
     (None, Normal, false, false, [],
      PreXmlDoc ((1,0), Fantomas.FCS.Xml.XmlDocCollector),
      SynValData
        (None, SynValInfo ([], SynArgInfo ([], false, None)), None,
         None),
      Named (SynIdent (sum, None), false, None, (1,4--1,7)), None,
      App
        (NonAtomic, false,
         App
           (NonAtomic, true,
            LongIdent
              (false,
               SynLongIdent
                 ([op_Addition], [], [Some (OriginalNotation "+")]),
               None, (1,20--1,21)),
            App
              (NonAtomic, false,
               App
                 (NonAtomic, true,
                  LongIdent
                    (false,
                     SynLongIdent
                       ([op_Addition], [],
                        [Some (OriginalNotation "+")]), None,
                     (1,16--1,17)),
                  App
                    (NonAtomic, false,
                     App
                       (NonAtomic, true,
                        LongIdent
                          (false,
                           SynLongIdent
                             ([op_Addition], [],
                              [Some (OriginalNotation "+")]), None,
                           (1,12--1,13)),
                        Const (Int32 1, (1,10--1,11)), (1,10--1,13)),
                     Const (Int32 2, (1,14--1,15)), (1,10--1,15)),
                  (1,10--1,17)), Const (Int32 3, (1,18--1,19)),
               (1,10--1,19)), (1,10--1,21)),
         Const (Int32 4, (1,22--1,23)), (1,10--1,23)), (1,4--1,7),
      Yes (1,0--1,23), { LeadingKeyword = Let (1,0--1,3)
                         InlineKeyword = None
                         EqualsRange = Some (1,8--1,9) })
*)

let thirdCursorPos = Position.mkPos 1 22

let thirdVisitor =
    { new SyntaxVisitorBase<int>() with
        override this.VisitExpr(path, traverseSynExpr, defaultTraverse, synExpr) =
            match synExpr with
            | SynExpr.Const (constant = SynConst.Int32 v) -> Some v
            // We do want to continue to traverse when nodes like `SynExpr.App` are found.
            | otherExpr -> defaultTraverse otherExpr }

let thirdResult =
    SyntaxTraversal.Traverse(cursorPos, mkTree thirdCodeSample, thirdVisitor) // Some 4

defaultTraverse is especially useful when you do not know upfront what syntax tree you will be walking.
This is a common case when dealing with IDE tooling. You won't know what actual code the end-user is currently processing.

Note: SyntaxVisitorBase is designed to find a single value inside a tree!
This is not an ideal solution when you are interested in all nodes of certain shape.
It will always verify if the given cursor position is still matching the range of the node.
As a fallback the first branch will be explored when you pass Position.pos0.
By design, it is meant to find a single result.

Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Compiler
namespace FSharp.Compiler.CodeAnalysis
namespace FSharp.Compiler.Text
namespace FSharp.Compiler.Syntax
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.")>] ?useSyntaxTreeCache: bool * [<Experimental ("This parameter is experimental and likely to be removed in the future.")>] ?useTransparentCompiler: bool -> FSharpChecker
val mkTree: codeSample: string -> ParsedInput
 A helper for constructing a `ParsedInput` from a code snippet.
val codeSample: string
val parseFileResults: FSharpParseFileResults
member FSharpChecker.ParseFile: fileName: string * projectSnapshot: FSharpProjectSnapshot * ?userOpName: string -> Async<FSharpParseFileResults>
member FSharpChecker.ParseFile: fileName: string * sourceText: ISourceText * options: FSharpParsingOptions * ?cache: bool * ?userOpName: string -> Async<FSharpParseFileResults>
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>
type FSharpParsingOptions = { SourceFiles: string array ApplyLineDirectives: bool ConditionalDefines: string list DiagnosticOptions: FSharpDiagnosticOptions LangVersionText: string IsInteractive: bool IndentationAwareSyntax: bool option StrictIndentation: bool option CompilingFSharpCore: bool IsExe: bool } static member Default: FSharpParsingOptions
<summary> Options used to determine active --define conditionals and other options relevant to parsing files in a project </summary>
property FSharpParsingOptions.Default: FSharpParsingOptions with get
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: System.Threading.CancellationToken -> 'T
property FSharpParseFileResults.ParseTree: ParsedInput with get
<summary> The syntax tree resulting from the parse </summary>
val brokenTypeDefn: string
val posInMiddleOfTypeDefn: pos
Multiple items
module Position from FSharp.Compiler.Text

--------------------
[<Struct>] type Position = member Column: int member Line: int
<summary> Represents a position in a file </summary>
val mkPos: line: int -> column: int -> pos
<summary> Create a position for the given line and column </summary>
val isPosInTypeDefn: bool
Multiple items
module ParsedInput from FSharp.Compiler.Syntax
<summary> Holds operations for working with the untyped abstract syntax tree (<see cref="T:FSharp.Compiler.Syntax.ParsedInput" />). </summary>

--------------------
type ParsedInput = | ImplFile of ParsedImplFileInput | SigFile of ParsedSigFileInput member FileName: string member Identifiers: Set<string> member QualifiedName: QualifiedNameOfFile member Range: range member ScopedPragmas: ScopedPragma list
<summary> Represents the syntax tree for a parsed implementation or signature file </summary>
val exists: predicate: (SyntaxVisitorPath -> SyntaxNode -> bool) -> position: pos -> parsedInput: ParsedInput -> bool
<summary> Applies the given predicate to each node of the AST and its context (path) down to a given position, returning true if a matching node is found, otherwise false. Traversal is short-circuited if no matching node is found through the given position. </summary>
<param name="predicate">The predicate to match each node against.</param>
<param name="position">The position in the input file down to which to apply the function.</param>
<param name="parsedInput">The AST to search.</param>
<returns>True if a matching node is found, or false if no matching node is found.</returns>
<example><code lang="fsharp"> let isInTypeDefn = (pos, parsedInput) ||&gt; ParsedInput.exists (fun _path node -&gt; match node with | SyntaxNode.SynTypeDefn _ -&gt; true | _ -&gt; false) </code></example>
val _path: SyntaxVisitorPath
val node: SyntaxNode
Multiple items
module SyntaxNode from FSharp.Compiler.Syntax
<summary> Holds operations for working with <see cref="T:FSharp.Compiler.Syntax.SyntaxNode" />s in the untyped abstract syntax tree (AST). </summary>

--------------------
type SyntaxNode = | SynPat of SynPat | SynType of SynType | SynExpr of SynExpr | SynModule of SynModuleDecl | SynModuleOrNamespace of SynModuleOrNamespace | SynTypeDefn of SynTypeDefn | SynMemberDefn of SynMemberDefn | SynMatchClause of SynMatchClause | SynBinding of SynBinding | SynModuleOrNamespaceSig of SynModuleOrNamespaceSig ... member Range: range
<summary> Represents a major syntax node in the untyped abstract syntax tree. </summary>
union case SyntaxNode.SynTypeDefn: SynTypeDefn -> SyntaxNode
val posAfterTypeDefn: pos
val isPosInTypeDefn': bool
val getLineStr: line: int -> string
val line: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val failwith: message: string -> 'T
val unnecessaryParentheses: string
namespace System
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type HashSet<'T> = interface ICollection<'T> interface IEnumerable<'T> interface IEnumerable interface IReadOnlyCollection<'T> interface ISet<'T> interface IReadOnlySet<'T> interface IDeserializationCallback interface ISerializable new: unit -> unit + 5 overloads member Add: item: 'T -> bool ...
<summary>Represents a set of values.</summary>
<typeparam name="T">The type of elements in the hash set.</typeparam>


--------------------
HashSet() : HashSet<'T>
HashSet(collection: IEnumerable<'T>) : HashSet<'T>
HashSet(comparer: IEqualityComparer<'T>) : HashSet<'T>
HashSet(capacity: int) : HashSet<'T>
HashSet(collection: IEnumerable<'T>, comparer: IEqualityComparer<'T>) : HashSet<'T>
HashSet(capacity: int, comparer: IEqualityComparer<'T>) : HashSet<'T>
val add: item: 'a -> set: HashSet<'a> -> HashSet<'a>
val item: 'a
val set: HashSet<'a>
val ignore: value: 'T -> unit
HashSet.Add(item: 'a) : bool
val unnecessaryParenthesesRanges: HashSet<range>
Multiple items
module Range from FSharp.Compiler.Text

--------------------
[<Struct>] type Range = member End: pos member EndColumn: int member EndLine: int member EndRange: range member FileName: string member IsSynthetic: bool member Start: pos member StartColumn: int member StartLine: int member StartRange: range ...
<summary> Represents a range within a file </summary>
val comparer: IEqualityComparer<range>
<summary> Equality comparer for range. </summary>
val fold: folder: ('State -> SyntaxVisitorPath -> SyntaxNode -> 'State) -> state: 'State -> parsedInput: ParsedInput -> 'State
<summary> Applies a function to each node of the AST and its context (path), threading an accumulator through the computation. </summary>
<param name="folder">The function to use to update the state given each node and its context.</param>
<param name="state">The initial state.</param>
<param name="parsedInput">The AST to fold over.</param>
<returns>The final state.</returns>
<example><code lang="fsharp"> let unnecessaryParentheses = (HashSet Range.comparer, parsedInput) ||&gt; ParsedInput.fold (fun acc path node -&gt; match node with | SyntaxNode.SynExpr (SynExpr.Paren (expr = inner; rightParenRange = Some _; range = range)) when not (SynExpr.shouldBeParenthesizedInContext getLineString path inner) -&gt; ignore (acc.Add range) acc | SyntaxNode.SynPat (SynPat.Paren (inner, range)) when not (SynPat.shouldBeParenthesizedInContext path inner) -&gt; ignore (acc.Add range) acc | _ -&gt; acc) </code></example>
val ranges: HashSet<range>
val path: SyntaxVisitorPath
union case SyntaxNode.SynExpr: SynExpr -> SyntaxNode
Multiple items
module SynExpr from FSharp.Compiler.Syntax

--------------------
type SynExpr = | Paren of expr: SynExpr * leftParenRange: range * rightParenRange: range option * range: range | Quote of operator: SynExpr * isRaw: bool * quotedExpr: SynExpr * isFromQueryExpression: bool * range: range | Const of constant: SynConst * range: range | Typed of expr: SynExpr * targetType: SynType * range: range | Tuple of isStruct: bool * exprs: SynExpr list * commaRanges: range list * range: range | AnonRecd of isStruct: bool * copyInfo: (SynExpr * BlockSeparator) option * recordFields: (SynLongIdent * range option * SynExpr) list * range: range * trivia: SynExprAnonRecdTrivia | ArrayOrList of isArray: bool * exprs: SynExpr list * range: range | Record of baseInfo: (SynType * SynExpr * range * BlockSeparator option * range) option * copyInfo: (SynExpr * BlockSeparator) option * recordFields: SynExprRecordField list * range: range | New of isProtected: bool * targetType: SynType * expr: SynExpr * range: range | ObjExpr of objType: SynType * argOptions: (SynExpr * Ident option) option * withKeyword: range option * bindings: SynBinding list * members: SynMemberDefns * extraImpls: SynInterfaceImpl list * newExprRange: range * range: range ... member IsArbExprAndThusAlreadyReportedError: bool member Range: range member RangeOfFirstPortion: range member RangeWithoutAnyExtraDot: range
<summary> Represents a syntax tree for F# expressions </summary>
union case SynExpr.Paren: expr: SynExpr * leftParenRange: range * rightParenRange: range option * range: range -> SynExpr
<summary> F# syntax: (expr) Parenthesized expressions. Kept in AST to distinguish A.M((x, y)) from A.M(x, y), among other things. </summary>
val inner: SynExpr
union case Option.Some: Value: 'T -> Option<'T>
Multiple items
val range: range

--------------------
type range = Range
<summary> Represents a range within a file </summary>
val shouldBeParenthesizedInContext: getSourceLineStr: (int -> string) -> path: SyntaxVisitorPath -> expr: SynExpr -> bool
<summary> Returns true if the given expression should be parenthesized in the given context, otherwise false. </summary>
<param name="getSourceLineStr">A function for getting the text of a given source line.</param>
<param name="path">The expression's ancestor nodes.</param>
<param name="expr">The expression to check.</param>
<returns>True if the given expression should be parenthesized in the given context, otherwise false.</returns>
union case SyntaxNode.SynPat: SynPat -> SyntaxNode
Multiple items
module SynPat from FSharp.Compiler.Syntax

--------------------
type SynPat = | Const of constant: SynConst * range: range | Wild of range: range | Named of ident: SynIdent * isThisVal: bool * accessibility: SynAccess option * range: range | Typed of pat: SynPat * targetType: SynType * range: range | Attrib of pat: SynPat * attributes: SynAttributes * range: range | Or of lhsPat: SynPat * rhsPat: SynPat * range: range * trivia: SynPatOrTrivia | ListCons of lhsPat: SynPat * rhsPat: SynPat * range: range * trivia: SynPatListConsTrivia | Ands of pats: SynPat list * range: range | As of lhsPat: SynPat * rhsPat: SynPat * range: range | LongIdent of longDotId: SynLongIdent * extraId: Ident option * typarDecls: SynValTyparDecls option * argPats: SynArgPats * accessibility: SynAccess option * range: range ... member Range: range
<summary> Represents a syntax tree for an F# pattern </summary>
union case SynPat.Paren: pat: SynPat * range: range -> SynPat
<summary> A parenthesized pattern </summary>
val inner: SynPat
val shouldBeParenthesizedInContext: path: SyntaxVisitorPath -> pat: SynPat -> bool
<summary> Returns true if the given pattern should be parenthesized in the given context, otherwise false. </summary>
<param name="path">The pattern's ancestor nodes.</param>
<param name="pat">The pattern to check.</param>
<returns>True if the given pattern should be parenthesized in the given context, otherwise false.</returns>
val functionApplication: string
val posOnY: pos
val yAndPath: (SyntaxNode * SyntaxVisitorPath) option
val tryNode: position: pos -> parsedInput: ParsedInput -> (SyntaxNode * SyntaxVisitorPath) option
<summary> Dives to the deepest node that contains the given position, returning the node and its path if found, or <c>None</c> if no node contains the position. </summary>
<param name="position">The position in the input file down to which to dive.</param>
<param name="parsedInput">The AST to search.</param>
<returns>The deepest node containing the given position, along with the path taken through the node's ancestors to find it.</returns>
val posAfterY: pos
val nope: (SyntaxNode * SyntaxVisitorPath) option
val recordExpr: string
val posInRecordExpr: pos
val recordExprRange: range option
val tryPick: chooser: (SyntaxVisitorPath -> SyntaxNode -> 'T option) -> position: pos -> parsedInput: ParsedInput -> 'T option
<summary> Applies the given function to each node of the AST and its context (path) down to a given position, returning <c>Some x</c> for the first node for which the function returns <c>Some x</c> for some value <c>x</c>, otherwise <c>None</c>. Traversal is short-circuited if no matching node is found through the given position. </summary>
<param name="chooser">The function to apply to each node and its context to derive an optional value.</param>
<param name="position">The position in the input file down to which to apply the function.</param>
<param name="parsedInput">The AST to search.</param>
<returns>The first value for which the function returns <c>Some</c>, or <c>None</c> if no matching node is found.</returns>
<example><code lang="fsharp"> let range = (pos, parsedInput) ||&gt; ParsedInput.tryPick (fun _path node -&gt; match node with | SyntaxNode.SynExpr (SynExpr.InterpolatedString (range = range)) when rangeContainsPos range pos -&gt; Some range | _ -&gt; None) </code></example>
union case SynExpr.Record: baseInfo: (SynType * SynExpr * range * BlockSeparator option * range) option * copyInfo: (SynExpr * BlockSeparator) option * recordFields: SynExprRecordField list * range: range -> SynExpr
<summary> F# syntax: { f1=e1; ...; fn=en } inherit includes location of separator (for tooling) copyOpt contains range of the following WITH part (for tooling) every field includes range of separator after the field (for tooling) </summary>
val rangeContainsPos: range -> pos -> bool
<summary> Test to see if a range contains a position </summary>
union case Option.None: Option<'T>
val myFunction: string
val myFunctionId: string option
val pos0: pos
<summary> The zero position </summary>
union case SynPat.LongIdent: longDotId: SynLongIdent * extraId: Ident option * typarDecls: SynValTyparDecls option * argPats: SynArgPats * accessibility: SynAccess option * range: range -> SynPat
<summary> A long identifier pattern possibly with argument patterns </summary>
Multiple items
union case SynLongIdent.SynLongIdent: id: LongIdent * dotRanges: range list * trivia: FSharp.Compiler.SyntaxTrivia.IdentTrivia option list -> SynLongIdent

--------------------
type SynLongIdent = | SynLongIdent of id: LongIdent * dotRanges: range list * trivia: IdentTrivia option list member Dots: range list member IdentsWithTrivia: SynIdent list member LongIdent: LongIdent member Range: range member RangeWithoutAnyExtraDot: range member ThereIsAnExtraDotAtTheEnd: bool member Trivia: IdentTrivia list
<summary> Represents a long identifier with possible '.' at end. Typically dotRanges.Length = lid.Length-1, but they may be same if (incomplete) code ends in a dot, e.g. "Foo.Bar." The dots mostly matter for parsing, and are typically ignored by the typechecker, but if dotRanges.Length = lid.Length, then the parser must have reported an error, so the typechecker is allowed more freedom about typechecking these expressions. LongIdent can be empty list - it is used to denote that name of some AST element is absent (i.e. empty type name in inherit) </summary>
val id: x: 'T -> 'T
val ident: Ident
union case SyntaxNode.SynBinding: SynBinding -> SyntaxNode
property Ident.idText: string with get
val multipleLetsInModule: string
val posInLastLet: pos
val bodyOfLetContainingPos: SynExpr option
Multiple items
union case SynBinding.SynBinding: accessibility: SynAccess option * kind: SynBindingKind * isInline: bool * isMutable: bool * attributes: SynAttributes * xmlDoc: FSharp.Compiler.Xml.PreXmlDoc * valData: SynValData * headPat: SynPat * returnInfo: SynBindingReturnInfo option * expr: SynExpr * range: range * debugPoint: DebugPointAtBinding * trivia: FSharp.Compiler.SyntaxTrivia.SynBindingTrivia -> SynBinding

--------------------
type SynBinding = | SynBinding of accessibility: SynAccess option * kind: SynBindingKind * isInline: bool * isMutable: bool * attributes: SynAttributes * xmlDoc: PreXmlDoc * valData: SynValData * headPat: SynPat * returnInfo: SynBindingReturnInfo option * expr: SynExpr * range: range * debugPoint: DebugPointAtBinding * trivia: SynBindingTrivia member RangeOfBindingWithRhs: range member RangeOfBindingWithoutRhs: range member RangeOfHeadPattern: range
<summary> Represents a binding for a 'let' or 'member' declaration </summary>
val e: SynExpr
val nestedModules: string
val posInsideOfInnermostNestedModule: pos
val outermostNestedModule: string list option
union case SyntaxNode.SynModule: SynModuleDecl -> SyntaxNode
type SynModuleDecl = | ModuleAbbrev of ident: Ident * longId: LongIdent * range: range | NestedModule of moduleInfo: SynComponentInfo * isRecursive: bool * decls: SynModuleDecl list * isContinuing: bool * range: range * trivia: SynModuleDeclNestedModuleTrivia | Let of isRecursive: bool * bindings: SynBinding list * range: range | Expr of expr: SynExpr * range: range | Types of typeDefns: SynTypeDefn list * range: range | Exception of exnDefn: SynExceptionDefn * range: range | Open of target: SynOpenDeclTarget * range: range | Attributes of attributes: SynAttributes * range: range | HashDirective of hashDirective: ParsedHashDirective * range: range | NamespaceFragment of fragment: SynModuleOrNamespace member Range: range
<summary> Represents a definition within a module </summary>
union case SynModuleDecl.NestedModule: moduleInfo: SynComponentInfo * isRecursive: bool * decls: SynModuleDecl list * isContinuing: bool * range: range * trivia: FSharp.Compiler.SyntaxTrivia.SynModuleDeclNestedModuleTrivia -> SynModuleDecl
<summary> A nested module definition 'module X = ...' </summary>
Multiple items
union case SynComponentInfo.SynComponentInfo: attributes: SynAttributes * typeParams: SynTyparDecls option * constraints: SynTypeConstraint list * longId: LongIdent * xmlDoc: FSharp.Compiler.Xml.PreXmlDoc * preferPostfix: bool * accessibility: SynAccess option * range: range -> SynComponentInfo

--------------------
type SynComponentInfo = | SynComponentInfo of attributes: SynAttributes * typeParams: SynTyparDecls option * constraints: SynTypeConstraint list * longId: LongIdent * xmlDoc: PreXmlDoc * preferPostfix: bool * accessibility: SynAccess option * range: range member Range: range
<summary> Represents the syntax tree associated with the name of a type definition or module in signature or implementation. This includes the name, attributes, type parameters, constraints, documentation and accessibility for a type definition or module. For modules, entries such as the type parameters are always empty. </summary>
val longId: LongIdent
val innermostNestedModule: string list option
val tryPickLast: chooser: (SyntaxVisitorPath -> SyntaxNode -> 'T option) -> position: pos -> parsedInput: ParsedInput -> 'T option
<summary> Applies the given function to each node of the AST and its context (path) down to a given position, returning <c>Some x</c> for the last (deepest) node for which the function returns <c>Some x</c> for some value <c>x</c>, otherwise <c>None</c>. Traversal is short-circuited if no matching node is found through the given position. </summary>
<param name="chooser">The function to apply to each node and its context to derive an optional value.</param>
<param name="position">The position in the input file down to which to apply the function.</param>
<param name="parsedInput">The AST to search.</param>
<returns>The last (deepest) value for which the function returns <c>Some</c>, or <c>None</c> if no matching node is found.</returns>
<example><code lang="fsharp"> let range = (pos, parsedInput) ||&gt; ParsedInput.tryPickLast (fun path node -&gt; match node, path with | FuncIdent range -&gt; Some range | _ -&gt; None) </code></example>
val nextToInnermostNestedModule: string list option
val visitor: SyntaxVisitorBase<string>
Multiple items
type SyntaxVisitorBase<'T> = new: unit -> SyntaxVisitorBase<'T> abstract VisitAttributeApplication: path: SyntaxVisitorPath * attributes: SynAttributeList -> 'T option + 1 overload abstract VisitBinding: path: SyntaxVisitorPath * defaultTraverse: (SynBinding -> 'T option) * synBinding: SynBinding -> 'T option + 1 overload abstract VisitComponentInfo: path: SyntaxVisitorPath * synComponentInfo: SynComponentInfo -> 'T option + 1 overload abstract VisitEnumDefn: path: SyntaxVisitorPath * cases: SynEnumCase list * range -> 'T option + 1 overload abstract VisitExpr: path: SyntaxVisitorPath * traverseSynExpr: (SynExpr -> 'T option) * defaultTraverse: (SynExpr -> 'T option) * synExpr: SynExpr -> 'T option + 1 overload abstract VisitHashDirective: path: SyntaxVisitorPath * hashDirective: ParsedHashDirective * range: range -> 'T option + 1 overload abstract VisitImplicitInherit: path: SyntaxVisitorPath * defaultTraverse: (SynExpr -> 'T option) * inheritedType: SynType * synArgs: SynExpr * range: range -> 'T option + 1 overload abstract VisitInheritSynMemberDefn: path: SyntaxVisitorPath * componentInfo: SynComponentInfo * typeDefnKind: SynTypeDefnKind * synType: SynType * members: SynMemberDefns * range: range -> 'T option + 1 overload abstract VisitInterfaceSynMemberDefnType: path: SyntaxVisitorPath * synType: SynType -> 'T option + 1 overload ...

--------------------
new: unit -> SyntaxVisitorBase<'T>
val this: SyntaxVisitorBase<string>
override SyntaxVisitorBase.VisitPat: path: SyntaxVisitorPath * defaultTraverse: (SynPat -> 'T option) * synPat: SynPat -> 'T option
val defaultTraverse: (SynPat -> string option)
val synPat: SynPat
val result: string option
module SyntaxTraversal from FSharp.Compiler.Syntax
val Traverse: pos: pos * parseTree: ParsedInput * visitor: SyntaxVisitorBase<'T> -> 'T option
val secondCodeSample: string
val secondVisitor: SyntaxVisitorBase<SynExpr>
val this: SyntaxVisitorBase<SynExpr>
override SyntaxVisitorBase.VisitBinding: path: SyntaxVisitorPath * defaultTraverse: (SynBinding -> 'T option) * synBinding: SynBinding -> 'T option
val defaultTraverse: (SynBinding -> SynExpr option)
val binding: SynBinding
val cursorPos: pos
val secondResult: SynExpr option
val thirdCodeSample: string
val thirdCursorPos: pos
val thirdVisitor: SyntaxVisitorBase<int>
val this: SyntaxVisitorBase<int>
override SyntaxVisitorBase.VisitExpr: path: SyntaxVisitorPath * traverseSynExpr: (SynExpr -> 'T option) * defaultTraverse: (SynExpr -> 'T option) * synExpr: SynExpr -> 'T option
val traverseSynExpr: (SynExpr -> int option)
val defaultTraverse: (SynExpr -> int option)
val synExpr: SynExpr
union case SynExpr.Const: constant: SynConst * range: range -> SynExpr
<summary> F# syntax: 1, 1.3, () etc. </summary>
type SynConst = | Unit | Bool of bool | SByte of sbyte | Byte of byte | Int16 of int16 | UInt16 of uint16 | Int32 of int32 | UInt32 of uint32 | Int64 of int64 | UInt64 of uint64 ... member Range: dflt: range -> range
<summary> The unchecked abstract syntax tree of constants in F# types and expressions. </summary>
union case SynConst.Int32: int32 -> SynConst
<summary> F# syntax: 13, 0x4000, 0o0777 </summary>
val v: int32
val otherExpr: SynExpr
val thirdResult: int option

Type something to start searching.