//----------------------------------------------------------------------------
//
// Copyright (c) 2002-2012 Microsoft Corporation. 
//
// This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
// copy of the license can be found in the License.html file at the root of this distribution. 
// By using this source code in any fashion, you are agreeing to be bound 
// by the terms of the Apache License, Version 2.0.
//
// You must not remove this notice, or any other, from this software.
//----------------------------------------------------------------------------

module internal Microsoft.FSharp.Compiler.Ast

open System.Collections.Generic
open Internal.Utilities
open Internal.Utilities.Text.Lexing
open Internal.Utilities.Text.Parsing
open Microsoft.FSharp.Compiler.AbstractIL 
open Microsoft.FSharp.Compiler.AbstractIL.IL 
open Microsoft.FSharp.Compiler.AbstractIL.Internal 
open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library 
open Microsoft.FSharp.Compiler 
open Microsoft.FSharp.Compiler.UnicodeLexing 
open Microsoft.FSharp.Compiler.ErrorLogger
open Microsoft.FSharp.Compiler.PrettyNaming
open Microsoft.FSharp.Compiler.AbstractIL.Diagnostics
open Microsoft.FSharp.Compiler.Lib
open Microsoft.FSharp.Compiler.Range

/// The prefix of the names used for the fake namespace path added to all dynamic code entries in FSI.EXE
let FsiDynamicModulePrefix = "FSI_"

[<RequireQualifiedAccess>]
module FSharpLib = 
    let Root      = "Microsoft.FSharp"
    let RootPath  = IL.splitNamespace Root 
    let Core      = Root + ".Core"
    let CorePath  = IL.splitNamespace Core


[<RequireQualifiedAccess>]
module CustomOperations =
    [<Literal>] 
    let Into = "into"

//------------------------------------------------------------------------
// XML doc pre-processing
//-----------------------------------------------------------------------


/// Used to collect XML documentation during lexing and parsing.
type XmlDocCollector() =
    let mutable savedLines = new ResizeArray<(string * pos)>()
    let mutable savedGrabPoints = new ResizeArray<pos>()
    let posCompare p1 p2 = if posGeq p1 p2 then 1 else if posEq p1 p2 then 0 else -1
    let savedGrabPointsAsArray = 
        lazy (savedGrabPoints.ToArray() |> Array.sortWith posCompare)

    let savedLinesAsArray = 
        lazy (savedLines.ToArray() |> Array.sortWith (fun (_,p1) (_,p2) -> posCompare p1 p2))

    let check() = 
        assert (not savedLinesAsArray.IsValueCreated && "can't add more XmlDoc elements to XmlDocCOllector after extracting first XmlDoc from the overall results" <> "")

    member x.AddGrabPoint(pos) = 
        check()
        savedGrabPoints.Add pos 

    member x.AddXmlDocLine(line,pos) = 
        check()
        savedLines.Add(line,pos)

    member x.LinesBefore(grabPointPos) = 
      try
        let lines = savedLinesAsArray.Force()
        let grabPoints = savedGrabPointsAsArray.Force()
        let firstLineIndexAfterGrabPoint = Array.findFirstIndexWhereTrue lines (fun (_,pos) -> posGeq pos grabPointPos) 
        let grabPointIndex = Array.findFirstIndexWhereTrue grabPoints (fun pos -> posGeq pos grabPointPos) 
        assert (posEq grabPoints.[grabPointIndex] grabPointPos)
        let firstLineIndexAfterPrevGrabPoint = 
            if grabPointIndex = 0 then 
                0 
            else
                let prevGrabPointPos = grabPoints.[grabPointIndex-1]
                Array.findFirstIndexWhereTrue lines (fun (_,pos) -> posGeq pos prevGrabPointPos) 
        //printfn "#lines = %d, firstLineIndexAfterPrevGrabPoint = %d, firstLineIndexAfterGrabPoint = %d" lines.Length firstLineIndexAfterPrevGrabPoint  firstLineIndexAfterGrabPoint
        lines.[firstLineIndexAfterPrevGrabPoint..firstLineIndexAfterGrabPoint-1] |> Array.map fst
      with e -> 
          //printfn "unexpected error in LinesBefore:\n%s" (e.ToString())
          [| |]

    
type XmlDoc = 
    | XmlDoc of string[]
    static member Empty = XmlDocStatics.Empty
    static member Merge (XmlDoc lines) (XmlDoc lines') = XmlDoc (Array.append lines lines')
    static member Process (XmlDoc lines) = 
        // This code runs for .XML generation and thus influences cross-project xmldoc tooltips; for within-project tooltips, see XmlDocumentation.fs in the language service
        let rec processLines (lines:string list) =
            match lines with 
            | [] -> []
            | (lineA::rest) as lines ->
                let lineAT = lineA.TrimStart([|' '|])
                if lineAT = "" then processLines rest
                else if String.hasPrefix lineAT "<" then lines
                else ["<summary>"] @
#if SILVERLIGHT
                     lines @
#else        
                     (lines |> List.map (fun line -> System.Security.SecurityElement.Escape(line))) @
#endif
                     ["</summary>"]               

        let lines = processLines (Array.toList lines)
        if lines.Length = 0 then XmlDoc.Empty 
        else XmlDoc (Array.ofList lines)

// Discriminated unions can't contain statics, so we use a separate type
and XmlDocStatics() = 
    static let empty = XmlDoc[| |]
    static member Empty = empty

type PreXmlDoc = 
    | PreXmlMerge of PreXmlDoc * PreXmlDoc
    | PreXmlDoc of pos * XmlDocCollector
    | PreXmlDocEmpty 

    member x.ToXmlDoc() = 
        match x with 
        | PreXmlMerge(a,b) -> XmlDoc.Merge (a.ToXmlDoc()) (b.ToXmlDoc())
        | PreXmlDocEmpty -> XmlDoc.Empty
        | PreXmlDoc (pos,collector) -> 
            let lines = collector.LinesBefore pos
            if lines.Length = 0 then XmlDoc.Empty
            else XmlDoc lines

    static member CreateFromGrabPoint(collector:XmlDocCollector,grabPointPos) = 
        collector.AddGrabPoint grabPointPos
        PreXmlDoc(grabPointPos,collector)

    static member Empty = PreXmlDocEmpty 
    static member Merge a b = PreXmlMerge (a,b)

type ParserDetail =
    | Ok
    | ThereWereSignificantParseErrorsSoDoNotTypecheckThisNode  // would cause spurious/misleading diagnostics

//------------------------------------------------------------------------
//  AST: identifiers and long identifiers
//-----------------------------------------------------------------------


// PERFORMANCE: consider making this a struct.
[<System.Diagnostics.DebuggerDisplay("{idText}")>]
[<Sealed>]
[<NoEquality; NoComparison>]
type Ident (text,range) = 
     member x.idText = text
     member x.idRange = range
     override x.ToString() = text

type LongIdent = Ident list
type LongIdentWithDots =
    /// LongIdentWithDots(lid, dotms)   
    /// Typically dotms.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 dotms.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)
    | LongIdentWithDots of LongIdent * range list
    with member this.Range =
            match this with
            | LongIdentWithDots([],_) -> failwith "rangeOfLidwd"
            | LongIdentWithDots([id],[]) -> id.idRange
            | LongIdentWithDots([id],[m]) -> unionRanges id.idRange m
            | LongIdentWithDots(h::t,[]) -> unionRanges h.idRange (List.last t).idRange
            | LongIdentWithDots(h::t,dotms) -> unionRanges h.idRange (List.last t).idRange |> unionRanges (List.last dotms)
         member this.Lid = match this with LongIdentWithDots(lid,_) -> lid
         member this.ThereIsAnExtraDotAtTheEnd = match this with LongIdentWithDots(lid,dots) -> lid.Length = dots.Length
         member this.RangeSansAnyExtraDot =
            match this with
            | LongIdentWithDots([],_) -> failwith "rangeOfLidwd"
            | LongIdentWithDots([id],_) -> id.idRange
            | LongIdentWithDots(h::t,dotms) -> 
                let nonExtraDots = if dotms.Length = t.Length then dotms else List.take t.Length dotms
                unionRanges h.idRange (List.last t).idRange |> unionRanges (List.last nonExtraDots)

//------------------------------------------------------------------------
//  AST: the grammar of implicitly scoped type parameters 
//-----------------------------------------------------------------------

type TyparStaticReq = 
    | NoStaticReq 
    | HeadTypeStaticReq 

[<NoEquality; NoComparison>]
type SynTypar = 
    | Typar of Ident * TyparStaticReq * (* isCompGen: *) bool 
    with member this.Range =
            match this with
            | Typar(id,_,_) ->
                id.idRange

//------------------------------------------------------------------------
//  AST: the grammar of constants and measures
//-----------------------------------------------------------------------

type 
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    /// The unchecked abstract syntax tree of constants in F# types and expressions.
    SynConst = 
    /// F# syntax: ()
    | Unit
    /// F# syntax: true, false
    | Bool of bool
    /// F# syntax: 13y, 0xFFy, 0o077y, 0b0111101y
    | SByte of sbyte
    /// F# syntax: 13uy, 0x40uy, 0oFFuy, 0b0111101uy
    | Byte of byte
    /// F# syntax: 13s, 0x4000s, 0o0777s, 0b0111101s
    | Int16 of int16
    /// F# syntax: 13us, 0x4000us, 0o0777us, 0b0111101us
    | UInt16 of uint16
    /// F# syntax: 13, 0x4000, 0o0777
    | Int32 of int32
    /// F# syntax: 13u, 0x4000u, 0o0777u
    | UInt32 of uint32
    /// F# syntax: 13L
    | Int64 of int64
    /// F# syntax: 13UL
    | UInt64 of uint64
    /// F# syntax: 13n
    | IntPtr of int64
    /// F# syntax: 13un
    | UIntPtr of uint64
    /// F# syntax: 1.30f, 1.40e10f etc.
    | Single of single
    /// F# syntax: 1.30, 1.40e10 etc.
    | Double of double
    /// F# syntax: 'a'
    | Char of char
    /// F# syntax: 23.4M
    | Decimal of System.Decimal
    /// UserNum(value, suffix)
    ///
    /// F# syntax: 1Q, 1Z, 1R, 1N, 1G
    | UserNum of ( string * string)
    /// F# syntax: verbatim or regular string, e.g. "abc"
    | String of string * range 
    /// F# syntax: verbatim or regular byte string, e.g. "abc"B.
    ///
    /// Also used internally in the typechecker once an array of unit16 contants 
    /// is detected, to allow more efficient processing of large arrays of uint16 constants. 
    | Bytes of byte[] * range 
    /// Used internally in the typechecker once an array of unit16 contants 
    /// is detected, to allow more efficient processing of large arrays of uint16 constants. 
    | UInt16s of uint16[] 
    /// Old comment: "we never iterate, so the const here is not another SynConst.Measure"
    | Measure of SynConst * SynMeasure 
    member c.Range dflt = 
        match c with 
        | SynConst.String (_,m0) | SynConst.Bytes (_,m0) -> m0 
        | _ -> dflt
      
and  
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    /// The unchecked abstract syntax tree of F# unit of measure annotaitons. 
    /// This should probably be merged with the represenation of SynType.
    SynMeasure = 
    | Named of LongIdent * range
    | Product of SynMeasure * SynMeasure * range
    | Seq of SynMeasure list * range
    | Divide of SynMeasure * SynMeasure * range
    | Power of SynMeasure * int * range
    | One 
    | Anon of range
    | Var of SynTypar * range

//------------------------------------------------------------------------
//  AST: the grammar of types, expressions, declarations etc.
//-----------------------------------------------------------------------

[<RequireQualifiedAccess>]
type SynAccess = 
    | Public
    | Internal
    | Private


type SequencePointInfoForTarget = 
    | SequencePointAtTarget
    | SuppressSequencePointAtTarget

type SequencePointInfoForSeq = 
    | SequencePointsAtSeq
    // This means "suppress a in 'a;b'" and "suppress b in 'a before b'"
    | SuppressSequencePointOnExprOfSequential
    // This means "suppress b in 'a;b'" and "suppress a in 'a before b'"
    | SuppressSequencePointOnStmtOfSequential

type SequencePointInfoForTry = 
    | SequencePointAtTry of range
    // Used for "use" and "for"
    | SequencePointInBodyOfTry 
    | NoSequencePointAtTry

type SequencePointInfoForWith = 
    | SequencePointAtWith of range
    | NoSequencePointAtWith

type SequencePointInfoForFinally = 
    | SequencePointAtFinally of range
    | NoSequencePointAtFinally

type SequencePointInfoForForLoop = 
    | SequencePointAtForLoop of range
    | NoSequencePointAtForLoop
    
type SequencePointInfoForWhileLoop = 
    | SequencePointAtWhileLoop of range
    | NoSequencePointAtWhileLoop
    
type SequencePointInfoForBinding = 
    | SequencePointAtBinding of range
    // Indicates the ommission of a sequence point for a binding for a 'do expr' 
    | NoSequencePointAtDoBinding
    // Indicates the ommission of a sequence point for a binding for a 'let e = expr' where 'expr' has immediate control flow
    | NoSequencePointAtLetBinding
    // Indicates the ommission of a sequence point for a compiler generated binding
    // where we've done a local expansion of some construct into something that involves
    // a 'let'. e.g. we've inlined a function and bound its arguments using 'let'
    // The let bindings are 'sticky' in that the inversion of the inlining would involve
    // replacing the entire expression with the original and not just the let bindings alone.
    | NoSequencePointAtStickyBinding
    // Given 'let v = e1 in e2', where this is a compiler generated binding, 
    // we are sometimes forced to generate a sequence point for the expression anyway based on its
    // overall range. If the let binding is given the flag below then it is asserting that
    // the binding has no interesting side effects and can be totally ignored and the range
    // of the inner expression is used instead
    | NoSequencePointAtInvisibleBinding
    
    // Don't drop sequence points when combining sequence points
    member x.Combine(y:SequencePointInfoForBinding) = 
        match x,y with 
        | SequencePointAtBinding _ as g, _  -> g
        | _, (SequencePointAtBinding _ as g)  -> g
        | _ -> x

