Ctrl K
ComunidadGuías…

interfaces/semgrep_interfaces/ast_generic_v1.atd

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
(* Yoann Padioleau
 *
 * Copyright (C) 2021-2023 r2c
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation, with the
 * special exception on linking described in file license.txt.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
 * license.txt for more details.
*)

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* Type definitions for the generic AST in the ATD format.
 * See semgrep-core/src/core/ast/AST_generic.ml for more information on
 * the generic AST.
 * See https://github.com/ahrefs/atd for more information on ATD.
 *
 * The definitions in this file are used to export the generic AST to JSON
 * so that other languages (e.g., Python, Javascript) can leverage the
 * generic AST. Those definitions can also be exported in a typed form to
 * other typed languages (e.g., Java, Scala, Typescript, Mypy), which
 * provides stronger guarantees when working on the generic AST.
 *
 * DO NOT MODIFY THIS FILE. The "API" defined here should be stable
 * across multiple versions of Semgrep. If you really want to change
 * things, you'll have to define an AST_generic_v2.atd
 *
 * If you do modify this file, please do not use the 'option' type in non-optional
 * fields. Prefer 'nullable' if possible.
 * For example '?foo: int option;' is fine, but not 'foo: int option;'
 * Prefer in that case 'foo: int nullable'.
 * This is because the 'option' type is not supported by atdpy because there
 * is no real option type in Python/Mypy.
 *
 * history: process to atd-ify AST_generic.ml:
 *  - add [] around variants
 *  - add () around tuple types
 *  - s/and/type for mutually recursive types
 *  - remove ref types
 *  - s/wrap/wrap_
 *)

(*****************************************************************************)
(* Parse_info *)
(*****************************************************************************)
(* start of "atd-ification" of pfff/h_program-lang/Parse_info.ml *)

type token_location = {
  str: string; (* the content of the "token" *)
  charpos: int; (* byte position, 0-based *)
  line: int; (* 1-based *)
  column: int; (* 0-based *)
  filename: string;
}

type token = [
  | OriginTok of token_location
  | FakeTok of string
]

(*****************************************************************************)
(* Token (leaf) *)
(*****************************************************************************)
(* start of "atd-ification" of semgrep-core/src/core/ast/AST_generic.ml *)

type tok = token

(* a shortcut to annotate some information with position information *)
(* atd: 'wrap' is a predefined type name in ATD, hence the '_' here *)
type 'a wrap_ = ('a * tok)

(* use for round(), square[], curly{}, and angle<> brackets. *)
type 'a bracket = (tok * 'a * tok)

(* semicolon (FakeTok in languages that do not require them (e.g., Python))*)
type sc = tok

(* an AST element not yet handled *)
type todo_kind = string wrap_

(*****************************************************************************)
(* Names *)
(*****************************************************************************)
type ident = string wrap_

(* usually separated by a '.', but can be used also with '::' separators *)
type dotted_ident = ident list (* at least 1 element *)