/// Indicates if a for loop is 'for x in e1 -> e2', only valid in sequence expressions
type SeqExprOnly = 
    | SeqExprOnly of bool

/// denotes location of the separator block + optional position of the semicolon (used for tooling support)
type BlockSeparator = range * pos option
/// stores pair: record field name + (true if given record field name is syntactically correct and can be used in name resolution)
type RecordFieldName = LongIdentWithDots * bool

type ExprAtomicFlag =
    /// Says that the expression is an atomic expression, i.e. is of a form that has no whitespace unless 
    /// enclosed in parantheses, e.g. 1, "3", ident, ident.[expr] and (expr). If an atomic expression has
    /// type T, then the largest expression ending at the same range as the atomic expression also has type T.
    | Atomic = 0
    | NonAtomic = 1

/// The kind associated with a binding - "let", "do" or a standalone expression
type SynBindingKind = 
    /// A standalone expression in a module
    | StandaloneExpression
    /// A normal 'let' binding in a module
    | NormalBinding
    /// A 'do' binding in a module. Must have type 'unit'
    | DoBinding
  
type
    [<NoEquality; NoComparison>]
    /// Represents the explicit declaration of a type parameter
    SynTyparDecl = 
    | TyparDecl of SynAttributes * SynTypar


and 
    [<NoEquality; NoComparison>]
    /// The unchecked abstract syntax tree of F# type constraints
    SynTypeConstraint =
    /// F# syntax : is 'typar : struct
    | WhereTyparIsValueType of SynTypar * range
    /// F# syntax : is 'typar : not struct
    | WhereTyparIsReferenceType of SynTypar * range
    /// F# syntax is 'typar : unmanaged
    | WhereTyparIsUnmanaged of SynTypar * range
    /// F# syntax is 'typar : null
    | WhereTyparSupportsNull of SynTypar * range
    /// F# syntax is 'typar : comparison 
    | WhereTyparIsComparable of SynTypar * range
    /// F# syntax is 'typar : equality
    | WhereTyparIsEquatable of SynTypar * range
    /// F# syntax is default ^T : type
    | WhereTyparDefaultsToType of SynTypar * SynType * range
    /// F# syntax is 'typar :> type
    | WhereTyparSubtypeOfType of SynTypar *  SynType * range
    /// F# syntax is ^T : (static member MemberName : ^T * int -> ^T) 
    | WhereTyparSupportsMember of SynTypar list * SynMemberSig * range
    /// F# syntax is 'typar : enum<'UnderlyingType>
    | WhereTyparIsEnum of SynTypar * SynType list * range
    /// F# syntax is 'typar : delegate<'Args,unit>
    | WhereTyparIsDelegate of SynTypar * SynType list * range

and 
    [<NoEquality; NoComparison;RequireQualifiedAccess>]
    /// The unchecked abstract syntax tree of F# types 
    SynType =
    /// F# syntax : A.B.C
    | LongIdent of LongIdentWithDots
    /// App(typeName, LESSm, typeArgs, commasm, GREATERm, isPostfix, m)
    ///
    /// F# syntax : type<type, ..., type> or type type or (type,...,type) type
    ///   isPostfix: indicates a postfix type application e.g. "int list" or "(int,string) dict"
    ///   commasm: ranges for interstitial commas, these only matter for parsing/design-time tooling, the typechecker may munge/discard them
    | App of SynType * range option * SynType list * range list * range option * bool * range
    /// LongIdentApp(typeName, longId, LESSm, tyArgs, commasm, GREATERm, wholem)
    ///
    /// F# syntax : type.A.B.C<type, ..., type>
    ///   commasm: ranges for interstitial commas, these only matter for parsing/design-time tooling, the typechecker may munge/discard them
    | LongIdentApp of SynType * LongIdentWithDots * range option * SynType list * range list * range option * range
    /// F# syntax : type * ... * type
    // the bool is true if / rather than * follows the type
    | Tuple of (bool*SynType) list * range    
    /// F# syntax : type[]
    | Array of  int * SynType * range
    /// F# syntax : type -> type
    | Fun of  SynType * SynType * range
    /// F# syntax : 'Var
    | Var of SynTypar * range
    /// F# syntax : _
    | Anon of range
    /// F# syntax : typ with constraints
    | WithGlobalConstraints of SynType * SynTypeConstraint list * range
    /// F# syntax : #type
    | HashConstraint of SynType * range
    /// F# syntax : for units of measure e.g. m / s 
    | MeasureDivide of SynType * SynType * range       
    /// F# syntax : for units of measure e.g. m^3 
    | MeasurePower of SynType * int * range      
    /// F# syntax : 1, "abc" etc, used in parameters to type providers
    /// For the dimensionless units i.e. 1 , and static parameters to provided types
    | StaticConstant of SynConst * range          
    /// F# syntax : const expr, used in static parameters to type providers
    | StaticConstantExpr of SynExpr * range
    /// F# syntax : ident=1 etc., used in static parameters to type providers
    | StaticConstantNamed of SynType * SynType * range
    /// Get the syntactic range of source code covered by this construct.
    member x.Range = 
        match x with 
        | SynType.LongIdent(lidwd) -> lidwd.Range
        | SynType.App(_,_,_,_,_,_,m) | SynType.LongIdentApp(_,_,_,_,_,_,m) | SynType.Tuple(_,m) | SynType.Array(_,_,m) | SynType.Fun(_,_,m)
        | SynType.Var(_,m) | SynType.Anon m | SynType.WithGlobalConstraints(_,_,m)
        | SynType.StaticConstant(_,m) | SynType.StaticConstantExpr(_,m) | SynType.StaticConstantNamed(_,_,m)
        | SynType.HashConstraint(_,m) | SynType.MeasureDivide(_,_,m) | SynType.MeasurePower(_,_,m) -> m

    