(* module_name can also be used for a package name or a namespace *)
type module_name = [
  | DottedName of dotted_ident (* ex: Python *)
  (* in FileName the '/' is similar to the '.' in DottedName *)
  | FileName of string wrap_   (* ex: Js import, C #include, Go import *)
  ]

(* A single unique id: sid (uid would be a better name, but it usually
 * means "user id" for people). See Naming_AST.ml for more information.
*)
type sid = int (* a single unique gensym'ed number *)

type resolved_name = (resolved_name_kind * sid)

(* EXPERIMENTAL *)
type resolved_name_kind = [
  | Global
  | Local
  | Param
  | ImportedEntity of dotted_ident
  | ImportedModule of module_name
  | OtherResolvedNameKind of string
  ]

type name = [
  | Id of (ident * id_info)
  | IdQualified of qualified_info
  ]

type qualified_info = {
  name_last : (ident * type_arguments nullable);
  ?name_middle : qualifier option;
  (* ::, Ruby, C++, also '`' abuse for PolyVariant in OCaml *)
  ?name_top : tok option;
  name_info : id_info;
}

type qualifier = [
  (* Java/C++/Rust *)
  | QDots of (ident * type_arguments nullable) list
  (* Ruby/Lua *)
  | QExpr of (expr * tok)
  ]

(*****************************************************************************)
(* Naming/typing *)
(*****************************************************************************)

(* EXPERIMENTAL *)
type id_info = {
  ?id_resolved: resolved_name option; (* variable tagger (naming) *)
  ?id_type:     type_         option; (* type checker (typing) *)
  ?id_svalue:   svalue    option; (* constant propagation *)
}

(*****************************************************************************)
(* Expression *)
(*****************************************************************************)

type expr = [
  (* basic (atomic) values *)
  | L of literal

  (* composite values *)
  | Container of (container_operator * expr list bracket)
  | Comprehension of (container_operator * comprehension bracket)

  (* And-type (field.vinit should be a Some) *)
  | Record of field list bracket

  (* Or-type (could be used instead of Container, Cons, Nil, etc.).
   * (ab)used also for polymorphic variants where qualifier is QTop with
   * the '`' token.
  *)
  | Constructor of (name * expr list bracket)

  | N of name

  | IdSpecial of special wrap_

  (* operators and function application *)
  | Call of (expr * arguments)

  (* 'type_' below is usually a TyN or TyArray (or TyExpr).
   * Note that certain languages do not have a 'new' keyword
   * (e.g., Python, Scala 3), instead certain 'Call' are really 'New'.
   *)
  | New of (tok (* 'new' (can be fake) *) * type_ * arguments)

  (* (XHP, JSX, TSX), could be transpiled also *)
  | Xml of xml

  (* IntepolatedString of expr list is simulated with a
   * Call(IdSpecial (Concat ...)) *)

  (* The left part should be an lvalue (Id, DotAccess, ArrayAccess, Deref)
   * but it can also be a pattern (Tuple, Container, even Record).
  *)
  | Assign of (expr * tok (* '=', '<-' in OCaml. ':=' Go is AssignOp (Eq) *) *
              expr)

  | AssignOp of (expr * operator wrap_ * expr)
  | LetPattern of (pattern * expr)

  (* can be used for Record, Class, or Module access depending on expr. *)
  | DotAccess of (expr * tok (* ., ::, ->, # *) * field_name)

  (* in Js ArrayAccess is also abused to perform DotAccess (..., FDynamic) *)
  | ArrayAccess of (expr * expr bracket)

  | SliceAccess of (
      expr
    * (expr nullable (* lower *)
       * expr nullable (* upper *)
       * expr nullable (* step *)) bracket
    )

  (* very special value *)
  | Lambda of function_definition
  (* usually an argument of a New (used in Java, Javascript) *)
  | AnonClass of class_definition

  (* a.k.a ternary expression, or regular if in OCaml *)
  | Conditional of (expr * expr * expr)

  | Yield of (tok * expr nullable * bool) (* 'from' for Python *)
  | Await of (tok * expr)

  | Cast of (type_ * expr)
  | Seq of expr list (* at least 2 elements *)

  | Ref   of (tok (* &, address of *) * expr)
  | DeRef of (tok (* '*' in C, '!' or '<-' in OCaml, ^ in Reason *) * expr)

  (* in theory we removed all semgrep-specific constructs but some
   * languages support ellipsis (e.g., Python) *)
  | Ellipsis of tok

  | ParenExpr of expr bracket
  (* Dual of ExprStmt. OCaml/Ruby/Scala/... have just expressions,
   * not separate statements *)
  | StmtExpr of stmt

  | OtherExpr of (todo_kind * any list)
  ]

type literal = [
  | Bool of bool wrap_
  (* the numbers are an option because OCaml numbers (e.g., 63bits int)
   * may not be able to represent all numbers.
  *)
  | Int of int <ocaml repr="int64"> nullable wrap_ | Float of float nullable wrap_
  | Char of string wrap_ | String of string wrap_
  | Regexp of (string wrap_ bracket (* // *)
               * string wrap_ nullable (* modifiers *))
  | Atom of (tok * string wrap_) (* Ruby *)
  | Unit of tok (* a.k.a Void *) | Null of tok | Undefined of tok (* JS *)
  | Imag of string wrap_ (* Go, Python *) | Ratio of string wrap_ (* Ruby *)
  ]

(* The type of an unknown constant. *)
type const_type = [ Cbool | Cint | Cstr | Cany ]

(* semantic value: set by the svalue propagation algorithm *)
type svalue = [ Lit of literal | Cst of const_type | Sym of expr | NotCst ]

type container_operator = [
  | Array
  | List | Set
  | Dict (* a.k.a Hash or Map (combine with Tuple to get Key/value pair) *)
  | Tuple
  ]

(* For Python/HCL (and Haskell later). The 'expr' can be a 'Tuple' to
 * represent a Key/Value pair (like in Container). *)
type comprehension = (expr * for_or_if_comp list)

(* at least one element *)
type for_or_if_comp = [
  | CompFor of (tok (*'for'*) * pattern * tok (* 'in' *) * expr)
  | CompIf of (tok (*'if'*) * expr)
]

type field_name = [
  | FN of name
  (* for PHP/JS fields (even though JS use ArrayAccess for that), or Ruby
   * or C++ ArrowStarAccess ->*
   *)
  | FDynamic of expr
]

(* EXPERIMENTAL *)
type special = [
  (* special vars *)
  | This | Super (* called 'base' in C# *)
  | Self | Parent (* different from This/Super? *)

  (* special calls *)
  | Eval
  | Typeof (* for C? and Go in switch x.(type) *)
  | Instanceof | Sizeof (* takes a ArgType *)
  | Defined (* defined? in Ruby, other? *)

  (* used for interpolated strings constructs *)
  | ConcatString of concat_string_kind
  | EncodedString of string (* only for Python for now (e.g., b"foo") *)
  | InterpolatedElement

  (* "Inline" the content of a var containing a list (a.k.a a Splat in Ruby).
   * Used in a Container or Call argument context.
   * The corresponding constructor in a parameter context is ParamRest.
  *)
  | Spread (* ...x in JS, *x in Python/Ruby *)
  (* Similar to Spread, but for a var containing a hashtbl.
   * The corresponding constructor in a parameter context is ParamHashSplat.
  *)
  | HashSplat (* **x in Python/Ruby
               * (not to confused with Pow below which is a Binary op *)

  | ForOf (* Javascript, for generators, used in ForEach *)

  (* used for unary and binary operations *)
  | Op of operator
  | IncrDecr of (incr_decr * prefix_postfix)
  (* JS: `require('foo')` *)
  | Require
  | OtherSpecial of string
  ]

(* mostly binary operators.
 * Note that Mod can be used for %style string formatting in Python.
 * Note that Plus can also be used for string concatenations in Go/??.
*)
type operator = [
  | Plus (* unary too *) | Minus (* unary too *)
  | Mult | Div | Mod
  | Pow (* ** binary op; for unary see HashSplat above *)
  | FloorDiv | MatMult (* Python *)
  | LSL | LSR | ASR (* L = logic, A = Arithmetic, SL = shift left *)
  | BitOr | BitXor | BitAnd | BitNot (* unary *) | BitClear (* Go *)
  | And | Or (* also shortcut operator *) | Xor (* PHP*)
  | Not (* unary *)
  | Eq (* '=' in OCaml, '==' in Go/... *)
  | NotEq
  | PhysEq (* '==' in OCaml, '===' in JS/... *)
  | NotPhysEq
  | Lt | LtE | Gt | GtE
  | Cmp (* <=>, PHP *)
  | Concat (* '.' PHP, '..' Lua *)
  | Append (* x[] = ... in PHP, just in AssignOp *)
  | RegexpMatch (* =~, Ruby (and Perl) *)
  | NotMatch (* !~ Ruby *)
  | Range (* .. or ..., Ruby, one arg can be nil for endless range *)
  | RangeInclusive (* '..=' in Rust *)
  | NotNullPostfix (* ! in Typescript, postfix operator *)
  | Length (* '#' in Lua *)
  | Elvis (* ?: in Kotlin, can compare possible null value *)
  | Nullish (* ?? in Javascript *)
  | In (* in: checks that value belongs to a collection *) | NotIn (* !in *)
  | Is (* is: checks value has type *) | NotIs (* !is: *)
  (* Shell & and | *)
  | Background
  | Pipe
  | LDA
  | RDA
  | LSA
  | RSA
  ]

type incr_decr = [Incr | Decr] (* '++', '--' *)

type prefix_postfix = [Prefix | Postfix]

(* EXPERIMENTAL *)
type concat_string_kind = [
  (* many languages do not require a special syntax to use interpolated
   * strings e.g. simply "this is {a}". Javascript uses backquotes.
  *)
  | InterpolatedConcat (* Javascript/PHP/Ruby/Perl *)
  (* many languages have a binary Concat operator to concatenate strings,
   * but some languages also allow the simple juxtaposition of multiple
   * strings to be concatenated, e.g. "hello" "world" in Python.
  *)
  | SequenceConcat (* Python/C *)
  (* Python requires the special f"" syntax to use interpolated strings,
   * and some semgrep users may want to explicitely match only f-strings,
   * which is why we record this information here.
  *)
  | FString of string(* Python *)
  (* Javascript uses a special syntax called tagged template literals, e.g.,
   * foo`template string = ${id}`.
   *)
  | TaggedTemplateLiteral
  ]

(* this is for JSX/TSX in javascript land *)
type xml = {
  xml_kind: xml_kind;
  xml_attrs: xml_attribute list;
  xml_body: xml_body list;
}

type xml_kind = [
  | XmlClassic   of (tok (*'<'*) * ident * tok (*'>'*) * tok) (*'</foo>'*)
  | XmlSingleton of (tok (*'<'*) * ident * tok) (* '/>', with xml_body = [] *)
  (* React/JS specific *)
  | XmlFragment of (tok (* '<>' *) * tok) (* '</>', with xml_attrs = [] *)
  ]

type xml_attribute = [
  | XmlAttr of (ident * tok (* = *) * xml_attr_value)
  (* jsx: usually a Spread operation, e.g., <foo {...bar} /> *)
  | XmlAttrExpr of expr bracket
  ]

(* either a String or a bracketed expr, but right now we just use expr *)
type xml_attr_value = expr

type xml_body = [
  | XmlText of string wrap_
  (* this can be None when people abuse {} to put comments in it *)
  | XmlExpr of expr nullable bracket
  | XmlXml of xml
  ]

(* brackets can be fake '()' for OCaml/Ruby *)
type arguments = argument list bracket

type argument = [
  (* regular argument *)
  | Arg of expr (* can be Call (IdSpecial Spread, Id foo) *)
  (* keyword argument *)
  | ArgKwd of (ident * expr)
  (* optional keyword argument - can occur in target code without being
     specified in the pattern *)
  | ArgKwdOptional of (ident * expr)
  (* type argument for New, instanceof/sizeof/typeof, C macros *)
  | ArgType of type_
  | OtherArg of (todo_kind * any list)
  ]

(*****************************************************************************)
(* Statement *)
(*****************************************************************************)

type stmt = [
  | ExprStmt of (expr * sc) (* fake tok in Python, but also in JS/Go with ASI*)

  | Block of stmt list bracket (* can be fake {} in Python where use layout *)

  | If of (tok (* 'if' or 'elif' *) * condition * stmt * stmt nullable)
  | While of (tok * condition * stmt)

  | Return of (tok * expr nullable * sc)

  | DoWhile of (tok * stmt * expr)
  | For of (tok (* 'for', 'foreach'*) * for_header * stmt)

  (* The expr can be None for Go and Ruby. *)
  | Switch of (tok (* 'switch' or also 'select' in Go *)
               * condition nullable
               * case_and_body list)

  | Continue of (tok * label_ident * sc)
  | Break of (tok * label_ident * sc)

  | Label of (label * stmt)
  | Goto of (tok * label)

  | Throw of (tok (* 'raise' in OCaml, 'throw' in Java/PHP *) * expr * sc)
  | Try of (tok * stmt * catch list * finally nullable)
  | WithUsingResource of (tok (* 'with' in Python, 'using' in C# *)
                          * stmt list
                          * stmt)
  | Assert of (tok * arguments * sc)

  | DefStmt of definition
  | DirectiveStmt of directive

  | OtherStmt of (todo_kind * any list)
  ]

type condition = [
  | Cond of expr
  | OtherCond of (todo_kind * any list)
]

type case_and_body = [
  | CasesAndBody of (case list * stmt)
  ]

type case  = [
  | Case    of (tok * pattern)
  | Default of tok
  (* For Go, expr can contain some Assign bindings. *)
  | CaseEqualExpr of (tok * expr)
  | OtherCase of (todo_kind * any list)
  ]

type catch = (tok (* 'catch', 'except' in Python *) * catch_exn * stmt)

type catch_exn = [
  | CatchPattern of pattern
  (* for Java/C++/PHP/etc. *)
  | CatchParam of parameter_classic
  | OtherCatch of (todo_kind * any list)
]

type finally = (tok (* 'finally' *) * stmt)

type label = ident

type label_ident = [
  | LNone (* C/Python *)
  | LId of label (* Java/Go *)
  | LInt of int wrap_ (* PHP *)
  | LDynamic of expr (* PHP, woohoo, dynamic break! bailout for CFG *)
  ]

type for_header = [
  | ForClassic of (for_var_or_expr list (* init *) *
                  expr nullable (* cond *) *
                  expr nullable) (* next *)
  | ForEach of for_each
  (* Scala *)
  | MultiForEach of multi_for_each list

  | ForIn of (for_var_or_expr list (* init *) *
             expr list) (* pattern 'in' expr *)
  ]

type for_var_or_expr = [
  | ForInitVar of (entity * variable_definition)
  | ForInitExpr of expr
  ]

type for_each =
  (pattern
   * tok   (* 'in' Python, 'range' Go, 'as' PHP, '' Java *)
   * expr) (* pattern 'in' expr *)

type multi_for_each = [
  | FE of for_each
  | FECond of (for_each * tok * expr)
]

(*****************************************************************************)
(* Pattern *)
(*****************************************************************************)
(* This is quite similar to expr. A few constructs in expr have
 * equivalent here prefixed with Pat (e.g., PaLiteral, PatId).
*)

type pattern = [
  | PatLiteral of literal
  (* Or-Type, used also to match OCaml exceptions *)
  (* Used with Rust path expressions, with an empty pattern list *)
  | PatConstructor of (name * pattern list)
  (* And-Type*)
  | PatRecord of (dotted_ident * pattern) list bracket

  | PatId of (ident * id_info) (* Usually Local/Param, Global in toplevel let*)

  (* special cases of PatConstructor *)
  | PatTuple of pattern list bracket (* at least 2 elements *)
  | PatList of pattern list bracket
  | PatKeyVal of (pattern * pattern) (* a kind of PatTuple *)

  (* special case of PatId *)
  | PatWildcard of tok

  (* OCaml *)
  | PatDisj  of (pattern * pattern) (* also abused for catch in Java *)
  | PatTyped of (pattern * type_)
  | PatWhen  of (pattern * expr)
  | PatAs    of (pattern * (ident * id_info))

  (* For Go also in switch x.(type) { case int: ... } *)
  | PatType of type_

  | OtherPat of (todo_kind * any list)
  ]

(*****************************************************************************)
(* Type *)
(*****************************************************************************)

type type_ = [
  | TyN of name
  (* covers tuples, list, etc. *)
  | TyApply of (type_ * type_arguments)

  | TyFun of (parameter list * type_ (* return type *))

  (* a special case of TApply, also a special case of TPointer *)
  | TyArray of ((* const_expr *) expr nullable bracket * type_)
  | TyTuple of type_ list bracket

  | TyVar of ident (* type variable in polymorphic types (not a typedef) *)
  | TyAny of tok (* anonymous type, '_' in OCaml *)

  | TyPointer of (tok * type_)
  | TyRef of (tok * type_) (* C++/Rust *)

  | TyQuestion of (type_ * tok) (* a.k.a option type *)
  | TyRest of (tok * type_) (* '...foo' e.g. in a typescript tuple type *)

  (* intersection types, used for Java Cast, and in Typescript *)
  | TyAnd of (type_ * tok (* & *) * type_)
  (* union types in Typescript *)
  | TyOr of (type_ * tok (* | *) * type_)

  (* Anonymous record type, a.k.a shape in PHP/Hack. See also AndType.
   * Most record types are defined via a TypeDef and are then referenced
   * via a TyName. Here we have flexible record types (a.k.a. rows in OCaml).
  *)
  | TyRecordAnon of (class_kind wrap_ (* 'struct/shape', fake in other *)
                     * field list bracket)

  (* For languages such as Python which abuse expr to represent types.*)
  | TyExpr of expr
  | OtherType of (todo_kind * any list)
  ]

type type_arguments = type_argument list bracket

type type_argument = [
  | TA of type_
  (* Java only *)
  | TAWildcard of (
        tok (* '?' *)
      * (bool wrap_ (* extends|super, true=super *) * type_) nullable
    )
  (* C++/Rust (Rust restrict expr to literals and ConstBlock) *)
  | TAExpr of expr
  | OtherTypeArg of (todo_kind * any list)
]

(*****************************************************************************)
(* Attribute *)
(*****************************************************************************)
(* a.k.a decorators, annotations *)

type attribute = [
  | KeywordAttr of keyword_attribute wrap_
  (* for general @annotations. *)
  | NamedAttr of (tok (* @ *) * name * arguments)

  | OtherAttribute of (todo_kind * any list)
  ]

type keyword_attribute = [
  | Static | Volatile | Extern
  (* for class fields *)
  | Public | Private | Protected
  | Abstract | Final
  | Override (* override *)
  (* for classes (mostly for JVM languages) *)
  (* Scala 'case class', Java 'record', Kotlin 'data class' *)
  | RecordClass
  (* '@interface' in Java, 'annotation class' in Kotlin *)
  | AnnotationClass
  | EnumClass
  (* for Scala and Java *)
  | SealedClass
  (* for vars (JS) *)
  | Var | Let
  (* for fields (kinda types) *)
  | Mutable | Const (* a.k.a 'readonly' in Typescript *)
  | Optional (* Typescript '?' *) | NotNull (* Typescript '!' *)
  (* for functions *)
  | Recursive | MutuallyRecursive
  | Generator (* '*' in JS *) | Async
  | Inline
  (* for methods *)
  | Ctor | Dtor
  | Getter | Setter
  (* Rust *)
  | Unsafe
  | DefaultImpl (* unstable, RFC 1210 *)
  (* Scala and Swift *)
  | Lazy
  (* Swift *)
  | Throws | Rethrows
  | OtherKeyword of string
  ]

(*****************************************************************************)
(* Definitions *)
(*****************************************************************************)
(* definition (or just declaration sometimes) *)

type definition = (entity * definition_kind)

type entity = {
  (* In Ruby you can define a class with a qualified name as in
   * class A::B::C, and even dynamically.
   * In C++ you can define a method with a class qualifier outside a class,
   * hence the use of entity_name below and not just ident.
  *)
  name: entity_name;
  attrs: attribute list;
  tparams: type_parameters;
}

type entity_name = [
  | EN of name
  | EDynamic of expr
  | EPattern of pattern

  | OtherEntity of (todo_kind * any list)
]

type definition_kind = [
  (* can be used also for methods, nested functions, lambdas. *)
  | FuncDef   of function_definition
  (* can be used also for constants. *)
  | VarDef    of variable_definition

  | ClassDef  of class_definition

  (* just inside a ClassDef with EnumClass *)
  | EnumEntryDef of enum_entry_definition

  | TypeDef   of type_definition

  | ModuleDef of module_definition
  | MacroDef of macro_definition

  (* in a header file (e.g., .mli in OCaml or 'module sig') *)
  | Signature of type_

  (* Only used inside a function.
   * Needed for languages without local VarDef (e.g., Python/PHP)
   * where the first use is also its declaration. In that case when we
   * want to access a global we need to disambiguate with creating a new
   * local.
  *)
  | UseOuterDecl of tok (* 'global' or 'nonlocal' in Python, 'use' in PHP *)

  | OtherDef of (todo_kind * any list)
  ]


(* template/generics/polymorphic-type *)
type type_parameter = [
  | TP of type_parameter_classic

  | OtherTypeParam of (todo_kind * any list)
]

type type_parameter_classic = {
  (* note: in Scala the ident can be a wildcard.*)
  tp_id : ident;
  tp_attrs : attribute list;
  (* upper type bounds (must-be-a-subtype-of) *)
  tp_bounds : type_ list;
  (* for Rust/C++. Similar to parameter_classic, but with type here. *)
  tp_default : type_ nullable;
  (* declaration-site variance (Kotlin/Hack/Scala) *)
  tp_variance : variance wrap_ nullable;
}

(* TODO bracket *)
type type_parameters = type_parameter list

(* less: have also Invariant? *)
type variance = [
  (* '+' in Scala/Hack, 'out' in C#/Kotlin *)
  | Covariant
  (* '-' in Scala/Hack, 'in' in C#/Kotlin *)
  | Contravariant
]

(* ------------------------------------------------------------------------- *)
(* Function (or method) definition *)
(* ------------------------------------------------------------------------- *)

type function_definition = {
  fkind: function_kind wrap_;
  fparams: parameters;
  frettype: type_ nullable; (* return type *)
  fbody: function_body;
}

type function_kind = [
  | Function
  | Method
  | LambdaKind
  (* a.k.a short lambdas *)
  | Arrow
  (* for Scala *)
  | BlockCases
  ]

type parameters = parameter list

type parameter = [
  | ParamClassic of parameter_classic

  | ParamPattern of pattern (* in OCaml, but also now JS, and Python2 *)

  | ParamRest of (tok (* '...' in JS, '*' in Python *) * parameter_classic)
  | ParamHashSplat of (tok (* '**' in Python *) * parameter_classic)

  | OtherParam of (todo_kind * any list)
  ]


type parameter_classic = {
  pname:    ident nullable;
  ptype:    type_ nullable;
  pdefault: expr  nullable;

  pattrs: attribute list;

  (* naming *)
  pinfo: id_info; (* Always Param *)
}

type function_body = [
  (* usually just a Block (where the brackets are fake in Ruby/Python/...) *)
  | FBStmt of stmt
  (* used for short lambdas in JS/Python, or regular func in OCaml/... *)
  | FBExpr of expr
  (* C/C++ prototypes or interface method declarations in Go/Java/... *)
  | FBDecl of sc
  (* Partial *)
  | FBNothing
]

(* ------------------------------------------------------------------------- *)
(* Variable definition *)
(* ------------------------------------------------------------------------- *)
(* Also used for constant_definition with attrs = [Const].
 * Also used for field definition in a class (and record).
*)

type variable_definition = {
  ?vinit: expr option;
  ?vtype: type_ option;
}

(* ------------------------------------------------------------------------- *)
(* Type definition *)
(* ------------------------------------------------------------------------- *)

type type_definition = {
  tbody: type_definition_kind;
}

type type_definition_kind = [
  (* Algrebraic data types (ADTs), and basic enums.
   * For enum class see class_definition *)
  | OrType  of or_type_element list
  (* Record definitions (for struct/class, see class_definition).
   * The fields will be defined via a DefStmt (VarDef variable_definition)
   * where the field.vtype should be defined.
   *)
  | AndType of field list bracket

  (* a.k.a typedef in C (and alias type in Go) *)
  | AliasType of type_
  (* Haskell/Hack/Go ('type x foo' vs 'type x = foo' in Go) *)
  | NewType of type_
  (* OCaml/Rust *)
  | AbstractType of tok (* usually a fake token *)
  | Exception of (ident (* same name than entity *) * type_ list)

  | OtherTypeKind of (todo_kind * any list)
  ]


type or_type_element = [
  (* OCaml *)
  | OrConstructor of (ident * type_ list)
  (* C *)
  | OrEnum of (ident * expr nullable)
  (* Java? *)
  | OrUnion of (ident * type_)

  | OtherOr of (todo_kind * any list)
  ]

(* ------------------------------------------------------------------------- *)
(* Object/struct/record/class field definition *)
(* ------------------------------------------------------------------------- *)

(* Field definition and use, for classes, objects, and records.
 * note: I don't call it field_definition because it's used both to
 * define the shape of a field (a definition), and when creating
 * an actual field (a value).
*)
type field = [
  | F of stmt
]

(* ------------------------------------------------------------------------- *)
(* Class definition *)
(* ------------------------------------------------------------------------- *)

type class_definition = {
  ckind: class_kind wrap_;

  (* usually just one parent, and type_ should be a TyN *)
  cextends:     class_parent list;
  (* class_kind in type_ must be Interface *)
  cimplements:  type_ list;
  (* class_kind in type_ is usually a Trait *)
  cmixins:      type_ list; (* PHP 'uses' *)

  (* for Java Record or Scala Classes; we could transpile them into fields *)
  cparams: parameters;
  cbody: field list bracket;
}

type class_kind = [
  | Class
  | Interface
  | Trait
  (* Kotlin, Scala *)
  | Object
  ]

(* A parent can have arguments in Scala/Java/Kotlin (because constructors
 * can be defined in the class header via cparams and then this class
 * header can call its parent constructor using those cparams).
 *)
type class_parent = (type_ * arguments nullable)

(* ------------------------------------------------------------------------- *)
(* Enum entry  *)
(* ------------------------------------------------------------------------- *)
(* for EnumClass, complex enums-as-classes in Java/Kotlin/Scala? *)
type enum_entry_definition = {
  (* the enum identifier is in the corresponding entity *)
  ee_args : arguments nullable;
  ee_body : field list bracket nullable;
}

(* ------------------------------------------------------------------------- *)
(* Module definition  *)
(* ------------------------------------------------------------------------- *)

type module_definition = {
  mbody: module_definition_kind;
}

type module_definition_kind = [
  | ModuleAlias of dotted_ident
  | ModuleStruct of (dotted_ident nullable * item list)

  | OtherModule of (todo_kind * any list)
  ]

(* ------------------------------------------------------------------------- *)
(* Macro definition *)
(* ------------------------------------------------------------------------- *)
(* Used by cpp in C/C++ *)

type macro_definition = {
  macroparams: ident list;
  macrobody: any list;
}

(*****************************************************************************)
(* Directives (Module import/export, package) *)
(*****************************************************************************)

type directive = [
  | ImportFrom of (
        tok (* 'import'/'from' for Python, 'include' for C *)
      * module_name
      * (ident * alias nullable (* as name alias *)) list
    )

  | ImportAs   of (tok * module_name * alias nullable) (* as name *)
  | ImportAll  of (tok * module_name * tok) (* '.' in Go, '*' in Java/Python *)

  (* packages are different from modules in that multiple files can reuse
   * the same package name; they are agglomarated in the same package
  *)
  | Package of (tok * dotted_ident) (* a.k.a namespace *)
  (* for languages such as C++/PHP with scoped namespaces *)
  | PackageEnd of tok

  | Pragma of (ident * any list)

  | OtherDirective of (todo_kind * any list)
  ]

(* ... as name *)
type alias = (ident * id_info)

(*****************************************************************************)
(* Toplevel *)
(*****************************************************************************)
type item = stmt

type program = item list

(*****************************************************************************)
(* Any *)
(*****************************************************************************)

(* EXPERIMENTAL *)
type any = [
  | E of expr
  | S of stmt
  | T of type_
  | P of pattern
  | At of attribute
  | Fld of field
  | Ar of argument
  | Pa of parameter
  | Ta of type_argument
  | Tp of type_parameter
  | Ce of catch_exn
  | Cs of case
  | ForOrIfComp of for_or_if_comp
  | En of entity
  (* misc *)
  | I of ident
  | Modn of module_name
  | Di of dotted_ident
  | Lbli of label_ident
  | Str of string wrap_
  | Tk of tok
  | TodoK of todo_kind
  | Anys of (any list)
  ]