and  
    [<NoEquality; NoComparison;RequireQualifiedAccess>]
    SynExpr =

    /// F# syntax: (expr)
    ///
    /// Paren(expr, leftParenRange, rightParenRange, wholeRangeIncludingParentheses)
    ///
    /// Parenthesized expressions. Kept in AST to distinguish A.M((x,y)) 
    /// from A.M(x,y), among other things.
    | Paren of SynExpr * range * range option * range  

    /// F# syntax: <@ expr @>, <@@ expr @@>
    /// 
    /// Quote(operator,isRaw,quotedSynExpr,isFromQueryExpression,m)
    | Quote of SynExpr * bool * SynExpr * bool * range 

    /// F# syntax: 1, 1.3, () etc.
    | Const of SynConst * range

    /// F# syntax: expr : type
    | Typed of  SynExpr * SynType * range

    /// F# syntax: e1, ..., eN
    | Tuple of  SynExpr list * range list * range  // "range list" is for interstitial commas, these only matter for parsing/design-time tooling, the typechecker may munge/discard them

    /// F# syntax: [ e1; ...; en ], [| e1; ...; en |]
    | ArrayOrList of  bool * SynExpr list * range 

    /// F# syntax: { f1=e1; ...; fn=en }
    /// SynExpr.Record((baseType, baseCtorArgs, mBaseCtor, sepAfterBase, mInherits), (copyExpr, sepAfterCopyExpr), (recordFieldName, fieldValue, sepAfterField), mWholeExpr)
    /// 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)
    | Record of (SynType * SynExpr * range * BlockSeparator option * range) option * (SynExpr * BlockSeparator) option * (RecordFieldName * (SynExpr option) * BlockSeparator option) list * range

    /// F# syntax: new C(...)
    /// The flag is true if known to be 'family' ('protected') scope 
    | New of bool * SynType * SynExpr * range 

    /// SynExpr.ObjExpr(objTy,argOpt,binds,extraImpls,mNewExpr,mWholeExpr)
    ///
    /// F# syntax: { new ... with ... }
    | ObjExpr of SynType * (SynExpr * Ident option) option * SynBinding list * SynInterfaceImpl list * range * range

    /// F# syntax: 'while ... do ...'
    | While of SequencePointInfoForWhileLoop * SynExpr * SynExpr * range

    /// F# syntax: 'for i = ... to ... do ...'
    | For of SequencePointInfoForForLoop * Ident * SynExpr * bool * SynExpr * SynExpr * range

    /// SynExpr.ForEach (spBind, seqExprOnly, isFromSource, pat, enumExpr, bodyExpr, mWholeExpr).
    ///
    /// F# syntax: 'for ... in ... do ...'
    | ForEach of SequencePointInfoForForLoop * SeqExprOnly * bool * SynPat * SynExpr * SynExpr * range

    /// F# syntax: [ expr ], [| expr |]
    | ArrayOrListOfSeqExpr of bool * SynExpr * range

    /// CompExpr(isArrayOrList, isNotNakedRefCell, expr)
    ///
    /// F# syntax: { expr }
    | CompExpr of bool * bool ref * SynExpr * range

    /// First bool indicates if lambda originates from a method. Patterns here are always "simple" 
    /// Second bool indicates if this is a "later" part of an iterated sequence of lambdas
    ///
    /// F# syntax: fun pat -> expr
    | Lambda of  bool * bool * SynSimplePats * SynExpr * range 

    /// F# syntax: function pat1 -> expr | ... | patN -> exprN
    | MatchLambda of bool * range * SynMatchClause list * SequencePointInfoForBinding * range

    /// F# syntax: match expr with pat1 -> expr | ... | patN -> exprN
    | Match of  SequencePointInfoForBinding * SynExpr * SynMatchClause list * bool * range (* bool indicates if this is an exception match in a computation expression which throws unmatched exceptions *)

    /// F# syntax: do expr 
    | Do of  SynExpr * range

    /// F# syntax: assert expr 
    | Assert of SynExpr * range

    /// App(exprAtomicFlag, isInfix, funcExpr, argExpr, m)
    ///  - exprAtomicFlag: indicates if the applciation is syntactically atomic, e.g. f.[1] is atomic, but 'f x' is not
    ///  - isInfix is true for the first app of an infix operator, e.g. 1+2 becomes App(App(+,1),2), where the inner node is marked isInfix 
    ///      (or more generally, for higher operator fixities, if App(x,y) is such that y comes before x in the source code, then the node is marked isInfix=true)
    ///
    /// F# syntax: f x
    | App of ExprAtomicFlag * bool * SynExpr * SynExpr * range

    /// TypeApp(expr, mLessThan, types, mCommas, mGreaterThan, mTypeArgs, mWholeExpr) 
    ///     "mCommas" are the ranges for interstitial commas, these only matter for parsing/design-time tooling, the typechecker may munge/discard them
    ///
    /// F# syntax: expr<type1,...,typeN>
    | TypeApp of SynExpr * range * SynType list * range list * range option * range * range

    /// LetOrUse(isRecursive, isUse, bindings, body, wholeRange)
    ///
    /// F# syntax: let pat = expr in expr 
    /// F# syntax: let f pat1 .. patN = expr in expr 
    /// F# syntax: let rec f pat1 .. patN = expr in expr 
    /// F# syntax: use pat = expr in expr 
    | LetOrUse of bool * bool * SynBinding list * SynExpr * range

    /// F# syntax: try expr with pat -> expr
    | TryWith of SynExpr * range * SynMatchClause list * range * range * SequencePointInfoForTry * SequencePointInfoForWith

    /// F# syntax: try expr finally expr
    | TryFinally of SynExpr * SynExpr * range * SequencePointInfoForTry * SequencePointInfoForFinally

    /// F# syntax: lazy expr
    | Lazy of SynExpr * range

    /// Seq(seqPoint, isTrueSeq, e1, e2, m)
    ///  isTrueSeq: false indicates "let v = a in b; v" 
    ///
    /// F# syntax: expr; expr
    | Sequential of SequencePointInfoForSeq * bool * SynExpr * SynExpr * range 

    ///  IfThenElse(exprGuard,exprThen,optionalExprElse,spIfToThen,isFromErrorRecovery,mIfToThen,mIfToEndOfLastBranch)
    ///
    /// F# syntax: if expr then expr
    /// F# syntax: if expr then expr else expr
    | IfThenElse of SynExpr * SynExpr * SynExpr option * SequencePointInfoForBinding * bool * range * range

    /// F# syntax: ident
    /// Optimized representation, = SynExpr.LongIdent(false,[id],id.idRange) 
    | Ident of Ident 

    /// F# syntax: ident.ident...ident
    /// LongIdent(isOptional, longIdent, altNameRefCell, m)
    ///   isOptional: true if preceded by a '?' for an optional named parameter 
    ///   altNameRefCell: Normally 'None' except for some compiler-generated variables in desugaring pattern matching. See SynSimplePat.Id
    | LongIdent of bool * LongIdentWithDots * SynSimplePatAlternativeIdInfo ref option * range  

    /// F# syntax: ident.ident...ident <- expr
    | LongIdentSet of LongIdentWithDots * SynExpr * range

    /// DotGet(expr, rangeOfDot, lid, wholeRange)
    ///
    /// F# syntax: expr.ident.ident
    | DotGet of SynExpr * range * LongIdentWithDots * range

    /// F# syntax: expr.ident...ident <- expr
    | DotSet of SynExpr * LongIdentWithDots * SynExpr * range

    /// F# syntax: expr.[expr,...,expr] 
    | DotIndexedGet of SynExpr * SynExpr list * range * range

    /// DotIndexedSet (objectExpr, indexExprs, valueExpr, rangeOfLeftOfSet, rangeOfDot, rangeOfWholeExpr)
    ///
    /// F# syntax: expr.[expr,...,expr] <- expr
    | DotIndexedSet of SynExpr * SynExpr list * SynExpr * range * range * range

    /// F# syntax: Type.Items(e1) <- e2 , rarely used named-property-setter notation, e.g. Foo.Bar.Chars(3) <- 'a'
    | NamedIndexedPropertySet of LongIdentWithDots * SynExpr * SynExpr * range

    /// F# syntax: expr.Items(e1) <- e2 , rarely used named-property-setter notation, e.g. (stringExpr).Chars(3) <- 'a'
    | DotNamedIndexedPropertySet of SynExpr * LongIdentWithDots * SynExpr * SynExpr * range

    /// F# syntax: expr :? type
    | TypeTest of  SynExpr * SynType * range

    /// F# syntax: expr :> type 
    | Upcast of  SynExpr * SynType * range

    /// F# syntax: expr :?> type 
    | Downcast of  SynExpr * SynType * range

    /// F# syntax: upcast expr
    | InferredUpcast of  SynExpr * range

    /// F# syntax: downcast expr
    | InferredDowncast of  SynExpr * range

    /// F# syntax: null
    | Null of range

    /// F# syntax: &expr, &&expr
    | AddressOf of  bool * SynExpr * range * range

    /// F# syntax: ((typar1 or ... or typarN): (member-dig) expr)
    | TraitCall of SynTypar list * SynMemberSig * SynExpr * range

    /// F# syntax: ... in ... 
    /// Computation expressions only, based on JOIN_IN token from lex filter
    | JoinIn of SynExpr * range * SynExpr * range 

    /// F# syntax: <implicit>
    /// Computation expressions only, implied by final "do" or "do!"
    | ImplicitZero of range 

    /// F# syntax: yield expr 
    /// F# syntax: return expr 
    /// Computation expressions only
    | YieldOrReturn   of (bool * bool) * SynExpr * range

    /// F# syntax: yield! expr 
    /// F# syntax: return! expr 
    /// Computation expressions only
    | YieldOrReturnFrom  of (bool * bool) * SynExpr * range

    /// SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhsExpr, bodyExpr, mWholeExpr).
    ///
    /// F# syntax: let! pat = expr in expr
    /// F# syntax: use! pat = expr in expr
    /// Computation expressions only
    | LetOrUseBang    of SequencePointInfoForBinding * bool * bool * SynPat * SynExpr * SynExpr * range

    /// F# syntax: do! expr 
    /// Computation expressions only
    | DoBang      of SynExpr * range

    /// Only used in FSharp.Core
    | LibraryOnlyILAssembly of ILInstr array *  SynType list * SynExpr list * SynType list * range (* Embedded IL assembly code *)

    /// Only used in FSharp.Core
    | LibraryOnlyStaticOptimization of SynStaticOptimizationConstraint list * SynExpr * SynExpr * range

    /// Only used in FSharp.Core
    | LibraryOnlyUnionCaseFieldGet of SynExpr * LongIdent * int * range

    /// Only used in FSharp.Core
    | LibraryOnlyUnionCaseFieldSet of SynExpr * LongIdent * int * SynExpr * range
  
    /// Inserted for error recovery
    | ArbitraryAfterError  of (*debugStr:*) string * range  

    /// Inserted for error recovery
    | FromParseError  of SynExpr * range  

    /// Inserted for error recovery when there is "expr." and missing tokens or error recovery after the dot
    | DiscardAfterMissingQualificationAfterDot  of SynExpr * range  
    /// Get the syntactic range of source code covered by this construct.
    member e.Range = 
        match e with 
        | SynExpr.Paren(_,_,_,m) 
        | SynExpr.Quote(_,_,_,_,m) 
        | SynExpr.Const(_,m) 
        | SynExpr.Typed (_,_,m)
        | SynExpr.Tuple (_,_,m)
        | SynExpr.ArrayOrList (_,_,m)
        | SynExpr.Record (_,_,_,m)
        | SynExpr.New (_,_,_,m)
        | SynExpr.ObjExpr (_,_,_,_,_,m)
        | SynExpr.While (_,_,_,m)
        | SynExpr.For (_,_,_,_,_,_,m)
        | SynExpr.ForEach (_,_,_,_,_,_,m)
        | SynExpr.CompExpr (_,_,_,m)
        | SynExpr.ArrayOrListOfSeqExpr (_,_,m)
        | SynExpr.Lambda (_,_,_,_,m)
        | SynExpr.Match (_,_,_,_,m)
        | SynExpr.MatchLambda (_,_,_,_,m)
        | SynExpr.Do (_,m)
        | SynExpr.Assert (_,m)
        | SynExpr.App (_,_,_,_,m)
        | SynExpr.TypeApp (_,_,_,_,_,_,m)
        | SynExpr.LetOrUse (_,_,_,_,m)
        | SynExpr.TryWith (_,_,_,_,m,_,_)
        | SynExpr.TryFinally (_,_,m,_,_)
        | SynExpr.Sequential (_,_,_,_,m)
        | SynExpr.ArbitraryAfterError(_,m)
        | SynExpr.FromParseError (_,m) 
        | SynExpr.DiscardAfterMissingQualificationAfterDot (_,m) 
        | SynExpr.IfThenElse (_,_,_,_,_,_,m)
        | SynExpr.LongIdent (_,_,_,m)
        | SynExpr.LongIdentSet (_,_,m)
        | SynExpr.NamedIndexedPropertySet (_,_,_,m)
        | SynExpr.DotIndexedGet (_,_,_,m)
        | SynExpr.DotIndexedSet (_,_,_,_,_,m)
        | SynExpr.DotGet (_,_,_,m)
        | SynExpr.DotSet (_,_,_,m)
        | SynExpr.DotNamedIndexedPropertySet (_,_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldGet (_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldSet (_,_,_,_,m)
        | SynExpr.LibraryOnlyILAssembly (_,_,_,_,m)
        | SynExpr.LibraryOnlyStaticOptimization (_,_,_,m)
        | SynExpr.TypeTest (_,_,m)
        | SynExpr.Upcast (_,_,m)
        | SynExpr.AddressOf (_,_,_,m)
        | SynExpr.Downcast (_,_,m)
        | SynExpr.JoinIn (_,_,_,m)
        | SynExpr.InferredUpcast (_,m)
        | SynExpr.InferredDowncast (_,m)
        | SynExpr.Null m
        | SynExpr.Lazy (_, m)
        | SynExpr.TraitCall(_,_,_,m)
        | SynExpr.ImplicitZero (m)
        | SynExpr.YieldOrReturn (_,_,m)
        | SynExpr.YieldOrReturnFrom (_,_,m)
        | SynExpr.LetOrUseBang  (_,_,_,_,_,_,m)
        | SynExpr.DoBang  (_,m) -> m
        | SynExpr.Ident id -> id.idRange
    /// range ignoring any (parse error) extra trailing dots
    member e.RangeSansAnyExtraDot = 
        match e with 
        | SynExpr.Paren(_,_,_,m) 
        | SynExpr.Quote(_,_,_,_,m) 
        | SynExpr.Const(_,m) 
        | SynExpr.Typed (_,_,m)
        | SynExpr.Tuple (_,_,m)
        | SynExpr.ArrayOrList (_,_,m)
        | SynExpr.Record (_,_,_,m)
        | SynExpr.New (_,_,_,m)
        | SynExpr.ObjExpr (_,_,_,_,_,m)
        | SynExpr.While (_,_,_,m)
        | SynExpr.For (_,_,_,_,_,_,m)
        | SynExpr.ForEach (_,_,_,_,_,_,m)
        | SynExpr.CompExpr (_,_,_,m)
        | SynExpr.ArrayOrListOfSeqExpr (_,_,m)
        | SynExpr.Lambda (_,_,_,_,m)
        | SynExpr.Match (_,_,_,_,m)
        | SynExpr.MatchLambda (_,_,_,_,m)
        | SynExpr.Do (_,m)
        | SynExpr.Assert (_,m)
        | SynExpr.App (_,_,_,_,m)
        | SynExpr.TypeApp (_,_,_,_,_,_,m)
        | SynExpr.LetOrUse (_,_,_,_,m)
        | SynExpr.TryWith (_,_,_,_,m,_,_)
        | SynExpr.TryFinally (_,_,m,_,_)
        | SynExpr.Sequential (_,_,_,_,m)
        | SynExpr.ArbitraryAfterError(_,m)
        | SynExpr.FromParseError (_,m) 
        | SynExpr.IfThenElse (_,_,_,_,_,_,m)
        | SynExpr.LongIdentSet (_,_,m)
        | SynExpr.NamedIndexedPropertySet (_,_,_,m)
        | SynExpr.DotIndexedGet (_,_,_,m)
        | SynExpr.DotIndexedSet (_,_,_,_,_,m)
        | SynExpr.DotSet (_,_,_,m)
        | SynExpr.DotNamedIndexedPropertySet (_,_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldGet (_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldSet (_,_,_,_,m)
        | SynExpr.LibraryOnlyILAssembly (_,_,_,_,m)
        | SynExpr.LibraryOnlyStaticOptimization (_,_,_,m)
        | SynExpr.TypeTest (_,_,m)
        | SynExpr.Upcast (_,_,m)
        | SynExpr.AddressOf (_,_,_,m)
        | SynExpr.Downcast (_,_,m)
        | SynExpr.JoinIn (_,_,_,m)
        | SynExpr.InferredUpcast (_,m)
        | SynExpr.InferredDowncast (_,m)
        | SynExpr.Null m
        | SynExpr.Lazy (_, m)
        | SynExpr.TraitCall(_,_,_,m)
        | SynExpr.ImplicitZero (m)
        | SynExpr.YieldOrReturn (_,_,m)
        | SynExpr.YieldOrReturnFrom (_,_,m)
        | SynExpr.LetOrUseBang  (_,_,_,_,_,_,m)
        | SynExpr.DoBang  (_,m) -> m
        | SynExpr.DotGet (expr,_,lidwd,m) -> if lidwd.ThereIsAnExtraDotAtTheEnd then unionRanges expr.Range lidwd.RangeSansAnyExtraDot else m
        | SynExpr.LongIdent (_,lidwd,_,_) -> lidwd.RangeSansAnyExtraDot 
        | SynExpr.DiscardAfterMissingQualificationAfterDot (expr,_) -> expr.Range
        | SynExpr.Ident id -> id.idRange
    /// Attempt to get the range of the first token or initial portion only - this is extremely ad-hoc, just a cheap way to improve a certain 'query custom operation' error range
    member e.RangeOfFirstPortion = 
        match e with 
        // haven't bothered making these cases better than just .Range
        | SynExpr.Quote(_,_,_,_,m)
        | SynExpr.Const(_,m) 
        | SynExpr.Typed (_,_,m)
        | SynExpr.Tuple (_,_,m)
        | SynExpr.ArrayOrList (_,_,m)
        | SynExpr.Record (_,_,_,m)
        | SynExpr.New (_,_,_,m)
        | SynExpr.ObjExpr (_,_,_,_,_,m)
        | SynExpr.While (_,_,_,m)
        | SynExpr.For (_,_,_,_,_,_,m)
        | SynExpr.CompExpr (_,_,_,m)
        | SynExpr.ArrayOrListOfSeqExpr (_,_,m)
        | SynExpr.Lambda (_,_,_,_,m)
        | SynExpr.Match (_,_,_,_,m)
        | SynExpr.MatchLambda (_,_,_,_,m)
        | SynExpr.Do (_,m)
        | SynExpr.Assert (_,m)
        | SynExpr.TypeApp (_,_,_,_,_,_,m)
        | SynExpr.LetOrUse (_,_,_,_,m)
        | SynExpr.TryWith (_,_,_,_,m,_,_)
        | SynExpr.TryFinally (_,_,m,_,_)
        | SynExpr.ArbitraryAfterError(_,m)
        | SynExpr.FromParseError (_,m) 
        | SynExpr.DiscardAfterMissingQualificationAfterDot (_,m) 
        | SynExpr.IfThenElse (_,_,_,_,_,_,m)
        | SynExpr.LongIdent (_,_,_,m)
        | SynExpr.LongIdentSet (_,_,m)
        | SynExpr.NamedIndexedPropertySet (_,_,_,m)
        | SynExpr.DotIndexedGet (_,_,_,m)
        | SynExpr.DotIndexedSet (_,_,_,_,_,m)
        | SynExpr.DotGet (_,_,_,m)
        | SynExpr.DotSet (_,_,_,m)
        | SynExpr.DotNamedIndexedPropertySet (_,_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldGet (_,_,_,m)
        | SynExpr.LibraryOnlyUnionCaseFieldSet (_,_,_,_,m)
        | SynExpr.LibraryOnlyILAssembly (_,_,_,_,m)
        | SynExpr.LibraryOnlyStaticOptimization (_,_,_,m)
        | SynExpr.TypeTest (_,_,m)
        | SynExpr.Upcast (_,_,m)
        | SynExpr.AddressOf (_,_,_,m)
        | SynExpr.Downcast (_,_,m)
        | SynExpr.JoinIn (_,_,_,m)
        | SynExpr.InferredUpcast (_,m)
        | SynExpr.InferredDowncast (_,m)
        | SynExpr.Null m
        | SynExpr.Lazy (_, m)
        | SynExpr.TraitCall(_,_,_,m)
        | SynExpr.ImplicitZero (m)
        | SynExpr.YieldOrReturn (_,_,m)
        | SynExpr.YieldOrReturnFrom (_,_,m)
        | SynExpr.LetOrUseBang  (_,_,_,_,_,_,m)
        | SynExpr.DoBang  (_,m) -> m
        // these are better than just .Range, and also commonly applicable inside queries
        | SynExpr.Paren(_,m,_,_) -> m
        | SynExpr.Sequential (_,_,e1,_,_)
        | SynExpr.App (_,_,e1,_,_) ->
            e1.RangeOfFirstPortion 
        | SynExpr.ForEach (_,_,_,pat,_,_,m) ->
            let start = m.Start 
            let e = (pat.Range : range).Start 
            mkRange m.FileName start e
        | SynExpr.Ident id -> id.idRange

and  
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    SynSimplePat =

    /// Id (ident, altNameRefCell, isCompilerGenerated, isThisVar, isOptArg, range)
    ///
    /// Indicates a simple pattern variable.
    ///
    ///   altNameRefCell 
    ///     Normally 'None' except for some compiler-generated variables in desugaring pattern matching. 
    ///     Pattern processing sets this reference for hidden variable introduced by desugaring pattern matching in arguments.
    ///     The info indicates an alternative (compiler generated) identifier to be used because the name of the identifier is already bound.
    ///     See Product Studio FSharp 1.0, bug 6389.
    ///
    ///   isCompilerGenerated : true if a compiler generated name 
    ///   isThisVar: true if 'this' variable in member  
    ///   isOptArg: true if a '?' is in front of the name
    | Id of  Ident * SynSimplePatAlternativeIdInfo ref option * bool * bool *  bool * range

    | Typed of  SynSimplePat * SynType * range
    | Attrib of  SynSimplePat * SynAttributes * range


and SynSimplePatAlternativeIdInfo = 
    /// We have not decided to use an alternative name in tha pattern and related expression 
    | Undecided of Ident
    /// We have decided to use an alternative name in tha pattern and related expression 
    | Decided of Ident

and 
    [<NoEquality; NoComparison>]
    SynStaticOptimizationConstraint =
    | WhenTyparTyconEqualsTycon of SynTypar *  SynType * range
    | WhenTyparIsStruct of SynTypar * range

and  
    [<NoEquality; NoComparison;RequireQualifiedAccess>]
    /// Represents a simple set of variable bindings a, (a,b) or (a:Type,b:Type) at a lambda,
    /// function definition or other binding point, after the elimination of pattern matching
    /// from the construct, e.g. after changing a "function pat1 -> rule1 | ..." to a 
    /// "fun v -> match v with ..."
    SynSimplePats =
    | SimplePats of SynSimplePat list * range
    | Typed of  SynSimplePats * SynType * range

and  
    [<NoEquality; NoComparison;RequireQualifiedAccess>]
    SynPat =
    | Const of SynConst * range
    | Wild of range
    | Named of  SynPat * Ident *  bool (* true if 'this' variable *)  * SynAccess option * range
    | Typed of  SynPat * SynType * range
    | Attrib of  SynPat * SynAttributes * range
    | Or of  SynPat * SynPat * range
    | Ands of  SynPat list * range
    | LongIdent of LongIdentWithDots * (* holds additional ident for tooling *) Ident option * SynValTyparDecls option (* usually None: temporary used to parse "f<'a> x = x"*) * SynPat list  * SynAccess option * range
    | Tuple of  SynPat list * range
    | Paren of  SynPat * range
    | ArrayOrList of  bool * SynPat list * range
    | Record of ((LongIdent * Ident) * SynPat) list * range
    /// 'null'
    | Null of range
    /// '?id' -- for optional argument names
    | OptionalVal of Ident * range
    /// ':? type '
    | IsInst of SynType * range
    /// <@ expr @>, used for active pattern arguments
    | QuoteExpr of SynExpr * range

    /// Deprecated character ranges
    | DeprecatedCharRange of char * char * range
    /// Used internally in the type checker
    | InstanceMember of  Ident * Ident * (* holds additional ident for tooling *) Ident option * SynAccess option * range (* adhoc overloaded method/property *)

    /// A pattern arising from a parse error
    | FromParseError of SynPat * range

    member p.Range = 
      match p with 
      | SynPat.Const(_,m) | SynPat.Wild m | SynPat.Named (_,_,_,_,m) | SynPat.Or (_,_,m) | SynPat.Ands (_,m) 
      | SynPat.LongIdent (_,_,_,_,_,m) | SynPat.ArrayOrList(_,_,m) | SynPat.Tuple (_,m) |SynPat.Typed(_,_,m) |SynPat.Attrib(_,_,m) 
      | SynPat.Record (_,m) | SynPat.DeprecatedCharRange (_,_,m) | SynPat.Null m | SynPat.IsInst (_,m) | SynPat.QuoteExpr (_,m)
      | SynPat.InstanceMember(_,_,_,_,m) | SynPat.OptionalVal(_,m) | SynPat.Paren(_,m) 
      | SynPat.FromParseError (_,m) -> m 

and  
    [<NoEquality; NoComparison>]
    SynInterfaceImpl = 
    | InterfaceImpl of SynType * SynBinding list * range

and  
    [<NoEquality; NoComparison>]
    SynMatchClause = 
    | Clause of SynPat * SynExpr option *  SynExpr * range * SequencePointInfoForTarget
    member this.RangeOfGuardAndRhs =
        match this with
        | Clause(_,eo,e,_,_) ->
            match eo with
            | None -> e.Range
            | Some x -> unionRanges e.Range x.Range
    member this.Range =
        match this with
        | Clause(_,eo,e,m,_) ->
            match eo with
            | None -> unionRanges e.Range m
            | Some x -> unionRanges (unionRanges e.Range m) x.Range

and SynAttributes = SynAttribute list

and  
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    SynAttribute = 
    { TypeName: LongIdentWithDots;
      ArgExpr: SynExpr 
      /// Target specifier, e.g. "assembly","module",etc.
      Target: Ident option 
      /// Is this attribute being applied to a property getter or setter?
      AppliesToGetterAndSetter: bool
      Range: range } 

and  
    [<NoEquality; NoComparison>]
    SynValData = 
    | SynValData of MemberFlags option * SynValInfo * Ident option

and  
    [<NoEquality; NoComparison>]
    SynBinding = 
    | Binding of 
        SynAccess option *
        SynBindingKind *  
        bool (* mustinline: *) *  
        bool (* mutable: *) *  
        SynAttributes * 
        PreXmlDoc *
        SynValData * 
        SynPat * 
        SynBindingReturnInfo option * 
        SynExpr  *
        range *
        SequencePointInfoForBinding
    // no member just named "Range", as that would be confusing: 
    //  - for everything else, the 'range' member that appears last/second-to-last is the 'full range' of the whole tree construct
    //  - but for Binding, the 'range' is only the range of the left-hand-side, the right-hand-side range is in the SynExpr
    //  - so we use explicit names to avoid confusion
    member x.RangeOfBindingSansRhs = let (Binding(_,_,_,_,_,_,_,_,_,_,m,_)) = x in m
    member x.RangeOfBindingAndRhs = let (Binding(_,_,_,_,_,_,_,_,_,e,m,_)) = x in unionRanges e.Range m
    member x.RangeOfHeadPat = let (Binding(_,_,_,_,_,_,_,headPat,_,_,_,_)) = x in headPat.Range

and 
    [<NoEquality; NoComparison>]
    SynBindingReturnInfo = 
    | SynBindingReturnInfo of SynType * range * SynAttributes


and 
    [<NoComparison>]
    MemberFlags =
    { IsInstance: bool;
      IsDispatchSlot: bool;
      IsOverrideOrExplicitImpl: bool;
      IsFinal: bool;
      MemberKind: MemberKind }

/// Note the member kind is actually computed partially by a syntax tree transformation in tc.fs
and 
    [<StructuralEquality; NoComparison; RequireQualifiedAccess>]
    MemberKind = 
    | ClassConstructor
    | Constructor
    | Member 
    | PropertyGet 
    | PropertySet    
    /// An artifical member kind used prior to the point where a get/set property is split into two distinct members.
    | PropertyGetSet    

and 
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    /// The untyped, unchecked syntax tree for a member signature, used in signature files, abstract member declarations
    /// and member constraints.
    SynMemberSig = 
    | Member of SynValSig  * MemberFlags * range 
    | Interface of SynType  * range
    | Inherit of SynType * range
    | ValField of SynField  * range
    | NestedType  of SynTypeDefnSig * range

and SynMemberSigs = SynMemberSig list

and 
    [<NoEquality; NoComparison>]
    SynTypeDefnKind = 
    | TyconUnspecified 
    | TyconClass 
    | TyconInterface 
    | TyconStruct 
    | TyconRecord
    | TyconUnion
    | TyconAbbrev
    | TyconHiddenRepr
    | TyconAugmentation
    | TyconILAssemblyCode
    | TyconDelegate of SynType * SynValInfo


and 
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    /// The untyped, unchecked syntax tree for the core of a simple type definition, in either signature
    /// or implementation. 
    SynTypeDefnSimpleRepr =
    /// A union type definition, type X = A | B
    | Union      of SynAccess option * SynUnionCases * range
    /// An enum type definition, type X = A = 1 | B = 2
    | Enum       of SynEnumCases * range
    /// A record type definition, type X = { A : int; B : int }
    | Record     of SynAccess option * SynFields * range
    /// An object oriented type definition. This is not a parse-tree form, but represents the core
    /// type representation which the type checker splits out from the "ObjectModel" cases of type definitions.
    | General    of SynTypeDefnKind * (SynType * range * Ident option) list * (SynValSig * MemberFlags) list * SynField list  * bool * bool * SynSimplePat list option * range 
    /// A type defined by using an IL assembly representation. Only used in FSharp.Core.
    /// 
    /// F# syntax: "type X = (# "..."#)
    | LibraryOnlyILAssembly of ILType * range
    /// A type abbreviation, "type X = A.B.C"
    | TypeAbbrev of ParserDetail * SynType * range
    /// An abstract definition , "type X"
    | None       of range
    member this.Range =
        match this with
        | Union(_,_,m) 
        | Enum(_,m)
        | Record(_,_,m)
        | General(_,_,_,_,_,_,_,m) 
        | LibraryOnlyILAssembly(_,m)
        | TypeAbbrev(_,_,m)
        | None(m) -> m

and SynEnumCases = SynEnumCase list

and 
    [<NoEquality; NoComparison>]
    SynEnumCase =
    /// The untyped, unchecked syntax tree for one case in an enum definition.
    | EnumCase of SynAttributes * Ident * SynConst * PreXmlDoc * range
    member this.Range =
        match this with
        | EnumCase(_,_,_,_,m) -> m

and SynUnionCases = SynUnionCase list

and 
    [<NoEquality; NoComparison>]
    SynUnionCase = 
    /// The untyped, unchecked syntax tree for one case in a union definition.
    | UnionCase of SynAttributes * Ident * SynUnionCaseType * PreXmlDoc * SynAccess option * range
    member this.Range =
        match this with
        | UnionCase(_,_,_,_,_,m) -> m

and 
    [<NoEquality; NoComparison>]
    /// The untyped, unchecked syntax tree for the right-hand-side of union definition, excluding members,
    /// in either a signature or implementation.
    SynUnionCaseType = 
    /// Normal style declaration 
    | UnionCaseFields of SynField list      
    /// Full type spec given by 'UnionCase : ty1 * tyN -> rty'. Only used in FSharp.Core, otherwise a warning.
    | UnionCaseFullType of (SynType * SynValInfo) 

and 
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    /// The untyped, unchecked syntax tree for the right-hand-side of a type definition in a signature.
    /// Note: in practice, using a discriminated union to make a distinction between 
    /// "simple" types and "object oriented" types is not particularly useful.
    SynTypeDefnSigRepr =
    /// Indicates the right right-hand-side is a class, struct, interface or other object-model type
    | ObjectModel of SynTypeDefnKind * SynMemberSigs * range
    /// Indicates the right right-hand-side is a record, union or other simple type. 
    | Simple of SynTypeDefnSimpleRepr * range 
    member this.Range =
        match this with
        | ObjectModel(_,_,m) -> m
        | Simple(_,m) -> m

and 
    [<NoEquality; NoComparison>]
    /// The untyped, unchecked syntax tree for a type definition in a signature
    SynTypeDefnSig =
    /// The information for a type definition in a signature
    | TypeDefnSig of SynComponentInfo * SynTypeDefnSigRepr * SynMemberSigs * range

and SynFields = SynField list

and 
    [<NoEquality; NoComparison>]
    /// The untyped, unchecked syntax tree for a field declaration in a record or class
    SynField = 
    | Field of SynAttributes * (* static: *) bool * Ident option * SynType * bool * PreXmlDoc * SynAccess option * range


and 
    [<NoEquality; NoComparison>]
    /// The untyped, unchecked 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.
    SynComponentInfo = 
    | ComponentInfo of SynAttributes * SynTyparDecl list * SynTypeConstraint list * LongIdent * PreXmlDoc * (* preferPostfix: *) bool * SynAccess option * range
    member this.Range =
        match this with
        | ComponentInfo(_,_,_,_,_,_,_,m) -> m

and 
    [<NoEquality; NoComparison>]
    SynValSig = 
    | ValSpfn of 
        SynAttributes * 
        Ident * 
        SynValTyparDecls * 
        SynType * 
        SynValInfo * 
        bool * 
        bool *  (* mutable? *) 
        PreXmlDoc * 
        SynAccess option *
        SynExpr option *
        range 

    member x.RangeOfId  = let (ValSpfn(_,id,_,_,_,_,_,_,_,_,_)) = x in id.idRange
    member x.SynInfo = let (ValSpfn(_,_,_,_,v,_,_,_,_,_,_)) = x in v
    member x.SynType = let (ValSpfn(_,_,_,ty,_,_,_,_,_,_,_)) = x in ty

/// The argument names and other metadata for a member or function
and 
    [<NoEquality; NoComparison>]
    SynValInfo = 
    /// SynValInfo(curriedArgInfos, returnInfo)
    | SynValInfo of SynArgInfo list list * SynArgInfo 
    member x.ArgInfos = (let (SynValInfo(args,_)) = x in args)

/// The argument names and other metadata for a parameter for a member or function
and 
    [<NoEquality; NoComparison>]
    SynArgInfo = 
    | SynArgInfo of SynAttributes * (*optional:*) bool *  Ident option

/// The names and other metadata for the type parameters for a member or function
and 
    [<NoEquality; NoComparison>]
    SynValTyparDecls = 
    | SynValTyparDecls of SynTyparDecl list * bool * SynTypeConstraint list

/// 'exception E = ... '
and [<NoEquality; NoComparison>]
    SynExceptionRepr = 
    | ExceptionDefnRepr of SynAttributes * SynUnionCase * LongIdent option * PreXmlDoc * SynAccess option * range
    member this.Range = match this with ExceptionDefnRepr(_,_,_,_,_,m) -> m

/// 'exception E = ... with ...'
and 
    [<NoEquality; NoComparison>]
    SynExceptionDefn = 
    | ExceptionDefn of SynExceptionRepr * SynMemberDefns * range

and 
    [<NoEquality; NoComparison>]
    SynTypeDefnRepr =
    | ObjectModel  of SynTypeDefnKind * SynMemberDefns * range
    | Simple of SynTypeDefnSimpleRepr * range
    member this.Range =
        match this with
        | ObjectModel(_,_,m) -> m
        | Simple(_,m) -> m

and 
    [<NoEquality; NoComparison>]
    SynTypeDefn =
    | TypeDefn of SynComponentInfo * SynTypeDefnRepr * SynMemberDefns * range
    member this.Range =
        match this with
        | TypeDefn(_,_,_,m) -> m

and 
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    SynMemberDefn = 
    | Open of LongIdent * range
    | Member of SynBinding * range                          
    /// implicit ctor args as a defn line, 'as' specification 
    | ImplicitCtor of SynAccess option * SynAttributes * SynSimplePat list * Ident option * range    
    /// inherit <typ>(args...) as base 
    | ImplicitInherit of SynType * SynExpr * Ident option * range   
    /// LetBindings(bindingList, isStatic, isRecursive, wholeRange)
    ///
    /// localDefns 
    | LetBindings of SynBinding list * bool * bool * range                    
    | AbstractSlot of SynValSig * MemberFlags * range 
    | Interface of SynType * SynMemberDefns option  * range
    | Inherit of SynType  * Ident option * range
    | ValField of SynField  * range
    /// A feature that is not implemented
    | NestedType of SynTypeDefn * SynAccess option * range
    /// SynMemberDefn.AutoProperty (attribs,isStatic,id,tyOpt,propKind,memberFlags,xmlDoc,access,synExpr,mGetSet,mWholeAutoProp).
    /// 
    /// F# syntax: 'member val X = expr'
    | AutoProperty of SynAttributes * bool * Ident * SynType option * MemberKind * (MemberKind -> MemberFlags) * PreXmlDoc * SynAccess option * SynExpr * range option * range
    member d.Range = 
        match d with 
        | SynMemberDefn.Member(_, m)
        | SynMemberDefn.Interface(_, _, m)
        | SynMemberDefn.Open(_, m)
        | SynMemberDefn.LetBindings(_,_,_,m) 
        | SynMemberDefn.ImplicitCtor(_,_,_,_,m)
        | SynMemberDefn.ImplicitInherit(_,_,_,m) 
        | SynMemberDefn.AbstractSlot(_,_,m)
        | SynMemberDefn.Inherit(_,_,m)
        | SynMemberDefn.ValField(_,m)
        | SynMemberDefn.AutoProperty(_,_,_,_,_,_,_,_,_,_,m)
        | SynMemberDefn.NestedType(_,_,m) -> m
      
and SynMemberDefns = SynMemberDefn list

and 
    [<NoEquality; NoComparison;RequireQualifiedAccess>]
    SynModuleDecl =
    | ModuleAbbrev of Ident * LongIdent * range
    | NestedModule of SynComponentInfo * 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 d.Range = 
        match d with 
        | SynModuleDecl.ModuleAbbrev(_,_,m) 
        | SynModuleDecl.NestedModule(_,_,_,m)
        | SynModuleDecl.Let(_,_,m) 
        | SynModuleDecl.DoExpr(_,_,m) 
        | SynModuleDecl.Types(_,m)
        | SynModuleDecl.Exception(_,m)
        | SynModuleDecl.Open (_,m)
        | SynModuleDecl.HashDirective (_,m)
        | SynModuleDecl.NamespaceFragment(SynModuleOrNamespace(_,_,_,_,_,_,m)) 
        | SynModuleDecl.Attributes(_,m) -> m

and SynModuleDecls = SynModuleDecl list

and 
    [<NoEquality; NoComparison>]
    SynExceptionSig = 
    | ExceptionSig of SynExceptionRepr * SynMemberSigs * range

and
    [<NoEquality; NoComparison; RequireQualifiedAccess>]
    SynModuleSigDecl =
    | ModuleAbbrev      of Ident * LongIdent * range
    | NestedModule      of SynComponentInfo * SynModuleSigDecls * range
    | Val               of SynValSig * range
    | Types             of SynTypeDefnSig list * range
    | Exception         of SynExceptionSig * range
    | Open              of LongIdent * range
    | HashDirective     of ParsedHashDirective * range
    | NamespaceFragment of SynModuleOrNamespaceSig 

    member d.Range = 
        match d with 
        | SynModuleSigDecl.ModuleAbbrev (_,_,m)
        | SynModuleSigDecl.NestedModule   (_,_,m)
        | SynModuleSigDecl.Val      (_,m)
        | SynModuleSigDecl.Types    (_,m)
        | SynModuleSigDecl.Exception      (_,m)
        | SynModuleSigDecl.Open     (_,m)
        | SynModuleSigDecl.NamespaceFragment (SynModuleOrNamespaceSig(_,_,_,_,_,_,m)) 
        | SynModuleSigDecl.HashDirective     (_,m) -> m

and SynModuleSigDecls = SynModuleSigDecl list

/// SynModuleOrNamespace(lid,isModule,decls,xmlDoc,attribs,SynAccess,m)
and 
    [<NoEquality; NoComparison>]
    SynModuleOrNamespace = 
    | SynModuleOrNamespace of LongIdent * (*isModule:*) bool * SynModuleDecls * PreXmlDoc * SynAttributes * SynAccess option * range 
    member this.Range =
        match this with
        | SynModuleOrNamespace(_,_,_,_,_,_,m) -> m

and 
    [<NoEquality; NoComparison>]
    SynModuleOrNamespaceSig = 
    | SynModuleOrNamespaceSig of LongIdent * (*isModule:*) bool * SynModuleSigDecls * PreXmlDoc * SynAttributes * SynAccess option * range 

and [<NoEquality; NoComparison>]
    ParsedHashDirective = 
    | ParsedHashDirective of string * string list * range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type ParsedImplFileFragment = 
    | AnonModule of SynModuleDecls * range
    | NamedModule of SynModuleOrNamespace
    | NamespaceFragment of LongIdent * bool * SynModuleDecls * PreXmlDoc * SynAttributes * range

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type ParsedSigFileFragment = 
    | AnonModule of SynModuleSigDecls * range
    | NamedModule of SynModuleOrNamespaceSig
    | NamespaceFragment of LongIdent * bool * SynModuleSigDecls * PreXmlDoc * SynAttributes * range

[<NoEquality; NoComparison>]
type ParsedFsiInteraction =
    | IDefns of SynModuleDecl list * range
    | IHash  of ParsedHashDirective * range

[<NoEquality; NoComparison>]
type ParsedImplFile = 
    | ParsedImplFile of ParsedHashDirective list * ParsedImplFileFragment list

[<NoEquality; NoComparison>]
type ParsedSigFile = 
    | ParsedSigFile of ParsedHashDirective list * ParsedSigFileFragment list

//----------------------------------------------------------------------
// AST and parsing utilities.
//----------------------------------------------------------------------

let ident (s,r) = new Ident(s,r)
let textOfId (id:Ident) = id.idText
let pathOfLid lid = List.map textOfId lid
let arrPathOfLid lid = Array.ofList (List.map textOfId lid)
let textOfPath path = String.concat "." path
let textOfArrPath path = String.concat "." (List.ofArray path)
let textOfLid lid = textOfPath (pathOfLid lid)

let rangeOfLid (lid: Ident list) = 
    match lid with 
    | [] -> failwith "rangeOfLid"
    | [id] -> id.idRange
    | h::t -> unionRanges h.idRange (List.last t).idRange 

[<RequireQualifiedAccess>]
type ScopedPragma = 
   | WarningOff of range * int
   // Note: this type may be extended in the future with optimization on/off switches etc.

// These are the results of parsing + folding in the implicit file name
/// ImplFile(modname,isScript,qualName,hashDirectives,modules,isLastCompiland)

/// QualifiedNameOfFile acts to fully-qualify module specifications and implementations, 
/// most importantly the ones that simply contribute fragments to a namespace (i.e. the ParsedSigFileFragment.NamespaceFragment case) 
/// There may be multiple such fragments in a single assembly.  There may thus also 
/// be multiple matching pairs of these in an assembly, all contributing types to the same 
/// namespace. 
[<NoEquality; NoComparison>]
type QualifiedNameOfFile = 
    | QualifiedNameOfFile of Ident 
    member x.Text = (let (QualifiedNameOfFile(t)) = x in t.idText)
    member x.Id = (let (QualifiedNameOfFile(t)) = x in t)
    member x.Range = (let (QualifiedNameOfFile(t)) = x in t.idRange)

[<NoEquality; NoComparison>]
type ParsedImplFileInput = 
    | ParsedImplFileInput of string * (*isScript: *) bool * QualifiedNameOfFile * ScopedPragma list * ParsedHashDirective list * SynModuleOrNamespace list * bool

[<NoEquality; NoComparison>]
type ParsedSigFileInput = 
    | ParsedSigFileInput of string * QualifiedNameOfFile * ScopedPragma list * ParsedHashDirective list * SynModuleOrNamespaceSig list

[<NoEquality; NoComparison; RequireQualifiedAccess>]
type ParsedInput = 
    | ImplFile of ParsedImplFileInput
    | SigFile of ParsedSigFileInput

    member inp.Range = 
        match inp with
        | ParsedInput.ImplFile (ParsedImplFileInput(_,_,_,_,_,(SynModuleOrNamespace(_,_,_,_,_,_,m) :: _),_))
        | ParsedInput.SigFile (ParsedSigFileInput(_,_,_,_,(SynModuleOrNamespaceSig(_,_,_,_,_,_,m) :: _))) -> m
        | ParsedInput.ImplFile (ParsedImplFileInput(filename,_,_,_,_,[],_))
        | ParsedInput.SigFile (ParsedSigFileInput(filename,_,_,_,[])) ->
#if DEBUG      
            assert("" = "compiler expects ParsedInput.ImplFile and ParsedInput.SigFile to have at least one fragment, 4488")
#endif    
            rangeN filename 0 (* There are no implementations, e.g. due to errors, so return a default range for the file *)


//----------------------------------------------------------------------
// Construct syntactic AST nodes
//-----------------------------------------------------------------------

// REVIEW: get rid of this global state 
type SynArgNameGenerator() = 
    let mutable count = 0 
    let generatedArgNamePrefix = "_arg"

    member __.New() : string = count <- count + 1; generatedArgNamePrefix + string count
    member __.Reset() = count <- 0

//----------------------------------------------------------------------
// Construct syntactic AST nodes
//-----------------------------------------------------------------------


let mkSynId m s = Ident(s,m)
let pathToSynLid m p = List.map (mkSynId m) p
let mkSynIdGet m n = SynExpr.Ident(mkSynId m n)
let mkSynLidGet m path n = 
    let lid = pathToSynLid m path @ [mkSynId m n]
    let dots = List.replicate (lid.Length - 1) m
    SynExpr.LongIdent(false,LongIdentWithDots(lid,dots),None,m)
let mkSynIdGetWithAlt m id altInfo = 
    match altInfo with 
    | None -> SynExpr.Ident id
    | _ -> SynExpr.LongIdent(false,LongIdentWithDots([id],[]),altInfo,m)

let mkSynSimplePatVar isOpt id = SynSimplePat.Id (id,None,false,false,isOpt,id.idRange)
let mkSynCompGenSimplePatVar id = SynSimplePat.Id (id,None,true,false,false,id.idRange)

/// Match a long identifier, including the case for single identifiers which gets a more optimized node in the syntax tree.
let (|LongOrSingleIdent|_|) inp = 
    match inp with
    | SynExpr.LongIdent(isOpt,lidwd,altId,_m) -> Some (isOpt,lidwd,altId,lidwd.RangeSansAnyExtraDot)
    | SynExpr.Ident id -> Some (false,LongIdentWithDots([id],[]),None,id.idRange)
    | _ -> None

let (|SingleIdent|_|) inp = 
    match inp with
    | SynExpr.LongIdent(false,LongIdentWithDots([id],_),None,_) -> Some id
    | SynExpr.Ident id -> Some id
    | _ -> None
    
/// This affects placement of sequence points
let rec IsControlFlowExpression e = 
    match e with 
    | SynExpr.ObjExpr _ 
    | SynExpr.Lambda _ 
    | SynExpr.LetOrUse _ 
    | SynExpr.Sequential _ 
    // Treat "ident { ... }" as a control flow expression
    | SynExpr.App (_, _, SynExpr.Ident _, SynExpr.CompExpr _,_) 
    | SynExpr.IfThenElse _ 
    | SynExpr.LetOrUseBang _
    | SynExpr.Match _  
    | SynExpr.TryWith _ 
    | SynExpr.TryFinally _ 
    | SynExpr.For _ 
    | SynExpr.ForEach _ 
    | SynExpr.While _ -> true
    | SynExpr.Typed(e,_,_) -> IsControlFlowExpression e
    | _ -> false

let mkAnonField (ty: SynType) = Field([],false,None,ty,false,PreXmlDoc.Empty,None,ty.Range)

let mkSynPatVar vis (id:Ident) = SynPat.Named (SynPat.Wild id.idRange,id,false,vis,id.idRange)
let mkSynThisPatVar (id:Ident) = SynPat.Named (SynPat.Wild id.idRange,id,true,None,id.idRange)
let mkSynPatMaybeVar lidwd vis m =  SynPat.LongIdent (lidwd,None,None,[],vis,m) 

/// Extract the argument for patterns corresponding to the declaration of 'new ... = ...'
let (|SynPatForConstructorDecl|_|) x = 
    match x with 
    | SynPat.LongIdent (LongIdentWithDots([_],_),_,_,[arg],_,_) -> Some arg
    | _ -> None

/// Recognize the '()' in 'new()'
let (|SynPatForNullaryArgs|_|) x = 
    match x with 
    | SynPat.Paren(SynPat.Const(SynConst.Unit,_),_) -> Some()
    | _ -> None
    
let (|SynExprErrorSkip|) (p:SynExpr) = 
    match p with 
    | SynExpr.FromParseError(p,_) -> p
    | _ -> p

let (|SynExprParen|_|) (e:SynExpr) = 
    match e with 
    | SynExpr.Paren(SynExprErrorSkip e,a,b,c) -> Some (e,a,b,c)
    | _ -> None

let (|SynPatErrorSkip|) (p:SynPat) = 
    match p with 
    | SynPat.FromParseError(p,_) -> p
    | _ -> p

/// Push non-simple parts of a patten match over onto the r.h.s. of a lambda.
/// Return a simple pattern and a function to build a match on the r.h.s. if the pattern is complex
let rec SimplePatOfPat (synArgNameGenerator: SynArgNameGenerator) p =
    match p with 
    | SynPat.Typed(p',ty,m) -> 
        let p2,laterf = SimplePatOfPat synArgNameGenerator p'
        SynSimplePat.Typed(p2,ty,m), 
        laterf
    | SynPat.Attrib(p',attribs,m) -> 
        let p2,laterf = SimplePatOfPat synArgNameGenerator p'
        SynSimplePat.Attrib(p2,attribs,m), 
        laterf
    | SynPat.Named (SynPat.Wild _, v,thisv,_,m) -> 
        SynSimplePat.Id (v,None,false,thisv,false,m), 
        None
    | SynPat.OptionalVal (v,m) -> 
        SynSimplePat.Id (v,None,false,false,true,m), 
        None
    | SynPat.Paren (p,_) -> SimplePatOfPat synArgNameGenerator p 
    | SynPat.FromParseError (p,_) -> SimplePatOfPat synArgNameGenerator p 
    | _ -> 
        let m = p.Range
        let isCompGen,altNameRefCell,id,item = 
            match p with 
            | SynPat.LongIdent(LongIdentWithDots([id],_),_,None,[],None,_) -> 
                // The pattern is 'V' or some other capitalized identifier.
                // It may be a real variable, in which case we want to maintain its name.
                // But it may also be a nullary union case or some other identifier.
                // In this case, we want to use an alternate compiler generated name for the hidden variable.
                let altNameRefCell = Some (ref (Undecided (mkSynId m (synArgNameGenerator.New()))))
                let item = mkSynIdGetWithAlt m id altNameRefCell
                false,altNameRefCell,id,item
            | _ -> 
                let nm = synArgNameGenerator.New()
                let id = mkSynId m nm
                let item = mkSynIdGet m nm
                true,None,id,item
        SynSimplePat.Id (id,altNameRefCell,isCompGen,false,false,id.idRange),
        Some (fun e -> 
                let clause = Clause(p,None,e,m,SuppressSequencePointAtTarget)
                SynExpr.Match(NoSequencePointAtInvisibleBinding,item,[clause],false,clause.Range)) 

let appFunOpt funOpt x = match funOpt with None -> x | Some f -> f x
let composeFunOpt funOpt1 funOpt2 = match funOpt2 with None -> funOpt1 | Some f -> Some (fun x -> appFunOpt funOpt1 (f x))
let rec SimplePatsOfPat synArgNameGenerator p =
    match p with 
    | SynPat.FromParseError (p,_) -> SimplePatsOfPat synArgNameGenerator p 
    | SynPat.Typed(p',ty,m) -> 
        let p2,laterf = SimplePatsOfPat synArgNameGenerator p'
        SynSimplePats.Typed(p2,ty,m), 
        laterf
//    | SynPat.Paren (p,m) -> SimplePatsOfPat synArgNameGenerator p 
    | SynPat.Tuple (ps,m) 
    | SynPat.Paren(SynPat.Tuple (ps,m),_) -> 
        let ps2,laterf = 
          List.foldBack 
            (fun (p',rhsf) (ps',rhsf') -> 
              p'::ps', 
              (composeFunOpt rhsf rhsf'))
            (List.map (SimplePatOfPat synArgNameGenerator) ps) 
            ([], None)
        SynSimplePats.SimplePats (ps2,m),
        laterf
    | SynPat.Paren(SynPat.Const (SynConst.Unit,m),_) 
    | SynPat.Const (SynConst.Unit,m) -> 
        SynSimplePats.SimplePats ([],m),
        None
    | _ -> 
        let m = p.Range
        let sp,laterf = SimplePatOfPat synArgNameGenerator p
        SynSimplePats.SimplePats ([sp],m),laterf

let PushPatternToExpr synArgNameGenerator isMember pat (rhs: SynExpr) =
    let nowpats,laterf = SimplePatsOfPat synArgNameGenerator pat
    nowpats, SynExpr.Lambda (isMember,false,nowpats, appFunOpt laterf rhs,rhs.Range)

let private isSimplePattern pat =
    let _nowpats,laterf = SimplePatsOfPat (SynArgNameGenerator()) pat
    isNone laterf
  
/// "fun (UnionCase x) (UnionCase y) -> body" 
///       ==> 
///   "fun tmp1 tmp2 -> 
///        let (UnionCase x) = tmp1 in 
///        let (UnionCase y) = tmp2 in 
///        body" 
let PushCurriedPatternsToExpr synArgNameGenerator wholem isMember pats rhs =
    // Two phases
    // First phase: Fold back, from right to left, pushing patterns into r.h.s. expr
    let spatsl,rhs = 
        (pats, ([],rhs)) 
           ||> List.foldBack (fun arg (spatsl,body) -> 
              let spats,bodyf = SimplePatsOfPat synArgNameGenerator arg
              // accumulate the body. This builds "let (UnionCase y) = tmp2 in body"
              let body = appFunOpt bodyf body
              // accumulate the patterns
              let spatsl = spats::spatsl
              (spatsl,body))
    // Second phase: build lambdas. Mark subsequent ones with "true" indicating they are part of an iterated sequence of lambdas
    let expr = 
        match spatsl with
        | [] -> rhs
        | h::t -> 
            let expr = List.foldBack (fun spats e -> SynExpr.Lambda (isMember,true,spats, e,wholem)) t rhs
            let expr = SynExpr.Lambda (isMember,false,h, expr,wholem)
            expr
    spatsl,expr

/// Helper for parsing the inline IL fragments. 
#if NO_INLINE_IL_PARSER
let ParseAssemblyCodeInstructions _s m = 
    errorR(Error((193,"Inline IL not valid in a hosted environment"),m)) ; [| |]
#else
let ParseAssemblyCodeInstructions s m = 
    try Microsoft.FSharp.Compiler.AbstractIL.Internal.AsciiParser.ilInstrs 
           Microsoft.FSharp.Compiler.AbstractIL.Internal.AsciiLexer.token 
           (UnicodeLexing.StringAsLexbuf s)
    with RecoverableParseError -> 
      errorR(Error(FSComp.SR.astParseEmbeddedILError(), m)); [| |]
#endif

/// Helper for parsing the inline IL fragments. 
#if NO_INLINE_IL_PARSER
let ParseAssemblyCodeType _s m = 
    // REVIEW: break out into a resource
    errorR(Error((193,"Inline IL not valid in a hosted environment"),m)) ; IL.ecmaILGlobals.typ_Object
#else
let ParseAssemblyCodeType s m = 
    try Microsoft.FSharp.Compiler.AbstractIL.Internal.AsciiParser.ilType 
           Microsoft.FSharp.Compiler.AbstractIL.Internal.AsciiLexer.token 
           (UnicodeLexing.StringAsLexbuf s)
    with RecoverableParseError -> 
      errorR(Error(FSComp.SR.astParseEmbeddedILTypeError(),m)); 
      IL.ecmaILGlobals.typ_Object
#endif

//------------------------------------------------------------------------
// AST constructors
//------------------------------------------------------------------------

let opNameParenGet  = CompileOpName parenGet 
let opNameQMark = CompileOpName qmark
let mkSynOperator opm oper = mkSynIdGet opm (CompileOpName oper)

let mkSynInfix opm (l:SynExpr) oper (r:SynExpr) = 
    let firstTwoRange = unionRanges l.Range opm
    let wholeRange = unionRanges l.Range r.Range
    SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynOperator opm oper, l, firstTwoRange), r, wholeRange)
let mkSynBifix   m oper x1 x2 = SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynOperator m oper,x1,m), x2,m)
let mkSynTrifix  m oper x1 x2 x3 = SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynOperator m oper,x1,m), x2,m), x3,m)
let mkSynQuadfix m oper x1 x2 x3 x4 = SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynOperator m oper,x1,m), x2,m), x3,m),x4,m)
let mkSynQuinfix m oper x1 x2 x3 x4 x5 = SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, false, SynExpr.App (ExprAtomicFlag.NonAtomic, true, mkSynOperator m oper,x1,m), x2,m), x3,m),x4,m),x5,m)
let mkSynPrefix opm m oper x = SynExpr.App (ExprAtomicFlag.NonAtomic, false, mkSynOperator opm oper, x,m)
let mkSynCaseName m n = [mkSynId m (CompileOpName n)]

let mkSynApp1 f x1 m = SynExpr.App(ExprAtomicFlag.NonAtomic,false,f,x1,m)
let mkSynApp2 f x1 x2 m = mkSynApp1 (mkSynApp1 f x1 m) x2 m
let mkSynApp3 f x1 x2 x3 m = mkSynApp1 (mkSynApp2 f x1 x2 m) x3 m
let mkSynApp4 f x1 x2 x3 x4 m = mkSynApp1 (mkSynApp3 f x1 x2 x3 m) x4 m
let mkSynApp5 f x1 x2 x3 x4 x5 m = mkSynApp1 (mkSynApp4 f x1 x2 x3 x4 m) x5 m
let mkSynDotParenSet  m a b c = mkSynTrifix m parenSet a b c
let mkSynDotBrackGet  m mDot a b   = SynExpr.DotIndexedGet(a,[b],mDot,m)
let mkSynQMarkSet m a b c = mkSynTrifix m qmarkSet a b c

let mkSynDotBrackSliceGet  m mDot arr (x,y) = 
    SynExpr.DotIndexedGet(arr,[x;y],mDot,m)

let mkSynDotBrackSlice2Get  m mDot arr (x1,y1) (x2,y2) = 
    SynExpr.DotIndexedGet(arr,[x1;y1;x2;y2],mDot,m)

let mkSynDotBrackSlice3Get  m mDot arr (x1,y1) (x2,y2) (x3,y3) = 
    SynExpr.DotIndexedGet(arr,[x1;y1;x2;y2;x3;y3],mDot,m)

let mkSynDotBrackSlice4Get  m mDot arr (x1,y1) (x2,y2) (x3,y3) (x4,y4) = 
    SynExpr.DotIndexedGet(arr,[x1;y1;x2;y2;x3;y3;x4;y4],mDot,m)

let mkSynDotParenGet lhsm dotm a b   = 
    match b with
    | SynExpr.Tuple ([_;_],_,_)   -> errorR(Deprecated(FSComp.SR.astDeprecatedIndexerNotation(),lhsm)) ; SynExpr.Const(SynConst.Unit,lhsm)
    | SynExpr.Tuple ([_;_;_],_,_) -> errorR(Deprecated(FSComp.SR.astDeprecatedIndexerNotation(),lhsm)) ; SynExpr.Const(SynConst.Unit,lhsm)
    | _ -> mkSynInfix dotm a parenGet b

let mkSynUnit m = SynExpr.Const(SynConst.Unit,m)
let mkSynUnitPat m = SynPat.Const(SynConst.Unit,m)
let mkSynDelay m e = SynExpr.Lambda (false,false,SynSimplePats.SimplePats ([mkSynCompGenSimplePatVar (mkSynId m "unitVar")],m), e, m)

let mkSynAssign (l: SynExpr) (r: SynExpr) = 
    let m = unionRanges l.Range r.Range
    match l with 
    //| SynExpr.Paren(l2,m2)  -> mkSynAssign m l2 r
    | LongOrSingleIdent(false,v,None,_)  -> SynExpr.LongIdentSet (v,r,m)
    | SynExpr.DotGet(e,_,v,_)  -> SynExpr.DotSet (e,v,r,m)
    | SynExpr.DotIndexedGet(e1,e2,mDot,mLeft)  -> SynExpr.DotIndexedSet (e1,e2,r,mLeft,mDot,m)
    | SynExpr.LibraryOnlyUnionCaseFieldGet (x,y,z,_) -> SynExpr.LibraryOnlyUnionCaseFieldSet (x,y,z,r,m) 
    | SynExpr.App (_, _, SynExpr.App(_, _, SingleIdent(nm), a, _),b,_) when nm.idText = opNameQMark -> 
        mkSynQMarkSet m a b r
    | SynExpr.App (_, _, SynExpr.App(_, _, SingleIdent(nm), a, _),b,_) when nm.idText = opNameParenGet -> 
        mkSynDotParenSet m a b r
    | SynExpr.App (_, _, SynExpr.LongIdent(false,v,None,_),x,_)  -> SynExpr.NamedIndexedPropertySet (v,x,r,m)
    | SynExpr.App (_, _, SynExpr.DotGet(e,_,v,_),x,_)  -> SynExpr.DotNamedIndexedPropertySet (e,v,x,r,m)
    |   _ -> errorR(Error(FSComp.SR.astInvalidExprLeftHandOfAssignment(), m));  l  // return just the LHS, so the typechecker can see it and capture expression typings that may be useful for dot lookups

let rec mkSynDot dotm m l r = 
    match l with 
    | SynExpr.LongIdent(isOpt,LongIdentWithDots(lid,dots),None,_) -> 
        SynExpr.LongIdent(isOpt,LongIdentWithDots(lid@[r],dots@[dotm]),None,m) // REVIEW: MEMORY PERFORMANCE: This list operation is memory intensive (we create a lot of these list nodes) - an ImmutableArray would be better here
    | SynExpr.Ident id -> 
        SynExpr.LongIdent(false,LongIdentWithDots([id;r],[dotm]),None,m)
    | SynExpr.DotGet(e,dm,LongIdentWithDots(lid,dots),_) -> 
        SynExpr.DotGet(e,dm,LongIdentWithDots(lid@[r],dots@[dotm]),m)// REVIEW: MEMORY PERFORMANCE: This is memory intensive (we create a lot of these list nodes) - an ImmutableArray would be better here
    | expr -> 
        SynExpr.DotGet(expr,dotm,LongIdentWithDots([r],[]),m)

let rec mkSynDotMissing dotm m l = 
    match l with 
    | SynExpr.LongIdent(isOpt,LongIdentWithDots(lid,dots),None,_) -> 
        SynExpr.LongIdent(isOpt,LongIdentWithDots(lid,dots@[dotm]),None,m) // REVIEW: MEMORY PERFORMANCE: This list operation is memory intensive (we create a lot of these list nodes) - an ImmutableArray would be better here
    | SynExpr.Ident id -> 
        SynExpr.LongIdent(false,LongIdentWithDots([id],[dotm]),None,m)
    | SynExpr.DotGet(e,dm,LongIdentWithDots(lid,dots),_) -> 
        SynExpr.DotGet(e,dm,LongIdentWithDots(lid,dots@[dotm]),m)// REVIEW: MEMORY PERFORMANCE: This is memory intensive (we create a lot of these list nodes) - an ImmutableArray would be better here
    | expr -> 
        SynExpr.DiscardAfterMissingQualificationAfterDot(expr,m)

let mkSynFunMatchLambdas synArgNameGenerator isMember wholem ps e = 
    let _,e =  PushCurriedPatternsToExpr synArgNameGenerator wholem isMember ps e 
    e


// error recovery - the contract is that these expressions can only be produced if an error has already been reported
// (as a result, future checking may choose not to report errors involving these, to prevent noisy cascade errors)
let arbExpr(debugStr,range:range) = SynExpr.ArbitraryAfterError(debugStr,range.MakeSynthetic())
type SynExpr with
    member this.IsArbExprAndThusAlreadyReportedError =
        match this with
        | SynExpr.ArbitraryAfterError _ -> true
        | _ -> false

/// The syntactic elements associated with the "return" of a function or method. Some of this is
/// mostly dummy information to make the return element look like an argument,
/// the important thing is that (a) you can give a return type for the function or method, and 
/// (b) you can associate .NET attributes to return of a function or method and these get stored in .NET metadata.
type SynReturnInfo = SynReturnInfo of (SynType * SynArgInfo) * range


/// Operations related to the syntactic analysis of arguments of value, function and member definitions and signatures.
///
/// Function and member definitions have strongly syntactically constrained arities.  We infer
/// the arity from the syntax.
///
/// For example, we record the arity for: 
/// StaticProperty --> [1]               -- for unit arg
/// this.InstanceProperty --> [1;1]        -- for unit arg
/// StaticMethod(args) --> map InferSynArgInfoFromSimplePat args
/// this.InstanceMethod() --> 1 :: map InferSynArgInfoFromSimplePat args
/// this.InstanceProperty with get(argpat) --> 1 :: [InferSynArgInfoFromSimplePat argpat]
/// StaticProperty with get(argpat) --> [InferSynArgInfoFromSimplePat argpat]
/// this.InstanceProperty with get() --> 1 :: [InferSynArgInfoFromSimplePat argpat]
/// StaticProperty with get() --> [InferSynArgInfoFromSimplePat argpat]
/// 
/// this.InstanceProperty with set(argpat)(v) --> 1 :: [InferSynArgInfoFromSimplePat argpat; 1]
/// StaticProperty with set(argpat)(v) --> [InferSynArgInfoFromSimplePat argpat; 1]
/// this.InstanceProperty with set(v) --> 1 :: [1]
/// StaticProperty with set(v) --> [1] 
module SynInfo = 
    /// The argument information for an argument without a name
    let unnamedTopArg1 = SynArgInfo([],false,None)

    /// The argument information for a curried argument without a name
    let unnamedTopArg = [unnamedTopArg1]

    /// The argument information for a '()' argument
    let unitArgData = unnamedTopArg

    /// The 'argument' information for a return value where no attributes are given for the return value (the normal case)
    let unnamedRetVal = SynArgInfo([],false,None)

    /// The 'argument' information for the 'this'/'self' parameter in the cases where it is not given explicitly
    let selfMetadata = unnamedTopArg

    /// Determine if a syntactic information represents a member without arguments (which is implicitly a property getter)
    let HasNoArgs (SynValInfo(args,_)) = isNil args

    /// Check if one particular argument is an optional argument. Used when adjusting the
    /// types of optional arguments for function and member signatures.
    let IsOptionalArg (SynArgInfo(_,isOpt,_)) = isOpt

    /// Check if there are any optional arguments in the syntactic argument information. Used when adjusting the
    /// types of optional arguments for function and member signatures.
    let HasOptionalArgs (SynValInfo(args,_)) = List.exists (List.exists IsOptionalArg) args

    /// Add a parameter entry to the syntactic value information to represent the '()' argument to a property getter. This is 
    /// used for the implicit '()' argument in property getter signature specifications.
    let IncorporateEmptyTupledArgForPropertyGetter (SynValInfo(args,retInfo)) = SynValInfo([]::args,retInfo)

    /// Add a parameter entry to the syntactic value information to represent the 'this' argument. This is 
    /// used for the implicit 'this' argument in member signature specifications.
    let IncorporateSelfArg (SynValInfo(args,retInfo)) = SynValInfo(selfMetadata::args,retInfo)

    /// Add a parameter entry to the syntactic value information to represent the value argument for a property setter. This is 
    /// used for the implicit value argument in property setter signature specifications.
    let IncorporateSetterArg (SynValInfo(args,retInfo)) = 
         let args = 
             match args with 
             | [] -> [unnamedTopArg] 
             | [arg] -> [arg@[unnamedTopArg1]] 
             | _ -> failwith "invalid setter type" 
         SynValInfo(args,retInfo)

    /// Get the argument counts for each curried argument group. Used in some adhoc places in tc.fs.
    let AritiesOfArgs (SynValInfo(args,_)) = List.map List.length args

    /// Get the argument attributes from the syntactic information for an argument.
    let AttribsOfArgData (SynArgInfo(attribs,_,_)) = attribs

    /// Infer the syntactic argument info for a single argument from a simple pattern.
    let rec InferSynArgInfoFromSimplePat attribs p = 
        match p with 
        | SynSimplePat.Id(nm,_,isCompGen,_,isOpt,_) -> 
           SynArgInfo(attribs, isOpt, (if isCompGen then None else Some nm))
        | SynSimplePat.Typed(a,_,_) -> InferSynArgInfoFromSimplePat attribs a
        | SynSimplePat.Attrib(a,attribs2,_) -> InferSynArgInfoFromSimplePat (attribs @ attribs2) a
      
    /// Infer the syntactic argument info for one or more arguments one or more simple patterns.
    let rec InferSynArgInfoFromSimplePats x = 
        match x with 
        | SynSimplePats.SimplePats(ps,_) -> List.map (InferSynArgInfoFromSimplePat []) ps
        | SynSimplePats.Typed(ps,_,_) -> InferSynArgInfoFromSimplePats ps

    /// Infer the syntactic argument info for one or more arguments a pattern.
    let InferSynArgInfoFromPat p = 
        // It is ok to use a fresh SynArgNameGenerator here, because compiler generated names are filtered from SynArgInfo, see InferSynArgInfoFromSimplePat above
        let sp,_ = SimplePatsOfPat (SynArgNameGenerator()) p 
        InferSynArgInfoFromSimplePats sp

    /// Make sure only a solitary unit argument has unit elimination
    let AdjustArgsForUnitElimination infosForArgs = 
        match infosForArgs with 
        | [[]] -> infosForArgs 
        | _ -> infosForArgs |> List.map (function [] -> unitArgData | x -> x)

    /// Transform a property declared using '[static] member P = expr' to a method taking a "unit" argument.
    /// This is similar to IncorporateEmptyTupledArgForPropertyGetter, but applies to member definitions
    /// rather than member signatures.
    let AdjustMemberArgs memFlags infosForArgs = 
        match infosForArgs with 
        | [] when memFlags=MemberKind.Member -> [] :: infosForArgs
        | _ -> infosForArgs

    /// For 'let' definitions, we infer syntactic argument information from the r.h.s. of a definition, if it
    /// is an immediate 'fun ... -> ...' or 'function ...' expression. This is noted in the F# language specification.
    /// This does not apply to member definitions.
    let InferLambdaArgs origRhsExpr = 
        let rec loop e = 
            match e with 
            | SynExpr.Lambda(false,_,spats,rest,_) -> 
                InferSynArgInfoFromSimplePats spats :: loop rest
            | _ -> []
        loop origRhsExpr

    let InferSynReturnData (retInfo: SynReturnInfo option) = 
        match retInfo with 
        | None -> unnamedRetVal 
        | Some(SynReturnInfo((_,retInfo),_)) -> retInfo

    let private emptySynValInfo = SynValInfo([],unnamedRetVal)
    
    let emptySynValData = SynValData(None,emptySynValInfo,None)

    /// Infer the syntactic information for a 'let' or 'member' definition, based on the argument pattern,
    /// any declared return information (e.g. .NET attributes on the return element), and the r.h.s. expression
    /// in the case of 'let' definitions.
    let InferSynValData (memberFlagsOpt, pat, retInfo, origRhsExpr) = 

        let infosForExplicitArgs = 
            match pat with 
            | Some(SynPat.LongIdent(_,_,_,curriedArgs,_,_)) -> List.map InferSynArgInfoFromPat curriedArgs
            | _ -> []

        let explicitArgsAreSimple = 
            match pat with 
            | Some(SynPat.LongIdent(_,_,_,curriedArgs,_,_)) -> List.forall isSimplePattern curriedArgs
            | _ -> true

        let retInfo = InferSynReturnData retInfo

        match memberFlagsOpt with
        | None -> 
            let infosForLambdaArgs = InferLambdaArgs origRhsExpr
            let infosForArgs = infosForExplicitArgs @ (if explicitArgsAreSimple then infosForLambdaArgs else [])
            let infosForArgs = AdjustArgsForUnitElimination infosForArgs 
            SynValData(None,SynValInfo(infosForArgs,retInfo),None)

        | Some memFlags  -> 
            let infosForObjArgs = 
                if memFlags.IsInstance then [ selfMetadata ] else []

            let infosForArgs = AdjustMemberArgs memFlags.MemberKind infosForExplicitArgs
            let infosForArgs = AdjustArgsForUnitElimination infosForArgs 
            
            let argInfos = infosForObjArgs @ infosForArgs
            SynValData(Some(memFlags),SynValInfo(argInfos,retInfo),None)



let mkSynBindingRhs staticOptimizations rhsExpr mRhs retInfo =
    let rhsExpr = List.foldBack (fun (c,e1) e2 -> SynExpr.LibraryOnlyStaticOptimization (c,e1,e2,mRhs)) staticOptimizations rhsExpr
    let rhsExpr,retTyOpt = 
        match retInfo with 
        | Some (SynReturnInfo((ty,SynArgInfo(rattribs,_,_)),tym)) -> SynExpr.Typed(rhsExpr,ty,rhsExpr.Range), Some(SynBindingReturnInfo(ty,tym,rattribs) )
        | None -> rhsExpr,None 
    rhsExpr,retTyOpt

let mkSynBinding (xmlDoc,headPat) (vis,isInline,isMutable,mBind,spBind,retInfo,origRhsExpr,mRhs,staticOptimizations,attrs,memberFlagsOpt) =
    let info = SynInfo.InferSynValData (memberFlagsOpt, Some headPat, retInfo, origRhsExpr)
    let rhsExpr,retTyOpt = mkSynBindingRhs staticOptimizations origRhsExpr mRhs retInfo
    Binding (vis,NormalBinding,isInline,isMutable,attrs,xmlDoc,info,headPat,retTyOpt,rhsExpr,mBind,spBind) 

let NonVirtualMemberFlags k = { MemberKind=k;                           IsInstance=true;  IsDispatchSlot=false; IsOverrideOrExplicitImpl=false; IsFinal=false }
let CtorMemberFlags =         { MemberKind=MemberKind.Constructor;       IsInstance=false; IsDispatchSlot=false; IsOverrideOrExplicitImpl=false; IsFinal=false }
let ClassCtorMemberFlags =    { MemberKind=MemberKind.ClassConstructor;  IsInstance=false; IsDispatchSlot=false; IsOverrideOrExplicitImpl=false; IsFinal=false }
let OverrideMemberFlags k =   { MemberKind=k;                           IsInstance=true;  IsDispatchSlot=false; IsOverrideOrExplicitImpl=true;  IsFinal=false }
let AbstractMemberFlags k =   { MemberKind=k;                           IsInstance=true;  IsDispatchSlot=true;  IsOverrideOrExplicitImpl=false; IsFinal=false }
let StaticMemberFlags k =     { MemberKind=k;                           IsInstance=false; IsDispatchSlot=false; IsOverrideOrExplicitImpl=false; IsFinal=false }

let inferredTyparDecls = SynValTyparDecls([],true,[])
let noInferredTypars = SynValTyparDecls([],false,[])

//------------------------------------------------------------------------
// Lexer args: status of #if/#endif processing.  
//------------------------------------------------------------------------

type LexerIfdefStackEntry = IfDefIf | IfDefElse 
type LexerIfdefStackEntries = (LexerIfdefStackEntry * range) list
type LexerIfdefStack = LexerIfdefStackEntries ref

/// Specifies how the 'endline' function in the lexer should continue after
/// it reaches end of line or eof. The options are to continue with 'token' function
/// or to continue with 'skip' function.
type LexerEndlineContinuation = 
    | Token of LexerIfdefStackEntries
    | Skip of LexerIfdefStackEntries * int * range
    member x.LexerIfdefStack = 
      match x with 
      | LexerEndlineContinuation.Token(ifd) 
      | LexerEndlineContinuation.Skip(ifd, _, _) -> ifd
          
/// The parser defines a number of tokens for whitespace and
/// comments eliminated by the lexer.  These carry a specification of
/// a continuation for the lexer for continued processing after we've dealt with
/// the whitespace.
[<RequireQualifiedAccess>]
[<NoComparison; NoEquality>]
type LexerWhitespaceContinuation = 
    | Token            of LexerIfdefStackEntries
    | IfDefSkip        of LexerIfdefStackEntries * int * range
    | String           of LexerIfdefStackEntries * range
    | VerbatimString   of LexerIfdefStackEntries * range
    | TripleQuoteString of LexerIfdefStackEntries * range
    | Comment          of LexerIfdefStackEntries * int * range
    | SingleLineComment of LexerIfdefStackEntries * int * range
    | StringInComment    of LexerIfdefStackEntries * int * range
    | VerbatimStringInComment   of LexerIfdefStackEntries * int * range
    | TripleQuoteStringInComment   of LexerIfdefStackEntries * int * range
    | MLOnly            of LexerIfdefStackEntries * range
    | EndLine           of LexerEndlineContinuation
    
    member x.LexerIfdefStack =
        match x with 
        | LexCont.Token ifd
        | LexCont.IfDefSkip (ifd,_,_)
        | LexCont.String (ifd,_)
        | LexCont.VerbatimString (ifd,_)
        | LexCont.Comment (ifd,_,_)
        | LexCont.SingleLineComment (ifd,_,_)
        | LexCont.TripleQuoteString (ifd,_)
        | LexCont.StringInComment (ifd,_,_)
        | LexCont.VerbatimStringInComment (ifd,_,_)
        | LexCont.TripleQuoteStringInComment (ifd,_,_)
        | LexCont.MLOnly (ifd,_) -> ifd
        | LexCont.EndLine endl -> endl.LexerIfdefStack

and LexCont = LexerWhitespaceContinuation

//------------------------------------------------------------------------
// Parser/Lexer state
//------------------------------------------------------------------------

/// The error raised by the parse_error_rich function, which is called by the parser engine
/// when a syntax error occurs. The first object is the ParseErrorContext which contains a dump of
/// information about the grammar at the point where the error occured, e.g. what tokens
/// are valid to shift next at that point in the grammar. This information is processed in build.fs.
[<NoEquality; NoComparison>]
exception SyntaxError of obj (* ParseErrorContext<_> *) * range

/// Get an F# compiler position from a lexer position
let posOfLexPosition (p:Position) = 
    mkPos p.Line p.Column

/// Get an F# compiler range from a lexer range
let mkSynRange (p1:Position) (p2: Position) = 
    mkFileIndexRange p1.FileIndex (posOfLexPosition p1) (posOfLexPosition p2)

type LexBuffer<'Char> with 
    member lexbuf.LexemeRange  = mkSynRange lexbuf.StartPos lexbuf.EndPos

/// Get the range corresponding to the result of a grammar rule while it is being reduced
let lhs (parseState: IParseState) = 
    let p1 = parseState.ResultStartPosition
    let p2 = parseState.ResultEndPosition
    mkSynRange p1 p2

/// Get the range covering two of the r.h.s. symbols of a grammar rule while it is being reduced
let rhs2 (parseState: IParseState) i j = 
    let p1 = parseState.InputStartPosition i
    let p2 = parseState.InputEndPosition j
    mkSynRange p1 p2

/// Get the range corresponding to one of the r.h.s. symbols of a grammar rule while it is being reduced
let rhs parseState i = rhs2 parseState i i 

type IParseState with 
    member x.GetSynArgNameGenerator() = 
        let key = "SynArgNameGenerator"
        let bls = x.LexBuffer.BufferLocalStore
        if not (bls.ContainsKey key) then  
            bls.[key] <- box (SynArgNameGenerator())
        bls.[key] :?> SynArgNameGenerator

    member x.ResetSynArgNameGenerator() = x.GetSynArgNameGenerator().Reset()


/// XmlDoc F# lexer/parser state, held in the BufferLocalStore for the lexer.
/// This is the only use of the lexer BufferLocalStore in the codebase.
module LexbufLocalXmlDocStore = 
    // The key into the BufferLocalStore used to hold the current accumulated XmlDoc lines 
    let private xmlDocKey = "XmlDoc"

    let ClearXmlDoc (lexbuf:Lexbuf) = 
        lexbuf.BufferLocalStore.[xmlDocKey] <- box (XmlDocCollector())

    /// Called from the lexer to save a single line of XML doc comment.
    let SaveXmlDocLine (lexbuf:Lexbuf, lineText, pos) = 
        if not (lexbuf.BufferLocalStore.ContainsKey(xmlDocKey)) then 
            lexbuf.BufferLocalStore.[xmlDocKey] <- box (XmlDocCollector())
        let collector = unbox<XmlDocCollector>(lexbuf.BufferLocalStore.[xmlDocKey])
        collector.AddXmlDocLine (lineText, pos)

    /// Called from the parser each time we parse a construct that marks the end of an XML doc comment range,
    /// e.g. a 'type' declaration. The markerRange is the range of the keyword that delimits the construct.
    let GrabXmlDocBeforeMarker (lexbuf:Lexbuf, markerRange:range)  = 
        if lexbuf.BufferLocalStore.ContainsKey(xmlDocKey) then 
            PreXmlDoc.CreateFromGrabPoint(unbox<XmlDocCollector>(lexbuf.BufferLocalStore.[xmlDocKey]),markerRange.End)
        else
            PreXmlDoc.Empty


   
/// Generates compiler-generated names. Each name generated also includes the StartLine number of the range passed in
/// at the point of first generation.
type NiceNameGenerator() = 

    let basicNameCounts = new Dictionary<string,int>(100)

    member x.FreshCompilerGeneratedName (name,m:range) =
        let basicName = GetBasicNameOfPossibleCompilerGeneratedName name
        let n = (if basicNameCounts.ContainsKey basicName then basicNameCounts.[basicName] else 0) 
        let nm = CompilerGeneratedNameSuffix basicName (string m.StartLine + (match n with 0 -> "" | n -> "-" + string n))
        basicNameCounts.[basicName] <- n+1
        nm

    member x.Reset () = basicNameCounts.Clear()

   

/// Generates compiler-generated names marked up with a source code location, but if given the same unique value then
/// return precisely the same name. Each name generated also includes the StartLine number of the range passed in
/// at the point of first generation.
type StableNiceNameGenerator() = 

    let names = new Dictionary<(string * int64),string>(100)
    let basicNameCounts = new Dictionary<string,int>(100)

    member x.GetUniqueCompilerGeneratedName (name,m:range,uniq) =
        let basicName = GetBasicNameOfPossibleCompilerGeneratedName name
        if names.ContainsKey (basicName,uniq) then
            names.[(basicName,uniq)]
        else 
            let n = (if basicNameCounts.ContainsKey basicName then basicNameCounts.[basicName] else 0) 
            let nm = CompilerGeneratedNameSuffix basicName (string m.StartLine + (match n with 0 -> "" | n -> "-" + string n))
            names.[(basicName,uniq)] <- nm
            basicNameCounts.[basicName] <- n+1
            nm

    member x.Reset () = 
        basicNameCounts.Clear()
        names.Clear()



let rec synExprContainsError inpExpr = 
    let rec walkBind (Binding(_, _, _, _, _, _, _, _, _, synExpr, _, _)) = walkExpr synExpr
    and walkExprs es = es |> List.exists walkExpr
    and walkBinds es = es |> List.exists walkBind
    and walkMatchClauses cl = cl |> List.exists (fun (Clause(_,whenExpr,e,_,_)) -> walkExprOpt whenExpr || walkExpr e)
    and walkExprOpt eOpt = eOpt |> Option.exists walkExpr
    and walkExpr e = 
          match e with 
          | SynExpr.FromParseError _
          | SynExpr.DiscardAfterMissingQualificationAfterDot _
          | SynExpr.ArbitraryAfterError _ -> true
          | SynExpr.LongIdent _ 
          | SynExpr.Quote _
          | SynExpr.LibraryOnlyILAssembly _
          | SynExpr.LibraryOnlyStaticOptimization _
          | SynExpr.Null _
          | SynExpr.Ident _
          | SynExpr.ImplicitZero _
          | SynExpr.Const _ -> false

          | SynExpr.TypeTest (e,_,_)
          | SynExpr.Upcast (e,_,_)
          | SynExpr.AddressOf (_,e,_,_)
          | SynExpr.CompExpr (_,_,e,_) 
          | SynExpr.ArrayOrListOfSeqExpr (_,e,_)
          | SynExpr.Typed (e,_,_)
          | SynExpr.FromParseError (e,_) 
          | SynExpr.Do (e,_)
          | SynExpr.Assert (e,_)
          | SynExpr.DotGet (e,_,_,_) 
          | SynExpr.LongIdentSet (_,e,_)
          | SynExpr.New (_,_,e,_) 
          | SynExpr.TypeApp (e,_,_,_,_,_,_) 
          | SynExpr.LibraryOnlyUnionCaseFieldGet (e,_,_,_) 
          | SynExpr.Downcast (e,_,_)
          | SynExpr.InferredUpcast (e,_)
          | SynExpr.InferredDowncast (e,_)
          | SynExpr.Lazy (e, _)
          | SynExpr.TraitCall(_,_,e,_)
          | SynExpr.YieldOrReturn (_,e,_)
          | SynExpr.YieldOrReturnFrom (_,e,_)
          | SynExpr.DoBang  (e,_) 
          | SynExpr.Paren(e,_,_,_) -> 
              walkExpr e

          | SynExpr.NamedIndexedPropertySet (_,e1,e2,_)
          | SynExpr.DotSet (e1,_,e2,_)
          | SynExpr.LibraryOnlyUnionCaseFieldSet (e1,_,_,e2,_)
          | SynExpr.JoinIn (e1,_,e2,_) 
          | SynExpr.App (_,_,e1,e2,_) -> 
              walkExpr e1 || walkExpr e2

          | SynExpr.ArrayOrList (_,es,_)
          | SynExpr.Tuple (es,_,_) -> 
              walkExprs es

          | SynExpr.Record (_,_,fs,_) ->
              let flds = fs |> List.choose (fun (_, v, _) -> v) 
              walkExprs (flds)

          | SynExpr.ObjExpr (_,_,bs,is,_,_) -> 
              walkBinds bs || walkBinds [ for (InterfaceImpl(_,bs,_)) in is do yield! bs  ]
          | SynExpr.ForEach (_,_,_,_,e1,e2,_) 
          | SynExpr.While (_,e1,e2,_) -> 
              walkExpr e1 || walkExpr e2
          | SynExpr.For (_,_,e1,_,e2,e3,_) -> 
              walkExpr e1 || walkExpr e2 || walkExpr e3
          | SynExpr.MatchLambda(_,_,cl,_,_) -> 
              walkMatchClauses cl
          | SynExpr.Lambda (_,_,_,e,_) -> 
              walkExpr e
          | SynExpr.Match (_,e,cl,_,_) ->
              walkExpr e || walkMatchClauses cl
          | SynExpr.LetOrUse (_,_,bs,e,_) -> 
              walkBinds bs || walkExpr e

          | SynExpr.TryWith (e,_,cl,_,_,_,_) -> 
              walkExpr e  || walkMatchClauses cl
                  
          | SynExpr.TryFinally (e1,e2,_,_,_) ->
              walkExpr e1 || walkExpr e2
          | SynExpr.Sequential (_,_,e1,e2,_) -> 
              walkExpr e1 || walkExpr e2
          | SynExpr.IfThenElse (e1,e2,e3opt,_,_,_,_) ->
              walkExpr e1 || walkExpr e2 || walkExprOpt e3opt
          | SynExpr.DotIndexedGet (e1,es,_,_) -> 
              walkExpr e1 || walkExprs es

          | SynExpr.DotIndexedSet (e1,es,e2,_,_,_) ->
              walkExpr e1 || walkExprs es || walkExpr e2
          | SynExpr.DotNamedIndexedPropertySet (e1,_,e2,e3,_) ->
              walkExpr e1 || walkExpr e2 || walkExpr e3

          | SynExpr.LetOrUseBang  (_,_,_,_,e1,e2,_) -> 
              walkExpr e1 || walkExpr e2 
    walkExpr inpExpr