Full Code of trailofbits/circomspect for AI

main ece9efe0a21e cached
99 files
692.1 KB
163.0k tokens
1364 symbols
1 requests
Download .txt
Showing preview only (728K chars total). Download the full file or copy to clipboard to get everything.
Repository: trailofbits/circomspect
Branch: main
Commit: ece9efe0a21e
Files: 99
Total size: 692.1 KB

Directory structure:
gitextract_dgkomprs/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .rustfmt.toml
├── CHANGELOG.md
├── CODEOWNERS
├── Cargo.toml
├── LICENSE
├── README.md
├── TODO.md
├── circom_algebra/
│   ├── Cargo.toml
│   └── src/
│       ├── lib.rs
│       └── modular_arithmetic.rs
├── cli/
│   ├── Cargo.toml
│   └── src/
│       └── main.rs
├── doc/
│   ├── analysis_passes.md
│   └── demo.cast
├── parser/
│   ├── Cargo.toml
│   ├── build.rs
│   └── src/
│       ├── errors.rs
│       ├── include_logic.rs
│       ├── lang.lalrpop
│       ├── lib.rs
│       ├── parser_logic.rs
│       ├── syntax_sugar_remover.rs
│       └── syntax_sugar_traits.rs
├── program_analysis/
│   ├── Cargo.toml
│   └── src/
│       ├── analysis_context.rs
│       ├── analysis_runner.rs
│       ├── bitwise_complement.rs
│       ├── bn254_specific_circuit.rs
│       ├── config.rs
│       ├── constant_conditional.rs
│       ├── constraint_analysis.rs
│       ├── definition_complexity.rs
│       ├── field_arithmetic.rs
│       ├── field_comparisons.rs
│       ├── lib.rs
│       ├── nonstrict_binary_conversion.rs
│       ├── side_effect_analysis.rs
│       ├── signal_assignments.rs
│       ├── taint_analysis.rs
│       ├── unconstrained_division.rs
│       ├── unconstrained_less_than.rs
│       ├── under_constrained_signals.rs
│       └── unused_output_signal.rs
├── program_structure/
│   ├── Cargo.toml
│   └── src/
│       ├── abstract_syntax_tree/
│       │   ├── assign_op_impl.rs
│       │   ├── ast.rs
│       │   ├── ast_impl.rs
│       │   ├── ast_shortcuts.rs
│       │   ├── expression_builders.rs
│       │   ├── expression_impl.rs
│       │   ├── mod.rs
│       │   ├── statement_builders.rs
│       │   └── statement_impl.rs
│       ├── control_flow_graph/
│       │   ├── basic_block.rs
│       │   ├── cfg.rs
│       │   ├── errors.rs
│       │   ├── lifting.rs
│       │   ├── mod.rs
│       │   ├── parameters.rs
│       │   ├── ssa_impl.rs
│       │   └── unique_vars.rs
│       ├── intermediate_representation/
│       │   ├── declarations.rs
│       │   ├── degree_meta.rs
│       │   ├── errors.rs
│       │   ├── expression_impl.rs
│       │   ├── ir.rs
│       │   ├── lifting.rs
│       │   ├── mod.rs
│       │   ├── statement_impl.rs
│       │   ├── type_meta.rs
│       │   ├── value_meta.rs
│       │   └── variable_meta.rs
│       ├── lib.rs
│       ├── program_library/
│       │   ├── file_definition.rs
│       │   ├── function_data.rs
│       │   ├── mod.rs
│       │   ├── program_archive.rs
│       │   ├── program_merger.rs
│       │   ├── report.rs
│       │   ├── report_code.rs
│       │   ├── template_data.rs
│       │   └── template_library.rs
│       ├── static_single_assignment/
│       │   ├── dominator_tree.rs
│       │   ├── errors.rs
│       │   ├── mod.rs
│       │   └── traits.rs
│       └── utils/
│           ├── constants.rs
│           ├── environment.rs
│           ├── mod.rs
│           ├── nonempty_vec.rs
│           ├── sarif_conversion.rs
│           └── writers.rs
└── program_structure_tests/
    ├── Cargo.toml
    └── src/
        ├── control_flow_graph.rs
        ├── lib.rs
        └── static_single_assignment.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  check:
    name: Check
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v2

      - name: Install stable toolchain
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Run cargo check
        uses: actions-rs/cargo@v1
        with:
          command: check

  test:
    name: Test Suite
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v2

      - name: Install stable toolchain
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Run cargo test
        uses: actions-rs/cargo@v1
        with:
          command: test

  lints:
    name: Lints
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v2

      - name: Install stable toolchain
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true
          components: rustfmt, clippy

      - name: Run cargo fmt
        uses: actions-rs/cargo@v1
        with:
          command: fmt
          args: --all -- --check

      - name: Run cargo clippy
        uses: actions-rs/cargo@v1
        with:
          command: clippy
          args: -- -D warnings


================================================
FILE: .gitignore
================================================
.vscode
/target
/examples


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: local
    hooks:
      - id: rustfmt
        name: rustfmt
        description: Check Rust source code formatting using cargo fmt
        entry: cargo fmt --all -- --check --color always
        language: system
        pass_filenames: false


================================================
FILE: .rustfmt.toml
================================================
fn_params_layout = "Tall"
use_small_heuristics = "Max"
max_width = 100
reorder_modules = false
reorder_imports = false


================================================
FILE: CHANGELOG.md
================================================
# Release Notes

## v0.8.1 (2023-03-21)

-   Updated dependencies flagged by cargo-audit.

## v0.8.0 (2023-03-21)

### Features

-   Circomspect will now only report findings for potential issues in the files
    specified on the command line. (It will still attempt to parse included
    files, but these will only be used to inform the analysis of the files
    specified by the user.)
-   Added support for tags, tuples, and anonymous components. Circomspect now
    supports Circom versions 2.0.0 - 2.1.4.
-   Added templates to the `bn254-specific-circuits` analysis pass.
-   Added `unused-output-signal` analysis pass.
-   All uses of the name BN128 have been replaced with BN254.

### Bug fixes

-   Rewrote the `unconstrained-less-than` analysis pass to better capture the
    underlying issue.
-   Fixed an issue where the cyclomatic complexity calculation could underflow
    in some cases in the `overly-complex-function-or-template` analysis pass.
-   Fixed an issue in the Sarif export implementation where reporting
    descriptors were added multiple times.

## v0.7.2 (2022-12-01)

### Features

-   Added a URL to the issue description for each output.

### Bug Fixes

-   Rewrote description of the unconstrained less-than analysis pass, as the
    previous description was too broad.
-   Fixed grammar in the under-constrained signal warning message.

## v0.7.0 (2022-11-29)

### Features

-   New analysis pass (`unconstrained-less-than`) that detects uses of the
    Circomlib `LessThan` template where the input signals are not constrained
    to be less than the bit size passed to `LessThan`.
-   New analysis pass (`unconstrained-division`) that detects signal
    assignments containing division, where the divisor is not constrained to be
    non-zero.
-   New analysis pass (`bn254-specific-circuits`) that detects uses of
    Circomlib templates with hard-coded BN254-specific constants together with
    a custom curve like BLS12-381 or Goldilocks.
-   New analysis pass (`under-constrained-signal`) that detects intermediate
    signals which do not occur in at least two separate constraints.
-   Rule name is now included in Sarif output. (The rule name is now also
    displayed by the VSCode Sarif extension.)
-   Improved parsing error messages.

### Bug Fixes

-   Fixed an issue during value propagation where values would be propagated to
    arrays by mistake.
-   Fixed an issue in the `nonstrict-binary-conversion` analysis pass where
    some instantiations of `Num2Bits` and `Bits2Num` would not be detected.
-   Fixed an issue where the maximum degree of switch expressions were
    evaluated incorrectly.
-   Previous versions could take a very long time to complete value and degree
    propagation. These analyses are now time boxed and will exit if the
    analysis takes more than 10 seconds to complete.


================================================
FILE: CODEOWNERS
================================================
* @fegge


================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "1"

members = [
  "cli",
  "parser",
  "program_analysis",
  "program_structure",
  "program_structure_tests",
]


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2021 0Kims Association <https://0kims.org>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    {one line to give the program's name and a brief idea of what it does.}
    Copyright (C) {year}  {name of author}

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    {project}  Copyright (C) {year}  {fullname}
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.


================================================
FILE: README.md
================================================
# Circomspect 🔎

![Crates.io badge](https://img.shields.io/crates/v/circomspect.svg) ![GitHub badge](https://github.com/trailofbits/circomspect/actions/workflows/ci.yml/badge.svg)

Circomspect is a static analyzer and linter for the [Circom](https://iden3.io/circom) programming language. The codebase borrows heavily from the Rust Circom compiler built by [iden3](https://github.com/iden3).

Circomspect currently implements a number of analysis passes which can identify potential issues in Circom circuits. It is our goal to continue to add new analysis passes to be able to detect more issues in the future.

![Circomspect example image](https://github.com/trailofbits/circomspect/raw/main/doc/circomspect.png)

## Installing Circomspect

Circomspect is available on [crates.io](https://crates.io/crates/circomspect) and can be installed by invoking

```sh
  cargo install circomspect
```

To build Circomspect from source, simply clone the repository and build the
project by running `cargo build` in the project root. To install from source, use

```sh
  cargo install --path cli
```

## Running Circomspect

To run Circomspect on a file or directory, simply run

```sh
  circomspect path/to/circuit
```

By default, Circomspect outputs warnings and errors to stdout. To see informational results as well you can set the output level using the `--level` option. To ignore certain types of results, you can use the `--allow` option together with the corresponding result ID. (The result ID can be obtained by passing the `--verbose` flag to Circomspect.)

To output the results to a Sarif file (which can be read by the [VSCode Sarif Viewer](https://marketplace.visualstudio.com/items?itemName=MS-SarifVSCode.sarif-viewer)), use the option `--sarif-file`.

![VSCode example image](https://github.com/trailofbits/circomspect/raw/main/doc/vscode.png)

Circomspect supports the same curves that Circom does: BN254, BLS12-381, and Goldilocks. If you are using a different curve than the default (BN254) you can set the curve using the command line option `--curve`.

## Analysis Passes

Circomspect implements analysis passes for a number of different types of issues. A complete list, together with a high-level description of each issue, can be found [here](https://github.com/trailofbits/circomspect/blob/main/doc/analysis_passes.md).


================================================
FILE: TODO.md
================================================
# TODO

  - [x] Implement a basic block type, and functionality allowing us to lift the
        AST to a CFG.
  - [x] Implement `vars_read`, `vars_written`, `signals_read`,
        `signals_written`, and `signals_constrained` on `Statement`.
  - [x] Compute dominators, dominator frontiers, and immediate dominators on
        basic blocks. (See _A Simple, Fast Dominance Algorithm_.)
  - [x] Implement (pruned) SSA.
  - [ ] Implement analyses enabled by SSA:
      - [x] Constant propagation
          - [x] Implement constant propagation.
          - [x] Implement/update `is_constant` and `value` on `Expression`.
      - [x] Dead code analysis
      - [ ] Value-range analysis (simple overflow detection)
      - [x] Intraprocedural data flow
      - [x] Unconstrained signals (simple)
  - [ ] Implement emulation.
      - [ ] Unconstrained signals (specific)
  - [ ] Implement symbolic execution.
      - [ ] Unconstrained signals (complete)
      - [ ] Overflow detection (complete)


# Potential issues

 - [x] Bit level arithmetic does not commute with modular reduction. This means that
     - Currently, `(p | 1) - 1 != 0` (see `circom_algebra/src/modular_arithmetic.rs`)
     - `!x` (256-bit complement) will typically overflow which means that `!x`
       does not satisfy `(!x)_i = x_i ^ 1` for all `i`.

 - [x] Arithmetic is done in `(p/2, p/2]` which may produce unexpected results.
     - E.g. `p/2 + 1 < p/2 - 1`.

 - [ ] Typically you want to constrain all input and output signals for each
       instantiated component in each circuit. There are exceptions from this
       rule (e.g. the circomlib `AliasCheck` template). We should add an
       analysis pass ensuring that signals belonging to instantiated
       subcomponents are properly constrained.

 - [ ] Find cases when it is possible to prove that the output from a component
       is not uniquely determined by the input.


================================================
FILE: circom_algebra/Cargo.toml
================================================
[package]
name = "circomspect-circom-algebra"
version = "2.0.2"
edition = "2021"
rust-version = "1.65"
license = "LGPL-3.0-only"
authors = ["hermeGarcia <hermegarcianavarro@gmail.com>"]
description = "Support crate for the Circomspect static analyzer"
repository = "https://github.com/trailofbits/circomspect"

[dependencies]
num-bigint-dig = "0.8"
num-traits = "0.2"


================================================
FILE: circom_algebra/src/lib.rs
================================================
pub extern crate num_bigint_dig as num_bigint;
pub extern crate num_traits;

pub mod modular_arithmetic;


================================================
FILE: circom_algebra/src/modular_arithmetic.rs
================================================
use num_bigint::{BigInt, ModInverse, Sign};
use num_traits::ToPrimitive;

pub enum ArithmeticError {
    DivisionByZero,
    BitOverFlowInShift,
}

fn modulus(a: &BigInt, b: &BigInt) -> BigInt {
    ((a % b) + b) % b
}
// The maximum number of bits a BigInt can have is 18_446_744_073_709_551_615
// Returns the LITTLE ENDIAN representation of the bigint
fn bit_representation(elem: &BigInt) -> (Sign, Vec<u8>) {
    elem.to_radix_le(2)
}
// Computes 2**b -1 where b is the number of bits of field
fn mask(field: &BigInt) -> BigInt {
    let two = BigInt::from(2);
    let b = bit_representation(field).1.len();
    let mask = num_traits::pow::pow(two, b);
    mask - 1
}

// Arithmetic operations
pub fn add(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    //let left = modulus(left,field);
    //let right = modulus(right,field);
    modulus(&(left + right), field)
}
pub fn mul(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    //let left = modulus(left,field);
    //let right = modulus(right,field);
    modulus(&(left * right), field)
}
pub fn sub(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    //let left = modulus(left,field);
    //let right = modulus(right,field);
    modulus(&(left - right), field)
}
pub fn div(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
    let right_inverse = right.mod_inverse(field).ok_or(ArithmeticError::DivisionByZero)?;
    let res = mul(left, &right_inverse, field);
    Ok(res)
}
pub fn idiv(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
    let zero = BigInt::from(0);
    let left = modulus(left, field);
    let right = modulus(right, field);
    if right == zero {
        Err(ArithmeticError::DivisionByZero)
    } else {
        Ok(left / right)
    }
}
pub fn mod_op(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
    let left = modulus(left, field);
    let right = modulus(right, field);
    Ok(modulus(&left, &right))
}
pub fn pow(base: &BigInt, exp: &BigInt, field: &BigInt) -> BigInt {
    base.modpow(exp, field)
}
pub fn prefix_sub(elem: &BigInt, field: &BigInt) -> BigInt {
    let minus_one = BigInt::from(-1);
    mul(elem, &minus_one, field)
}

//Bit operations

// 256 bit complement
pub fn complement_256(elem: &BigInt, field: &BigInt) -> BigInt {
    let (sign, mut bit_repr) = bit_representation(elem);
    while bit_repr.len() > 256 {
        bit_repr.pop();
    }
    for _i in bit_repr.len()..256 {
        bit_repr.push(0);
    }
    for bit in &mut bit_repr {
        *bit = u8::from(*bit == 0);
    }
    let cp = BigInt::from_radix_le(sign, &bit_repr, 2).unwrap();
    modulus(&cp, field)
}

pub fn shift_l(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
    let two = BigInt::from(2);
    let top = field / &two;
    if right <= &top {
        let usize_repr = right.to_usize().ok_or(ArithmeticError::DivisionByZero)?;
        let value = modulus(&((left * &num_traits::pow(two, usize_repr)) & &mask(field)), field);
        Ok(value)
    } else {
        shift_r(left, &(field - right), field)
    }
}
pub fn shift_r(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigInt, ArithmeticError> {
    let two = BigInt::from(2);
    let top = field / &two;
    if right <= &top {
        let usize_repr = right.to_usize().ok_or(ArithmeticError::DivisionByZero)?;
        let value = left / &num_traits::pow(two, usize_repr);
        Ok(value)
    } else {
        shift_l(left, &(field - right), field)
    }
}
pub fn bit_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    modulus(&(left | right), field)
}
pub fn bit_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    modulus(&(left & right), field)
}
pub fn bit_xor(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    modulus(&(left ^ right), field)
}

// Boolean operations
fn constant_true() -> BigInt {
    BigInt::from(1)
}
fn constant_false() -> BigInt {
    BigInt::from(0)
}
fn val(elem: &BigInt, field: &BigInt) -> BigInt {
    let c = (field / &BigInt::from(2)) + 1;
    if &c <= elem && elem < field {
        elem - field
    } else {
        elem.clone()
    }
}
fn comparable_element(elem: &BigInt, field: &BigInt) -> BigInt {
    val(&modulus(elem, field), field)
}
fn normalize(elem: &BigInt, field: &BigInt) -> BigInt {
    let f = constant_false();
    let t = constant_true();
    if comparable_element(elem, field) == f {
        f
    } else {
        t
    }
}
pub fn as_bool(elem: &BigInt, field: &BigInt) -> bool {
    normalize(elem, field) != constant_false()
}
pub fn not(elem: &BigInt, field: &BigInt) -> BigInt {
    (normalize(elem, field) + 1) % 2
}
pub fn bool_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    (normalize(left, field) + normalize(right, field) + bool_and(left, right, field)) % 2
}
pub fn bool_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    normalize(left, field) * normalize(right, field)
}
pub fn eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    let left = modulus(left, field);
    let right = modulus(right, field);
    if left == right {
        constant_true()
    } else {
        constant_false()
    }
}
pub fn lesser(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    let left = comparable_element(left, field);
    let right = comparable_element(right, field);
    if left < right {
        constant_true()
    } else {
        constant_false()
    }
}
pub fn not_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    not(&eq(left, right, field), field)
}
pub fn lesser_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    bool_or(&lesser(left, right, field), &eq(left, right, field), field)
}
pub fn greater(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    not(&lesser_eq(left, right, field), field)
}
pub fn greater_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
    bool_or(&greater(left, right, field), &eq(left, right, field), field)
}

#[cfg(test)]
mod tests {
    use super::*;
    const FIELD: &str = "257";
    #[test]
    fn mod_check() {
        let a = BigInt::from(-8);
        let b = BigInt::from(5);
        let res = super::modulus(&a, &b);
        assert_eq!(res, BigInt::from(2));
    }
    #[test]
    fn comparison_check() {
        let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
            .expect("generating the big int was not possible");
        let a = sub(&BigInt::from(2), &BigInt::from(1), &field);
        let b = BigInt::from(-1);
        let res = not_eq(&a, &b, &field);
        assert!(as_bool(&res, &field));
    }
    #[test]
    fn mod_operation_check() {
        let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
            .expect("generating the big int was not possible");
        let a = BigInt::from(17);
        let b = BigInt::from(32);
        if let Ok(res) = mod_op(&a, &b, &field) {
            assert_eq!(a, res)
        } else {
            unreachable!();
        }
    }
    #[test]
    fn complement_of_complement_is_the_original_test() {
        let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
            .expect("generating the big int was not possible");
        let big_num = BigInt::parse_bytes("1234".as_bytes(), 10)
            .expect("generating the big int was not possible");
        let big_num_complement = complement_256(&big_num, &field);
        let big_num_complement_complement = complement_256(&big_num_complement, &field);
        let big_num_modulus = modulus(&big_num, &field);
        assert_eq!(big_num_complement_complement, big_num_modulus);
    }
    #[test]
    fn lesser_eq_test() {
        let field = BigInt::parse_bytes(FIELD.as_bytes(), 10)
            .expect("generating the big int was not possible");
        let zero = BigInt::from(0);
        let two = BigInt::from(2);
        assert!(zero < two);
        assert!(as_bool(&lesser_eq(&zero, &two, &field), &field));
    }
}


================================================
FILE: cli/Cargo.toml
================================================
[package]
name = "circomspect"
version = "0.9.0"
edition = "2021"
rust-version = "1.65"
license = "LGPL-3.0-only"
authors = ["Trail of Bits"]
readme = "../README.md"
description = "A static analyzer and linter for the Circom zero-knowledge DSL"
keywords = ["cryptography", "static-analysis", "zero-knowledge", "circom"]
repository = "https://github.com/trailofbits/circomspect"

[dependencies]
anyhow = "1.0"
atty = "0.2"
clap = { version = "4.5", features = ["derive"] }
log = "0.4"
parser = { package = "circomspect-parser", version = "2.1.3", path = "../parser" }
pretty_env_logger = "0.5"
program_analysis = { package = "circomspect-program-analysis", version = "0.8.1", path = "../program_analysis" }
program_structure = { package = "circomspect-program-structure", version = "2.1.3", path = "../program_structure" }
serde_json = "1.0"
termcolor = "1.1"


================================================
FILE: cli/src/main.rs
================================================
use std::collections::HashSet;
use std::path::PathBuf;
use std::process::ExitCode;
use clap::{CommandFactory, Parser};

use program_analysis::config;
use program_analysis::analysis_runner::AnalysisRunner;

use program_structure::constants::Curve;
use program_structure::file_definition::FileID;
use program_structure::report::Report;
use program_structure::report::MessageCategory;
use program_structure::writers::{LogWriter, ReportWriter, SarifWriter, CachedStdoutWriter};

#[derive(Parser, Debug)]
#[command(styles=cli_styles())]
/// A static analyzer and linter for Circom programs.
struct Cli {
    /// Initial input file(s)
    #[clap(name = "INPUT")]
    input_files: Vec<PathBuf>,

    /// Library file paths
    #[clap(short = 'L', long = "library", name = "LIBRARIES")]
    libraries: Vec<PathBuf>,

    /// Output level (INFO, WARNING, or ERROR)
    #[clap(short = 'l', long = "level", name = "LEVEL", default_value = config::DEFAULT_LEVEL)]
    output_level: MessageCategory,

    /// Output analysis results to a Sarif file
    #[clap(short, long, name = "OUTPUT")]
    sarif_file: Option<PathBuf>,

    /// Ignore results from given analysis passes
    #[clap(short = 'a', long = "allow", name = "ID")]
    allow_list: Vec<String>,

    /// Enable verbose output
    #[clap(short = 'v', long = "verbose")]
    verbose: bool,

    /// Set curve (BN254, BLS12_381, or GOLDILOCKS)
    #[clap(short = 'c', long = "curve", name = "NAME", default_value = config::DEFAULT_CURVE)]
    curve: Curve,
}

/// Styles the help output for the [`Cli`].
fn cli_styles() -> clap::builder::Styles {
    use clap::builder::styling::*;

    Styles::styled()
        .header(AnsiColor::Yellow.on_default())
        .usage(AnsiColor::Green.on_default())
        .literal(AnsiColor::Green.on_default())
        .placeholder(AnsiColor::Green.on_default())
}

/// Returns true if a primary location of the report corresponds to a file
/// specified on the command line by the user.
fn filter_by_file(report: &Report, user_inputs: &HashSet<FileID>) -> bool {
    report.primary_file_ids().iter().any(|file_id| user_inputs.contains(file_id))
}

/// Returns true if the report level is greater than or equal to the given
/// level.
fn filter_by_level(report: &Report, output_level: &MessageCategory) -> bool {
    report.category() >= output_level
}

/// Returns true if the report ID is not in the given list.
fn filter_by_id(report: &Report, allow_list: &[String]) -> bool {
    !allow_list.contains(&report.id())
}

fn main() -> ExitCode {
    // Initialize logger and options.
    pretty_env_logger::init();
    let options = Cli::parse();
    if options.input_files.is_empty() {
        match Cli::command().print_help() {
            Ok(()) => return ExitCode::SUCCESS,
            Err(_) => return ExitCode::FAILURE,
        }
    }

    // Set up analysis runner.
    let (mut runner, reports) = AnalysisRunner::new(options.curve)
        .with_libraries(&options.libraries)
        .with_files(&options.input_files);

    // Set up writer and write reports to `stdout`.
    let allow_list = options.allow_list.clone();
    let user_inputs = runner.file_library().user_inputs().clone();
    let mut stdout_writer = CachedStdoutWriter::new(options.verbose)
        .add_filter(move |report: &Report| filter_by_level(report, &options.output_level))
        .add_filter(move |report: &Report| filter_by_file(report, &user_inputs))
        .add_filter(move |report: &Report| filter_by_id(report, &allow_list));
    stdout_writer.write_reports(&reports, runner.file_library());

    // Analyze functions and templates in user provided input files.
    runner.analyze_functions(&mut stdout_writer, true);
    runner.analyze_templates(&mut stdout_writer, true);

    // If a Sarif file is passed to the program we write the reports to it.
    if let Some(sarif_file) = options.sarif_file {
        let allow_list = options.allow_list.clone();
        let user_inputs = runner.file_library().user_inputs().clone();
        let mut sarif_writer = SarifWriter::new(&sarif_file)
            .add_filter(move |report: &Report| filter_by_level(report, &options.output_level))
            .add_filter(move |report: &Report| filter_by_file(report, &user_inputs))
            .add_filter(move |report: &Report| filter_by_id(report, &allow_list));
        if sarif_writer.write_reports(stdout_writer.reports(), runner.file_library()) > 0 {
            stdout_writer.write_message(&format!("Result written to `{}`.", sarif_file.display()));
        }
    }

    // Use the exit code to indicate if any issues were found.
    match stdout_writer.reports_written() {
        0 => {
            stdout_writer.write_message("No issues found.");
            ExitCode::SUCCESS
        }
        1 => {
            stdout_writer.write_message("1 issue found.");
            ExitCode::FAILURE
        }
        n => {
            stdout_writer.write_message(&format!("{n} issues found."));
            ExitCode::FAILURE
        }
    }
}


================================================
FILE: doc/analysis_passes.md
================================================
# Analysis Passes

### Side-effect free assignment

An assigned value which does not contribute either directly or indirectly to a constraint, or a function return value, typically indicates a mistake in the implementation of the circuit. For example, consider the following `BinSum` template from circomlib where we've changed the final constraint to introduce a bug.

```cpp
  template BinSum(n, ops) {
      var nout = nbits((2 ** n - 1) * ops);
      var lin = 0;
      var lout = 0;

      signal input in[ops][n];
      signal output out[nout];

      var e2 = 1;
      for (var k = 0; k < n; k++) {
          for (var j = 0; j < ops; j++) {
              lin += in[j][k] * e2;
          }
          e2 = e2 + e2;
      }

      e2 = 1;
      for (var k = 0; k < nout; k++) {
          out[k] <-- (lin >> k) & 1;
          out[k] * (out[k] - 1) === 0;

          lout += out[k] * e2;  // The value assigned here is not used.
          e2 = e2 + e2;
      }

      lin === nout;  // Should use `lout`, but uses `nout` by mistake.
  }
```

Here, `lout` no longer influences the generated circuit, which is detected by Circomspect.

### Shadowing variable

A shadowing variable declaration is a declaration of a variable with the same name as a previously declared variable. This does not have to be a problem, but if a variable declared in an outer scope is shadowed by mistake, this could change the semantics of the program which would be an issue.

For example, consider this function which is supposed to compute the number of bits needed to represent `a`.

```cpp
  function numberOfBits(a) {
      var n = 1;
      var r = 0;  // Shadowed variable is declared here.
      while (n - 1 < a) {
          var r = r + 1;  // Shadowing declaration here.
          n *= 2;
      }
      return r;
  }
```

Since a new variable `r` is declared in the while-statement body, the outer variable is never updated and the return value is always 0.

### Signal assignment

Signals should typically be assigned using the constraint assignment operator `<==`. This ensures that the circuit and witness generation stay in sync. If `<--` is used it is up to the developer to ensure that the signal is properly constrained. Circomspect will try to detect if the right-hand side of the assignment is a quadratic expression. If it is, the signal assignment can be rewritten using the constraint assignment operator `<==`.

However, sometimes it is not possible to express the assignment using a quadratic expression. In this case Circomspect will try to list all constraints containing the assigned signal to make it easier for the developer (or reviewer) to ensure that the variable is properly constrained.

The Tornado Cash codebase was originally affected by an issue of this type. For details see [the Tornado Cash disclosure](https://tornado-cash.medium.com/tornado-cash-got-hacked-by-us-b1e012a3c9a8).

### Under-constrained signal

Under-constrained signals are one of the most common issues in zero-knowledge circuits. Circomspect will flag intermediate signals that only occur in a single constraint. Since intermediate signals are not available outside the template, this typically indicates an issue with the implementation.

### Unused output signal

When a template is instantiated, the corresponding input signals must be constrained. This is typically also true for the output signals defined by the template, but if we fail to constrain an output signal defined by a template this will not be flagged as an error by the compiler. There are examples (like `Num2Bits` from Circomlib) where the template constrains the input and no further constraints on the output are required. However, in the general case, failing to constrain the output from a template indicates a potential mistake that should be investigated.

Circomspect will generate a warning whenever it identifies an instantiated template where one or more output signals defined by the template are not constrained. Each location can then be manually reviewed for correctness.

This type of issue [was identified by Veridise](https://medium.com/veridise/circom-pairing-a-million-dollar-zk-bug-caught-early-c5624b278f25) during a review of the circom-pairing library.

### Constant branching condition

If a branching statement condition always evaluates to either `true` or `false`, this means that the branch is either always taken, or never taken. This typically indicates a mistake in the code which should be fixed.

### Non-strict binary conversion

Using `Num2Bits` and `Bits2Num` from
[Circomlib](https://github.com/iden3/circomlib) to convert a field element to
and from binary form is only safe if the input size is smaller than the size of
the prime. If not, there may be multiple correct representations of the input
which could cause issues, since we typically expect the circuit output to be
uniquely determined by the input.

For example, suppose that we create a component `n2b` given by `Num2Bits(254)` and set the input to `1`. Now, both the binary representation of `1` _and_ the representation of `p + 1` (where `p` is the order of the underlying finite field) will satisfy the circuit over BN254, since both are 254-bit numbers. If you cannot restrict the input size below the prime size you should use the strict versions `Num2Bits_strict` and `Bits2Num_strict` to convert to and from binary representation. Circomspect will generate a warning if it cannot prove (using constant propagation) that the input size passed to `Num2Bits` or `Bits2Num` is less than the size of the prime in bits.

### Unconstrained less-than

The Circomlib `LessThan` template takes an input size as argument. If the individual input signals are not constrained to be non-negative (for example using the Circomlib `Num2Bits` circuit), it is possible to find inputs `a` and `b` such that `a > b`, but `LessThan` still evaluates to true when given `a` and `b` as inputs.

For example, consider the following template which takes a single input signal
and attempts to constrain it to be less than two.

```cpp
  template LessThanTwo() {
    signal input in;

    component lt = LessThan(8);
    lt.in[0] <== in;
    lt.in[1] <== 2;

    lt.out === 1;
  }
```

Suppose that we define the private input `in` as `p - 254`, where `p` is the prime order of the field. Clearly, `p - 254` is not less than two (at least not when viewed as an unsigned integer), so we would perhaps expect `LessThanTwo` to fail. However, looking at [the implementation](https://github.com/iden3/circomlib/blob/cff5ab6288b55ef23602221694a6a38a0239dcc0/circuits/comparators.circom#L89-L99) of `LessThan`, we see that `lt.out` is given by

```cpp
    1 - n2b.out[8] = 1 - bit 8 of (p - 254 + (1 << 8) - 2) = 1 - 0 = 1.
```

It follows that `p - 254` satisfies `LessThanTwo()`, which is probably not what we expected. Note that, `p - 254` is equal to -254 which _is_ less than two, so there is nothing wrong with the Circomlib `LessThan` circuit. This may just be unexpected behavior if we're thinking of field elements as unsigned integers.

Circomspect will check if the inputs to `LessThan` are constrained to be strictly less than `log(p) - 1` bits using `Num2Bits`. This guarantees that both inputs are non-negative, which avoids this issue. If it cannot prove that both inputs are constrained in this way, a warning is generated.

### Unconstrained division

Since division cannot be expressed directly using a quadratic constraint, it is common to use the following pattern to ensure that the signal `c` is equal to `a / b`.

```cpp
    c <-- a / b;
    c * b === a;
```

This forces `c` to be equal to `a / b` during witness generation, and checks that `c * b = a` during proof verification. However, the statement `c = a / b` only makes sense when `b` is non-zero, whereas `c * b = a` may be true even when `b` is zero. For this reason it is important to also constrain the divisor `b` to ensure that it is non-zero when the proof is verified.

Circomspect will identify signal assignments on the form `c <-- a / b` and ensure that the expression `b` is constrained to be non-zero using the Circomlib `IsZero` template. If no such constraint is found, a warning is emitted.

### BN254 specific circuit

Circom defaults to using the BN254 scalar field (a 254-bit prime field),
but it also supports BSL12-381 (which has a 255-bit scalar field) and
Goldilocks (with a 64-bit scalar field). However, since there are no constants denoting either the prime or the prime size in bits available in the Circom language, some Circomlib templates like `Sign` (which returns the sign of the input signal), and `AliasCheck` (used by the strict versions of `Num2Bits` and `Bits2Num`), hardcode either the BN254 prime size or some other constant related to BN254. Using these circuits with a custom prime may thus lead to unexpected results and should be avoided.

Circomlib templates that may be problematic when used together with curves other than BN254 include the following circuit definitions. (An `x` means that the template should not be used together with the corresponding curve.)

| Template                  | Goldilocks (64 bits) | BLS12-381 (255 bits) |
| :------------------------ | :------------------: | :------------------: |
| `AliasCheck`              |           x          |           x          |
| `BabyPbk`                 |           x          |                      |
| `Bits2Num_strict`         |           x          |           x          |
| `Num2Bits_strict`         |           x          |           x          |
| `CompConstant`            |           x          |           x          |
| `EdDSAVerifier`           |           x          |           x          |
| `EdDSAMiMCVerifier`       |           x          |           x          |
| `EdDSAMiMCSpongeVerifier` |           x          |           x          |
| `EdDSAPoseidonVerifier`   |           x          |           x          |
| `EscalarMulAny`           |           x          |                      |
| `MiMC7`                   |           x          |                      |
| `MultiMiMC7`              |           x          |                      |
| `MiMCFeistel`             |           x          |                      |
| `MiMCSponge`              |           x          |                      |
| `Pedersen`                |           x          |                      |
| `Bits2Point_strict`       |           x          |           x          |
| `Point2Bits_strict`       |           x          |           x          |
| `PoseidonEx`              |           x          |                      |
| `Poseidon`                |           x          |                      |
| `Sign`                    |           x          |           x          |
| `SMTHash1`                |           x          |                      |
| `SMTHash2`                |           x          |                      |
| `SMTProcessor`            |           x          |           x          |
| `SMTProcessorLevel`       |           x          |                      |
| `SMTVerifier`             |           x          |           x          |
| `SMTVerifierLevel`        |           x          |                      |

### Overly complex function or template

As functions and templates grow in complexity they become more difficult to review and maintain. This typically indicates that the code should be refactored into smaller, more easily understandable, components. Circomspect uses cyclomatic complexity to estimate the complexity of each function and template, and will generate a warning if the code is considered too complex. Circomspect will also generate a warning if a function or template takes too many arguments, as this also impacts the readability of the code.

### Bitwise complement

Circom supports taking the 256-bit complement `~x` of a field element `x`. Since the result is reduced modulo `p`, it will typically not satisfy the expected relations `(~x)ᵢ == ~(xᵢ)` for each bit `i`, which could lead to surprising results.

### Field element arithmetic

Circom supports a large number of arithmetic expressions. Since arithmetic expressions can overflow or underflow in Circom it is worth paying extra attention to field arithmetic to ensure that elements are constrained to the correct range.

### Field element comparison

Field elements are normalized to the interval `(-p/2, p/2]` before they are compared, by first reducing them modulo `p` and then mapping them to the correct interval by subtracting `p` from the value `x`, if `x` is greater than `p/2`. In particular, this means that `p/2 + 1 < 0 < p/2 - 1`. This can be surprising if you are used to thinking of elements in `GF(p)` as unsigned integers.


================================================
FILE: doc/demo.cast
================================================
{"version": 2, "width": 114, "height": 35, "timestamp": 1656748733, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
[0.048458, "o", "Restored session: Sat Jul  2 09:56:58 CEST 2022\r\n"]
[0.25259, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m                                                                                                                 \r \r"]
[0.252916, "o", "\u001b]7;file://DELLINSON153567/Users/fredah/Projects/circomspect\u0007"]
[0.40162, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;36mcircomspect\u001b[0m on \u001b[1;35m🌱 \u001b[0m\u001b[1;35mmain\u001b[0m via \u001b[1;31m🦀 \u001b[0m\u001b[1;31mv1.61.0\u001b[0m \r\n\u001b[1;32m❯\u001b[0m \u001b[K\u001b[?2004h"]
[1.660509, "o", "c"]
[1.662497, "o", "\bc\u001b[90mircomspect --output-level INFO examples/unconstrained-signal.circom\u001b[39m\u001b[67D"]
[1.906025, "o", "\bc\u001b[39mi"]
[2.008057, "o", "\u001b[39mr"]
[2.205441, "o", "\u001b[39mc"]
[2.325101, "o", "\u001b[39mo"]
[2.381572, "o", "\u001b[39mm"]
[2.546839, "o", "\u001b[39ms"]
[2.655813, "o", "\u001b[39mp"]
[2.803134, "o", "\u001b[39me"]
[2.970875, "o", "\u001b[39mc"]
[3.193901, "o", "\u001b[39mt"]
[3.282417, "o", "\u001b[39m "]
[3.811358, "o", "\u001b[39m-"]
[3.944232, "o", "\u001b[39m-"]
[4.172232, "o", "\u001b[39mh\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[53D"]
[4.173668, "o", "\u001b[90melp\u001b[39m\b\b\b"]
[4.229432, "o", "\u001b[39me"]
[4.335885, "o", "\u001b[39ml"]
[4.395073, "o", "\u001b[39mp"]
[4.738144, "o", "\u001b[?2004l\r\r\n"]
[4.759646, "o", "circomspect 0.2.1\r\nA static analyzer for Circom programs\r\n\r\nUSAGE:\r\n    circomspect [OPTIONS] <input>\r\n\r\nFLAGS:\r\n    -h, --help       Prints help information\r\n    -V, --version    Prints version information\r\n\r\nOPTIONS:\r\n        --output-level <level>          Output level (either INFO, WARNING, or ERROR) [default: WARNING]\r\n        --sarif-file <output>           Output analysis results to a Sarif file\r\n        --compiler-version <version>    Expected compiler version [default: 2.0.3]\r\n\r\nARGS:\r\n    <input>    Initial input file\r\n"]
[4.760177, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m                                                                                                                 \r \r"]
[4.760835, "o", "\u001b]7;file://DELLINSON153567/Users/fredah/Projects/circomspect\u0007"]
[4.907543, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;36mcircomspect\u001b[0m on \u001b[1;35m🌱 \u001b[0m\u001b[1;35mmain\u001b[0m via \u001b[1;31m🦀 \u001b[0m\u001b[1;31mv1.61.0\u001b[0m \r\n\u001b[1;32m❯\u001b[0m \u001b[K\u001b[?2004h"]
[12.362466, "o", "c"]
[12.364043, "o", "\bc\u001b[90mircomspect --help\u001b[39m\u001b[17D"]
[12.511489, "o", "\bc\u001b[39mi"]
[12.659933, "o", "\u001b[39mr"]
[12.87152, "o", "\u001b[39mc"]
[12.990419, "o", "\u001b[39mo"]
[13.03172, "o", "\u001b[39mm"]
[13.214778, "o", "\u001b[39ms"]
[13.306093, "o", "\u001b[39mp"]
[13.486231, "o", "\u001b[39me"]
[13.651574, "o", "\u001b[39mc"]
[13.905489, "o", "\u001b[39mt"]
[14.025442, "o", "\u001b[39m "]
[14.548997, "o", "\u001b[39m-"]
[14.701112, "o", "\u001b[39m-"]
[15.289916, "o", "\u001b[39mo\u001b[39m \u001b[39m \u001b[39m \b\b\b"]
[15.291958, "o", "\u001b[90mutput-level INFO examples/unconstrained-signal.circom\u001b[39m\u001b[53D"]
[15.391246, "o", "\u001b[39mu"]
[15.480107, "o", "\u001b[39mt"]
[16.032857, "o", "\u001b[39mp\u001b[39mu\u001b[39mt\u001b[39m-\u001b[39ml\u001b[39me\u001b[39mv\u001b[39me\u001b[39ml\u001b[39m \u001b[39mI\u001b[39mN\u001b[39mF\u001b[39mO\u001b[39m \u001b[39me\u001b[39mx\u001b[39ma\u001b[39mm\u001b[39mp\u001b[39ml\u001b[39me\u001b[39ms\u001b[39m/\u001b[39mu\u001b[39mn\u001b[39mc\u001b[39mo\u001b[39mn\u001b[39ms\u001b[39mt\u001b[39mr\u001b[39ma\u001b[39mi\u001b[39mn\u001b[39me\u001b[39md\u001b[39m-\u001b[39ms\u001b[39mi\u001b[39mg\u001b[39mn\u001b[39ma\u001b[39ml\u001b[39m.\u001b[39mc\u001b[39mi\u001b[39mr\u001b[39mc\u001b[39mo\u001b[39mm"]
[16.51743, "o", "\u001b[?2004l\r\r\n"]
[16.542809, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: The variable `value` is assigned a value, but this value is never read.\u001b[0m\r\n   \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":13:5\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m13\u001b[0m \u001b[0m\u001b[34m│\u001b[0m     \u001b[0m\u001b[33mvar value = 0\u001b[0m;\r\n   \u001b[0m\u001b[34m│\u001b[0m     \u001b[0m\u001b[33m^^^^^^^^^^^^^\u001b[0m \u001b[0m\u001b[33mThe value assigned here is never read.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: Using the signal assignment operator `<--` does not constrain the assigned signal.\u001b[0m\r\n   \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":17:9\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m17\u001b[0m \u001b[0m\u001b[34m│\u001b[0m         \u001b[0m\u001b[33mout[i] <-- (in >> i) & 1\u001b[0m;\r\n   \u001b[0m\u001b[34m│\u001b[0m         \u001b[0m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m \u001b[0m\u001b[33mThe assigned signal `out` is not constrained here.\u001b[0m\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n   \u001b[0m\u001b[34m=\u001b[0m Consider using the constraint assignment operator `<==` instead.\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Field element arithmetic could overflow, which may produce unexpected results.\u001b[0m\r\n   \u001b[0m\u001b[34m"]
[16.542959, "o", "┌─\u001b[0m \"examples/unconstrained-signal.circom\":19:9\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m19\u001b[0m \u001b[0m\u001b[34m│\u001b[0m         \u001b[0m\u001b[32mresult += out[i] * power\u001b[0m;\r\n   \u001b[0m\u001b[34m│\u001b[0m         \u001b[0m\u001b[32m^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m \u001b[0m\u001b[32mField element arithmetic here.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Field element arithmetic could overflow, which may produce unexpected results.\u001b[0m\r\n   \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":20:17\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m20\u001b[0m \u001b[0m\u001b[34m│\u001b[0m         power = \u001b[0m\u001b[32mpower + power\u001b[0m;\r\n   \u001b[0m\u001b[34m│\u001b[0m                 \u001b[0m\u001b[32m^^^^^^^^^^^^^\u001b[0m \u001b[0m\u001b[32mField element arithmetic here.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Field element arithmetic could overflow, which may produce unexpected results.\u001b[0m\r\n   \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":16:28\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m16\u001b[0m \u001b[0m\u001b[34m│\u001b[0m     for (var i = 0; i < n; \u001b[0m\u001b[32mi++\u001b[0m) {\r\n   \u001b[0m\u001b[34m│\u001b[0m                            \u001b[0m\u001b[32m^^^\u001b[0m \u001b[0m\u001b[32mF"]
[16.543137, "o", "ield element arithmetic here.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Comparisons with field elements greater than `p/2` may produce unexpected results.\u001b[0m\r\n   \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":16:21\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m16\u001b[0m \u001b[0m\u001b[34m│\u001b[0m     for (var i = 0; \u001b[0m\u001b[32mi < n\u001b[0m; i++) {\r\n   \u001b[0m\u001b[34m│\u001b[0m                     \u001b[0m\u001b[32m^^^^^\u001b[0m \u001b[0m\u001b[32mField element comparison here.\u001b[0m\r\n   \u001b[0m\u001b[34m│\u001b[0m\r\n   \u001b[0m\u001b[34m=\u001b[0m Field elements are always normalized to the interval `(p/2, p/2]` before they are compared.\r\n\r\n"]
[16.543258, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m: Using the signal assignment operator `<--` does not constrain the assigned signal.\u001b[0m\r\n  \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":6:3\r\n  \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m6\u001b[0m \u001b[0m\u001b[34m│\u001b[0m   in + 2 === out;\r\n  \u001b[0m\u001b[34m│\u001b[0m   \u001b[0m\u001b[34m---------------\u001b[0m \u001b[0m\u001b[34mThe signal `out` is constrained here.\u001b[0m\r\n\u001b[0m\u001b[34m7\u001b[0m \u001b[0m\u001b[34m│\u001b[0m   \u001b[0m\u001b[33mout <-- in + 1\u001b[0m;\r\n  \u001b[0m\u001b[34m│\u001b[0m   \u001b[0m\u001b[33m^^^^^^^^^^^^^^\u001b[0m \u001b[0m\u001b[33mThe assigned signal `out` is not constrained here.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Field element arithmetic could overflow, which may produce unexpected results.\u001b[0m\r\n  \u001b[0m\u001b[34m┌─\u001b[0m \"examples/unconstrained-signal.circom\":6:3\r\n  \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m6\u001b[0m \u001b[0m\u001b[34m│\u001b[0m   \u001b[0m\u001b[32min + 2\u001b[0m === out;\r\n  \u001b[0m\u001b[34m│\u001b[0m   \u001b[0m\u001b[32m^^^^^^\u001b[0m \u001b[0m\u001b[32mField element arithmetic here.\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[1m: Field element arithmetic could overflow, which may produce unexpected results.\u001b[0m\r\n  \u001b[0m\u001b[34m┌─"]
[16.543378, "o", "\u001b[0m \"examples/unconstrained-signal.circom\":7:11\r\n  \u001b[0m\u001b[34m│\u001b[0m\r\n\u001b[0m\u001b[34m7\u001b[0m \u001b[0m\u001b[34m│\u001b[0m   out <-- \u001b[0m\u001b[32min + 1\u001b[0m;\r\n  \u001b[0m\u001b[34m│\u001b[0m           \u001b[0m\u001b[32m^^^^^^\u001b[0m \u001b[0m\u001b[32mField element arithmetic here.\u001b[0m\r\n\r\n"]
[16.54405, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m                                                                                                                 \r \r"]
[16.54466, "o", "\u001b]7;file://DELLINSON153567/Users/fredah/Projects/circomspect\u0007"]
[16.686447, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[1;36mcircomspect\u001b[0m on \u001b[1;35m🌱 \u001b[0m\u001b[1;35mmain\u001b[0m via \u001b[1;31m🦀 \u001b[0m\u001b[1;31mv1.61.0\u001b[0m \r\n\u001b[1;32m❯\u001b[0m \u001b[K\u001b[?2004h"]
[28.919242, "o", "\u001b[?2004l\r\r\n"]
[28.922062, "o", "\r\nSaving session..."]
[28.939634, "o", "\r\n...saving history..."]
[28.956811, "o", "truncating history files..."]
[28.970221, "o", "\r\n..."]
[28.970294, "o", "completed.\r\n"]


================================================
FILE: parser/Cargo.toml
================================================
[package]
name = "circomspect-parser"
version = "2.2.0"
edition = "2021"
rust-version = "1.65"
build = "build.rs"
license = "LGPL-3.0-only"
description = "Support crate for the Circomspect static analyzer"
repository = "https://github.com/trailofbits/circomspect"
authors = [
  "Hermenegildo <hermegar@ucm.es>",
  "Fredrik Dahlgren <fredrik.dahlgren@trailofbits.com>",
]

[build-dependencies]
rustc-hex = "2.0"
lalrpop = { version = "0.20", features = ["lexer"] }
num-bigint-dig = "0.8"
num-traits = "0.2"

[dependencies]
program_structure = { package = "circomspect-program-structure", version = "2.1.4", path = "../program_structure" }
lalrpop = { version = "0.20", features = ["lexer"] }
lalrpop-util = "0.20"
log = "0.4"
regex = "1.7"
rustc-hex = "2.1"
num-bigint-dig = "0.8"
num-traits = "0.2"
serde = "1.0"
serde_derive = "1.0"

[dev-dependencies]
program_structure = { package = "circomspect-program-structure", version = "2.1.4", path = "../program_structure" }


================================================
FILE: parser/build.rs
================================================
extern crate lalrpop;

fn main() {
    lalrpop::process_root().unwrap();
}


================================================
FILE: parser/src/errors.rs
================================================
use program_structure::ast::{Meta, Version};
use program_structure::report_code::ReportCode;
use program_structure::report::Report;
use program_structure::file_definition::{FileID, FileLocation};

pub struct UnclosedCommentError {
    pub location: FileLocation,
    pub file_id: FileID,
}

impl UnclosedCommentError {
    pub fn into_report(self) -> Report {
        let mut report = Report::error("Unterminated comment.".to_string(), ReportCode::ParseFail);
        report.add_primary(self.location, self.file_id, "Comment starts here.".to_string());
        report
    }
}

pub struct ParsingError {
    pub location: FileLocation,
    pub file_id: FileID,
    pub message: String,
}

impl ParsingError {
    pub fn into_report(self) -> Report {
        let mut report = Report::error(self.message, ReportCode::ParseFail);
        report.add_primary(
            self.location,
            self.file_id,
            "This token is invalid or unexpected here.".to_string(),
        );
        report
    }
}

pub struct FileOsError {
    pub path: String,
}
impl FileOsError {
    pub fn into_report(self) -> Report {
        Report::error(format!("Failed to open file `{}`.", self.path), ReportCode::ParseFail)
    }
}

pub struct IncludeError {
    pub path: String,
    pub file_id: Option<FileID>,
    pub file_location: FileLocation,
}
impl IncludeError {
    pub fn into_report(self) -> Report {
        let mut report =
            Report::error(format!("Failed to open file `{}`.", self.path), ReportCode::ParseFail);
        if let Some(file_id) = self.file_id {
            report.add_primary(self.file_location, file_id, "File included here.".to_string());
        }
        report
    }
}

pub struct MultipleMainError;
impl MultipleMainError {
    pub fn produce_report() -> Report {
        Report::error(
            "Multiple main components found in the project structure.".to_string(),
            ReportCode::MultipleMainInComponent,
        )
    }
}

pub struct CompilerVersionError {
    pub path: String,
    pub required_version: Version,
    pub version: Version,
}
impl CompilerVersionError {
    pub fn into_report(self) -> Report {
        let message = format!(
            "The file `{}` requires version {}, which is not supported by Circomspect (version {}).",
            self.path,
            version_string(&self.required_version),
            version_string(&self.version),
        );
        Report::error(message, ReportCode::CompilerVersionError)
    }
}

pub struct NoCompilerVersionWarning {
    pub path: String,
    pub version: Version,
}
impl NoCompilerVersionWarning {
    pub fn produce_report(error: Self) -> Report {
        Report::warning(
            format!(
                "The file `{}` does not include a version pragma. Assuming version {}.",
                error.path,
                version_string(&error.version)
            ),
            ReportCode::NoCompilerVersionWarning,
        )
    }
}

pub struct AnonymousComponentError {
    pub meta: Option<Meta>,
    pub message: String,
    pub primary: Option<String>,
}

impl AnonymousComponentError {
    pub fn new(meta: Option<&Meta>, message: &str, primary: Option<&str>) -> Self {
        AnonymousComponentError {
            meta: meta.cloned(),
            message: message.to_string(),
            primary: primary.map(ToString::to_string),
        }
    }

    pub fn into_report(self) -> Report {
        let mut report = Report::error(self.message, ReportCode::AnonymousComponentError);
        if let Some(meta) = self.meta {
            let primary = self.primary.unwrap_or_else(|| "The problem occurs here.".to_string());
            report.add_primary(meta.file_location(), meta.get_file_id(), primary);
        }
        report
    }

    pub fn boxed_report(meta: &Meta, message: &str) -> Box<Report> {
        Box::new(Self::new(Some(meta), message, None).into_report())
    }
}

pub struct TupleError {
    pub meta: Option<Meta>,
    pub message: String,
    pub primary: Option<String>,
}

impl TupleError {
    pub fn new(meta: Option<&Meta>, message: &str, primary: Option<&str>) -> Self {
        TupleError {
            meta: meta.cloned(),
            message: message.to_string(),
            primary: primary.map(ToString::to_string),
        }
    }

    pub fn into_report(self) -> Report {
        let mut report = Report::error(self.message, ReportCode::TupleError);
        if let Some(meta) = self.meta {
            let primary = self.primary.unwrap_or_else(|| "The problem occurs here.".to_string());
            report.add_primary(meta.file_location(), meta.get_file_id(), primary);
        }
        report
    }

    pub fn boxed_report(meta: &Meta, message: &str) -> Box<Report> {
        Box::new(Self::new(Some(meta), message, None).into_report())
    }
}

fn version_string(version: &Version) -> String {
    format!("{}.{}.{}", version.0, version.1, version.2)
}


================================================
FILE: parser/src/include_logic.rs
================================================
use crate::errors::FileOsError;

use log::debug;

use super::errors::IncludeError;
use program_structure::ast::Include;
use program_structure::report::{Report, ReportCollection};
use std::collections::HashSet;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;

pub struct FileStack {
    current_location: Option<PathBuf>,
    black_paths: HashSet<PathBuf>,
    user_inputs: HashSet<PathBuf>,
    libraries: Vec<Library>,
    stack: Vec<PathBuf>,
}

#[derive(Debug)]
struct Library {
    dir: bool,
    path: PathBuf,
}

impl FileStack {
    pub fn new(paths: &[PathBuf], libs: &[PathBuf], reports: &mut ReportCollection) -> FileStack {
        let mut result = FileStack {
            current_location: None,
            black_paths: HashSet::new(),
            user_inputs: HashSet::new(),
            libraries: Vec::new(),
            stack: Vec::new(),
        };
        result.add_libraries(libs, reports);
        result.add_files(paths, reports);
        result.user_inputs = result.stack.iter().cloned().collect::<HashSet<_>>();

        result
    }

    fn add_libraries(&mut self, libs: &[PathBuf], reports: &mut ReportCollection) {
        for path in libs {
            if path.is_dir() {
                self.libraries.push(Library { dir: true, path: path.clone() });
            } else if let Some(extension) = path.extension() {
                // Add Circom files to file stack.
                if extension == "circom" {
                    match fs::canonicalize(path) {
                        Ok(path) => self.libraries.push(Library { dir: false, path: path.clone() }),
                        Err(_) => {
                            reports.push(
                                FileOsError { path: path.display().to_string() }.into_report(),
                            );
                        }
                    }
                }
            }
        }
    }

    fn add_files(&mut self, paths: &[PathBuf], reports: &mut ReportCollection) {
        for path in paths {
            if path.is_dir() {
                // Handle directories on a best effort basis only.
                if let Ok(entries) = fs::read_dir(path) {
                    let paths: Vec<_> = entries.flatten().map(|x| x.path()).collect();
                    self.add_files(&paths, reports);
                }
            } else if let Some(extension) = path.extension() {
                // Add Circom files to file stack.
                if extension == "circom" {
                    match fs::canonicalize(path) {
                        Ok(path) => self.stack.push(path),
                        Err(_) => {
                            reports.push(
                                FileOsError { path: path.display().to_string() }.into_report(),
                            );
                        }
                    }
                }
            }
        }
    }

    pub fn add_include(&mut self, include: &Include) -> Result<(), Box<Report>> {
        let mut location = self.current_location.clone().expect("parsing file");
        location.push(include.path.clone());
        match fs::canonicalize(&location) {
            Ok(path) => {
                if !self.black_paths.contains(&path) {
                    debug!("adding local or absolute include `{}`", location.display());
                    self.stack.push(path);
                }
                Ok(())
            }
            Err(_) => self.include_library(include),
        }
    }

    fn include_library(&mut self, include: &Include) -> Result<(), Box<Report>> {
        // try and perform library resolution on the include
        // at this point any absolute path has been handled by the push in add_include
        let pathos = OsString::from(include.path.clone());
        for lib in &self.libraries {
            if lib.dir {
                // only match relative paths that do not start with .
                if include.path.find('.') == Some(0) {
                    continue;
                }

                let libpath = lib.path.join(&include.path);
                debug!("searching for `{}` in `{}`", include.path, lib.path.display());
                if fs::canonicalize(&libpath).is_ok() {
                    debug!("adding include `{}` from directory", libpath.display());
                    self.stack.push(libpath);
                    return Ok(());
                }
            } else {
                // only match include paths with a single component i.e. lib.circom and not dir/lib.circom or
                // ./lib.circom
                if include.path.find(std::path::MAIN_SEPARATOR).is_none() {
                    debug!("checking if `{}` matches `{}`", include.path, lib.path.display());
                    if lib.path.file_name().expect("good library file") == pathos {
                        debug!("adding include `{}` from file", lib.path.display());
                        self.stack.push(lib.path.clone());
                        return Ok(());
                    }
                }
            }
        }

        let error = IncludeError {
            path: include.path.clone(),
            file_id: include.meta.file_id,
            file_location: include.meta.file_location(),
        };
        Err(Box::new(error.into_report()))
    }

    pub fn take_next(&mut self) -> Option<PathBuf> {
        loop {
            match self.stack.pop() {
                None => {
                    break None;
                }
                Some(file_path) if !self.black_paths.contains(&file_path) => {
                    let mut location = file_path.clone();
                    location.pop();
                    self.current_location = Some(location);
                    self.black_paths.insert(file_path.clone());
                    break Some(file_path);
                }
                _ => {}
            }
        }
    }

    pub fn is_user_input(&self, path: &PathBuf) -> bool {
        self.user_inputs.contains(path)
    }
}


================================================
FILE: parser/src/lang.lalrpop
================================================
use num_bigint::BigInt;
use program_structure::statement_builders::*;
use program_structure::expression_builders::*;
use program_structure::ast::*;
use program_structure::ast_shortcuts::{self, Symbol, TupleInit};
use std::str::FromStr;

grammar;

// ====================================================================
// Body
// ====================================================================

// A identifier list is a comma separated list of identifiers
IdentifierListDef : Vec<String> = {
    <v:(<IDENTIFIER> ",")*> <e:IDENTIFIER> => {
        let mut v = v;
        v.push(e);
        v
    }
};

// Pragma is included at the start of the file.
// Their structure is the following: pragma circom "version of the compiler"
ParsePragma : Version = { // maybe change to usize instead of BigInt
    "pragma circom" <version: Version>  ";"
    => version,
};

// Pragma to indicate that we are allowing the definition of custom templates.
ParseCustomGates : () = {
    "pragma" "custom_templates" ";" => ()
}

// Includes are added at the start of the file.
// Their structure is the following: #include "path to the file"
ParseInclude : Include = {
    <s:@L> "include" <file: STRING>  ";" <e:@L>
    => build_include(Meta::new(s, e), file),
};

// Parsing a program requires:
// Parsing the version pragma, if there is one
// Parsing the custom templates pragma, if there is one
// Parsing "includes" instructions, if there is anyone
// Parsing function and template definitions
// Parsing the declaration of the main component
pub ParseAst : AST = {
    <s:@L> <version: ParsePragma?> <custom_gates: ParseCustomGates?> <includes: ParseInclude*> <definitions: ParseDefinition*> <main: ParseMainComponent?> <e:@R>
    => AST::new(Meta::new(s,e), version, custom_gates.is_some(), includes, definitions, main),
};

// ====================================================================
// Definitions
// ====================================================================

// The private list of the main component stands for the
// list of private input signals
ParsePublicList : Vec<String> = {
    "{" "public" "[" <id: IdentifierListDef> "]" "}" => id,
};

pub ParseMainComponent : MainComponent = {
    <s:@L> "component" "main" <public_list: ParsePublicList?> "=" <init: ParseExpression> ";" <e:@L>
    => match public_list {
        None => build_main_component(Vec::new(),init),
        Some(list) => build_main_component(list,init)
       },
};


pub ParseDefinition : Definition = {
    <s:@L> "function" <name: IDENTIFIER> "(" <args:@L> <arg_names: IdentifierListDef?>  <arge:@R> ")" <body: ParseBlock> <e:@R>
    => match arg_names {
        None
        => build_function(Meta::new(s,e),name,Vec::new(),args..arge,body),
        Some(a)
        => build_function(Meta::new(s,e),name,a,args..arge,body),
    },
    <s:@L> "template" <custom_gate: "custom"?> <parallel: "parallel"?> <name: IDENTIFIER> "(" <args:@L> <arg_names: IdentifierListDef?> <arge:@R> ")" <body: ParseBlock> <e:@R>
    => match arg_names {
        None
        => build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some(), custom_gate.is_some()),
        Some(a)
        => build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some(), custom_gate.is_some()),
    },
};




// ====================================================================
// VariableDefinitions
// ====================================================================

// To generate the list of tags associated to a signal
ParseTagsList : Vec<String> = {
    "{" <id: IdentifierListDef> "}" => id,
};

ParseSignalType: SignalType = {
    "input" => SignalType::Input,
    "output" => SignalType::Output
};

SignalHeader : VariableType = {
    "signal"  <signal_type: ParseSignalType?> <tags_list: ParseTagsList?>
    => {
        let s = match signal_type {
            None => SignalType::Intermediate,
            Some(st) => st,
        };
        let t = match tags_list {
            None => Vec::new(),
            Some(tl) => tl,
        };
        VariableType::Signal(s, t)
    }
};

// ====================================================================
// Statements
// ====================================================================

// A Initialization is either just the name of a variable or
// the name followed by a expression that initialices the variable.
TupleInitialization : TupleInit = {
    "<==" <rhe: ParseExpression> => TupleInit {
        tuple_init : (AssignOp::AssignConstraintSignal, rhe)
    },
    "<--" <rhe: ParseExpression> => TupleInit {
        tuple_init : (AssignOp::AssignSignal, rhe)
    },
    "=" <rhe: ParseExpression> => TupleInit {
        tuple_init : (AssignOp::AssignVar, rhe)
    },
}

SimpleSymbol : Symbol = {
    <name:IDENTIFIER> <dims:ParseArrayAcc*>
    => Symbol {
        name,
        is_array: dims,
        init: None,
    },
}

ComplexSymbol : Symbol = {
    <name:IDENTIFIER> <dims:ParseArrayAcc*> "=" <rhe: ParseExpression>
    => Symbol {
        name,
        is_array: dims,
        init: Some(rhe),
    },
};

SignalConstraintSymbol : Symbol = {
    <name:IDENTIFIER> <dims:ParseArrayAcc*> "<==" <rhe: ParseExpression>
    => Symbol {
        name,
        is_array: dims,
        init: Some(rhe),
    },
};

SignalSimpleSymbol : Symbol = {
    <name:IDENTIFIER> <dims:ParseArrayAcc*> "<--" <rhe: ParseExpression>
    => Symbol {
        name,
        is_array: dims,
        init: Some(rhe),
    },
};


SomeSymbol : Symbol = {
    ComplexSymbol,
    SimpleSymbol,
}

SignalSymbol : Symbol = {
    SimpleSymbol,
    SignalConstraintSymbol,
}

// A declaration is the definition of a type followed by the initialization
ParseDeclaration : Statement = {
    <s:@L> "var" "(" <symbols:(<SimpleSymbol> ",")*> <symbol: SimpleSymbol> ")" <init : TupleInitialization?> <e:@R> => {
        let mut symbols = symbols;
        let meta = Meta::new(s, e);
        let xtype = VariableType::Var;
        symbols.push(symbol);
        ast_shortcuts::split_declaration_into_single_nodes_and_multi_substitution(meta, xtype, symbols, init)
    },
    <s:@L> <xtype:SignalHeader> "(" <symbols:(<SimpleSymbol> ",")*> <symbol: SimpleSymbol> ")" <init : TupleInitialization?>  <e:@R> => {
        let mut symbols = symbols;
        let meta = Meta::new(s, e);
        symbols.push(symbol);
        ast_shortcuts::split_declaration_into_single_nodes_and_multi_substitution(meta, xtype, symbols, init)
    },
    <s:@L> "component" "(" <symbols:(<SimpleSymbol> ",")*> <symbol: SimpleSymbol> ")" <init : TupleInitialization?>  <e:@R> => {
        let mut symbols = symbols;
        let meta = Meta::new(s, e);
        let xtype = VariableType::Component;
        symbols.push(symbol);
        ast_shortcuts::split_declaration_into_single_nodes_and_multi_substitution(meta, xtype, symbols, init)
    },
    <s:@L> "var" <symbols:(<SomeSymbol> ",")*> <symbol: SomeSymbol> <e:@R> => {
            let mut symbols = symbols;
            let meta = Meta::new(s, e);
            let xtype = VariableType::Var;
            symbols.push(symbol);
            ast_shortcuts::split_declaration_into_single_nodes(meta, xtype, symbols, AssignOp::AssignVar)
    },
    <s:@L> "component" <symbols:(<SomeSymbol> ",")*> <symbol: SomeSymbol> <e:@R> => {
            let mut symbols = symbols;
            let meta = Meta::new(s, e);
            let xtype = VariableType::Component;
            symbols.push(symbol);
            ast_shortcuts::split_declaration_into_single_nodes(meta, xtype, symbols, AssignOp::AssignVar)
    },
    <s:@L><xtype: SignalHeader> <symbols:(<SignalSymbol> ",")*> <symbol: SignalSymbol>  <e:@R> => {
            let mut symbols = symbols;
            let meta = Meta::new(s, e);
            symbols.push(symbol);
            ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols,AssignOp::AssignConstraintSignal)
    },
    <s:@L><xtype: SignalHeader> <symbols:(<SignalSimpleSymbol> ",")*> <symbol: SignalSimpleSymbol>  <e:@R> => {
            let mut symbols = symbols;
            let meta = Meta::new(s, e);
            symbols.push(symbol);
            ast_shortcuts::split_declaration_into_single_nodes(meta, xtype, symbols, AssignOp::AssignSignal)
    },
};

ParseSubstitution : Statement = {
    <s:@L> <variable: ParseExpression> <ops: ParseAssignOp> <rhe: ParseExpression> <e:@R> => {
        if let Expression::Variable {meta, name, access} = variable {
            build_substitution(Meta::new(s, e), name, access, ops, rhe)
        } else {
            build_multi_substitution(Meta::new(s, e), variable, ops, rhe)
        }
    },
    <s:@L> <lhe: ParseExpression> "-->" <variable: ParseExpression> <e:@R> => {
        if let Expression::Variable {meta, name, access} = variable {
            build_substitution(Meta::new(s, e), name, access, AssignOp::AssignSignal, lhe)
        } else {
            build_multi_substitution(Meta::new(s, e), variable, AssignOp::AssignSignal, lhe)
        }
    },
    <s:@L> <lhe: ParseExpression> "==>" <variable: ParseExpression>  <e:@R> => {
        if let Expression::Variable {meta, name, access} = variable {
           build_substitution(Meta::new(s, e), name, access, AssignOp::AssignConstraintSignal, lhe)
        } else{
           build_multi_substitution(Meta::new(s, e), variable, AssignOp::AssignConstraintSignal, lhe)
        }
    },
    <s:@L> <variable: ParseVariable>  "\\=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::IntDiv, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "**=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Pow, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "+=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Add, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "-=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Sub, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "*=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Mul, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "/=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Div, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "%=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Mod, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "<<=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::ShiftL, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  ">>=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::ShiftR, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "&=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitAnd, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "|=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitOr, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "^=" <rhe: ParseExpression> <e:@R> =>
        ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitXor, Meta::new(s, e), variable, rhe),

    <s:@L> <variable: ParseVariable>  "++" <e:@R> =>
        ast_shortcuts::plusplus(Meta::new(s,e),variable),

    <s:@L> <variable: ParseVariable>  "--" <e:@R> =>
        ast_shortcuts::subsub(Meta::new(s, e), variable),
};

ParseBlock : Statement = {
    <s:@L> "{" <stmts :ParseStatement3*> "}" <e:@R>
     => build_block(Meta::new(s, e), stmts),
};

pub ParseStatement : Statement = {
    ParseStatement0
};

ParseElse<StmtLevel> : Statement = {
    "else" <else_case: StmtLevel> => else_case,
};

ParseStatement0 : Statement = {
    ParseStmt0NB,
    ParseStatement1
};

ParseStmt0NB : Statement = {
    <s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStmt0NB> <e:@R> =>
        build_conditional_block(Meta::new(s, e), cond, if_case, None),

    <s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <e:@R> =>
        build_conditional_block(Meta::new(s, e), cond, if_case, None),

    <s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <else_case: ParseElse<ParseStmt0NB>><e:@R> =>
        build_conditional_block(Meta::new(s, e), cond, if_case, Some(else_case)),
};

ParseStatement1 : Statement = {
    <s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <else_case: ParseElse<ParseStatement1>><e:@R> =>
        build_conditional_block(Meta::new(s, e), cond, if_case, Some(else_case)),

    ParseStatement2
};

ParseStatement2 : Statement = {
    <s:@L> "for" "(" <init: ParseDeclaration> ";" <cond: ParseExpression> ";" <step: ParseSubstitution> ")" <body: ParseStatement2> <e:@R> =>
        ast_shortcuts::for_into_while(Meta::new(s, e), init, cond, step, body),

    <s:@L> "for" "(" <init: ParseSubstitution> ";" <cond: ParseExpression> ";" <step: ParseSubstitution> ")" <body: ParseStatement2> <e:@R> =>
        ast_shortcuts::for_into_while(Meta::new(s, e), init, cond, step, body),

    <s:@L>"while" "(" <cond: ParseExpression> ")" <stmt: ParseStatement2> <e:@R> =>
        build_while_block(Meta::new(s, e), cond, stmt),

    <s:@L> "return" <value: ParseExpression> ";"<e:@R> =>
        build_return(Meta::new(s, e), value),

    <subs: ParseSubstitution> ";" =>
        subs,

    <s:@L> <lhe: ParseExpression> "===" <rhe: ParseExpression> ";" <e:@R> =>
        build_constraint_equality(Meta::new(s, e), lhe, rhe),

    ParseStatementLog,

    <s:@L> "assert" "(" <arg: ParseExpression> ")" ";" <e:@R> =>
        build_assert(Meta::new(s,e),arg),

    <s:@L> <lhe: ParseExpression> ";" <e:@R> =>
        build_anonymous_component_statement(Meta::new(s, e), lhe),

    ParseBlock
};

ParseStatementLog : Statement = {
    <s:@L> "log" "(" <args: LogListable> ")" ";" <e:@R>
    => build_log_call(Meta::new(s,e),args),

    <s:@L> "log" "(" ")" ";" <e:@R>
    => build_log_call(Meta::new(s,e),Vec::new()),
};

ParseStatement3 : Statement = {
    <dec: ParseDeclaration> ";"
    => dec,

    ParseStatement
};




// ====================================================================
// Variable
// ====================================================================

ParseVarAccess : Access  = {
    <arr_dec: ParseArrayAcc> => build_array_access(arr_dec),
    <component_acc: ParseComponentAcc> => build_component_access(component_acc),
};

ParseArrayAcc: Expression = {
    "["<dim: ParseExpression>"]" => dim
};

ParseComponentAcc: String = {
    "." <id: IDENTIFIER> => id,
};

ParseVariable : (String,Vec<Access>) = {
    <name:IDENTIFIER> <access: ParseVarAccess*>
        => (name, access),
};

// ====================================================================
// Expression
// ====================================================================

Listable: Vec<Expression> = {
    <e:(<ParseExpression> ",")*> <tail: ParseExpression> => {
        let mut e = e;
        e.push(tail);
        e
    },
};

ListableWithInputNames  : (Vec<Expression>,Option<Vec<(AssignOp,String)>>) = {
    <e : (< IDENTIFIER> <ParseAssignOp> < ParseExpression> ",")*>
    <name: IDENTIFIER> <op : ParseAssignOp> <signal: ParseExpression> => {
        let (mut operators_names, mut signals) = unzip_3(e);
        signals.push(signal);
        match operators_names.len() {
            0 => (signals, Option::None),
            _ => { operators_names.push((op,name)); (signals, Option::Some(operators_names))
            }
        }
    }
};

ListableAnon : (Vec<Expression>,Option<Vec<(AssignOp,String)>>) = {
    <l : Listable> => {
        (l, Option::None)
    },

    <l : ListableWithInputNames> =>
        l,
};

ParseString : LogArgument = {
     <e: STRING> =>
        build_log_string(e),
};

ParseLogExp: LogArgument = {
    <e : ParseExpression> =>
        build_log_expression(e),
}

ParseLogArgument : LogArgument = {
    ParseLogExp,
    ParseString
};

LogListable: Vec<LogArgument> = {
    <e:(<ParseLogArgument> ",")*> <tail: ParseLogArgument> => {
        let mut e = e;
        e.push(tail);
        e
    },
};

TwoElemsListable: Vec<Expression> = {
    <head: ParseExpression> "," <head1: ParseExpression> <rest: ("," <ParseExpression>)*>
    => {
        let mut rest = rest;
        let mut new_v = vec![head, head1];
        new_v.append(&mut rest);
        new_v
    },
};

InfixOpTier<Op,NextTier> : Expression = {
    <s:@L> <lhe:InfixOpTier<Op,NextTier>> <infix_op:Op> <rhe:NextTier> <e: @R> =>
        build_infix(Meta::new(s, e), lhe, infix_op, rhe),

    NextTier
};

PrefixOpTier<Op,NextTier >: Expression = {
    <s:@L> <prefix_op:Op> <rhe:NextTier> <e:@R> =>
        build_prefix(Meta::new(s, e), prefix_op, rhe),

    NextTier
};

pub ParseExpression: Expression = {
    Expression14,
    ParseExpression1,
}

pub ParseExpression1: Expression = {
    Expression13,
    Expression12,
};

// parallel expr
Expression14: Expression = {
    <s:@L> "parallel" <expr: ParseExpression1> <e:@L>
    => {
        build_parallel_op(Meta::new(s, e), expr)
    },

}

// ops: e ? a : i
Expression13 : Expression  = {
    <s:@L> <cond: Expression12>  "?" <if_true: Expression12> ":" <if_false: Expression12> <e:@R>
    => build_inline_switch_op(Meta::new(s, e), cond, if_true, if_false),
};

// ops: ||
Expression12 = InfixOpTier<ParseBoolOr, Expression11>;

// ops: &&
Expression11 = InfixOpTier<ParseBoolAnd, Expression10>;

// ops:  == != < > <= >=
Expression10 = InfixOpTier<ParseCmpOpCodes, Expression9>;

// ops: |
Expression9 = InfixOpTier<ParseBitOr, Expression8>;

// ops: ^
Expression8 = InfixOpTier<ParseBitXOR, Expression7>;

// ops: &
Expression7 = InfixOpTier<ParseBitAnd, Expression6>;

// ops: << >>
Expression6 = InfixOpTier<ParseShift, Expression5>;

// ops: + -
Expression5 = InfixOpTier<ParseAddAndSub, Expression4>;

// ops: * / \\ %
Expression4 = InfixOpTier<ParseMulDiv, Expression3>;

// ops: **
Expression3 = InfixOpTier<ParseExp, Expression2>;

// ops: Unary - ! ~
Expression2 = PrefixOpTier<ParseExpressionPrefixOpcode, Expression1>;

// function call, array inline, anonymous component call
Expression1: Expression = {
    <s:@L> <id: IDENTIFIER> "(" <args: Listable?> ")" "(" <args2: ListableAnon?> ")"  <e:@R> => {
        let params = match args {
            None => Vec::new(),
            Some(a) => a
        };
        let (signals, names) = match args2 {
            None => (Vec::new(),Option::None),
            Some(a) => a
        };
        build_anonymous_component(Meta::new(s, e), id, params, signals, names, false)
    },

    <s:@L> <id: IDENTIFIER> "(" <args: Listable?> ")" <e:@R> => match args {
        None => build_call(Meta::new(s, e), id, Vec::new()),
        Some(a) => build_call(Meta::new(s, e), id, a),
    },

    <s:@L> "[" <values: Listable> "]" <e:@R> =>
        build_array_in_line(Meta::new(s, e), values),

    <s:@L> "(" <values: TwoElemsListable> ")" <e:@R> =>
        build_tuple(Meta::new(s,e), values),

    Expression0,
};

// Literal, parentheses
Expression0: Expression = {
    <s:@L> <variable: ParseVariable> <e:@L> => {
        let (name, access) = variable;
        build_variable(Meta::new(s, e), name, access)
    },

    <s:@L> "_" <e:@L> =>
        build_variable(Meta::new(s, e), "_".to_string(), Vec::new()),


    <s:@L> <value:DECNUMBER> <e:@L> =>
        build_number(Meta::new(s, e), value),

    <s:@L> <value:HEXNUMBER> <e:@L> =>
        build_number(Meta::new(s, e), value),

    "(" <ParseExpression> ")"
};

// ====================================================================
// Terminals
// ====================================================================



ParseExpressionPrefixOpcode: ExpressionPrefixOpcode = {
    "!" => ExpressionPrefixOpcode::BoolNot,
    "~" => ExpressionPrefixOpcode::Complement,
    "-" => ExpressionPrefixOpcode::Sub,
};

ParseBoolOr : ExpressionInfixOpcode = {
    "||" => ExpressionInfixOpcode::BoolOr,
};

ParseBoolAnd : ExpressionInfixOpcode = {
    "&&" => ExpressionInfixOpcode::BoolAnd,
};

ParseCmpOpCodes : ExpressionInfixOpcode = {
    "==" => ExpressionInfixOpcode::Eq,
    "!=" => ExpressionInfixOpcode::NotEq,
    "<"  => ExpressionInfixOpcode::Lesser,
    ">"  => ExpressionInfixOpcode::Greater,
    "<=" => ExpressionInfixOpcode::LesserEq,
    ">=" => ExpressionInfixOpcode::GreaterEq,
};

ParseBitOr : ExpressionInfixOpcode = {
    "|" =>  ExpressionInfixOpcode::BitOr,
};

ParseBitAnd : ExpressionInfixOpcode = {
    "&" =>  ExpressionInfixOpcode::BitAnd,
};

ParseShift : ExpressionInfixOpcode = {
    "<<" => ExpressionInfixOpcode::ShiftL,
    ">>" => ExpressionInfixOpcode::ShiftR,
};

ParseAddAndSub : ExpressionInfixOpcode = {
    "+" =>  ExpressionInfixOpcode::Add,
    "-" =>  ExpressionInfixOpcode::Sub,
};

ParseMulDiv : ExpressionInfixOpcode = {
    "*" =>  ExpressionInfixOpcode::Mul,
    "/" =>  ExpressionInfixOpcode::Div,
    "\\" => ExpressionInfixOpcode::IntDiv,
    "%" =>  ExpressionInfixOpcode::Mod,
};

ParseExp : ExpressionInfixOpcode = {
    "**" => ExpressionInfixOpcode::Pow,
};

ParseBitXOR : ExpressionInfixOpcode = {
    "^" =>  ExpressionInfixOpcode::BitXor,
};


ParseAssignOp: AssignOp = {
    "="   => AssignOp::AssignVar,
    "<--" => AssignOp::AssignSignal,
    "<==" => AssignOp::AssignConstraintSignal,
};

DECNUMBER: BigInt = {
    r"[0-9]+" => BigInt::parse_bytes(&<>.as_bytes(),10).expect("failed to parse base10")
};

HEXNUMBER : BigInt = {
    r"0x[0-9A-Fa-f]*" => BigInt::parse_bytes(&(<>.as_bytes()[2..]),16).expect("failed to parse base16")
};

IDENTIFIER : String = {
    r"[$_]*[a-zA-Z][a-zA-Z$_0-9]*" => String::from(<>)
};

STRING : String = {
    <s:r#""[^"]*""#> => String::from(&s[1..s.len()-1])
};

SMALL_DECNUMBER: usize = {
    r"[0-9]+" => usize::from_str(<>).expect("failed to parse number")
};


// Version used by pragma to describe the compiler, its syntax is Number1.Number2.Number3...
Version : Version = {
    <version: SMALL_DECNUMBER> "." <subversion:SMALL_DECNUMBER> "." <subsubversion:SMALL_DECNUMBER> => {
        (version, subversion, subsubversion)
    }
};


================================================
FILE: parser/src/lib.rs
================================================
extern crate num_bigint_dig as num_bigint;
extern crate num_traits;
extern crate serde;
extern crate serde_derive;

#[macro_use]
extern crate lalrpop_util;

// Silence clippy warnings for generated code.
lalrpop_mod!(#[allow(clippy::all)] pub lang);

use log::debug;

mod errors;
mod include_logic;
mod parser_logic;
mod syntax_sugar_traits;
mod syntax_sugar_remover;

pub use parser_logic::parse_definition;

use include_logic::FileStack;
use program_structure::ast::{Version, AST};
use program_structure::report::{Report, ReportCollection};
use program_structure::file_definition::{FileID, FileLibrary};
use program_structure::program_archive::ProgramArchive;
use program_structure::template_library::TemplateLibrary;
use std::collections::HashMap;
use std::path::{Path, PathBuf};

/// A result from the Circom parser.
pub enum ParseResult {
    /// The program was successfully parsed without issues.
    Program(Box<ProgramArchive>, ReportCollection),
    /// The parser failed to parse a complete program.
    Library(Box<TemplateLibrary>, ReportCollection),
}

pub fn parse_files(
    file_paths: &[PathBuf],
    libraries: &[PathBuf],
    compiler_version: &Version,
) -> ParseResult {
    let mut reports = ReportCollection::new();
    let mut file_stack = FileStack::new(file_paths, libraries, &mut reports);
    let mut file_library = FileLibrary::new();
    let mut definitions = HashMap::new();
    let mut main_components = Vec::new();
    while let Some(file_path) = FileStack::take_next(&mut file_stack) {
        match parse_file(&file_path, &mut file_stack, &mut file_library, compiler_version) {
            Ok((file_id, program, mut warnings)) => {
                if let Some(main_component) = program.main_component {
                    main_components.push((file_id, main_component, program.custom_gates));
                }
                debug!(
                    "adding {} definitions from `{}`",
                    program.definitions.iter().map(|x| x.name()).collect::<Vec<_>>().join(", "),
                    file_path.display(),
                );
                definitions.insert(file_id, program.definitions);
                reports.append(&mut warnings);
            }
            Err(error) => {
                reports.push(*error);
            }
        }
    }
    // Create a parse result.
    let mut result = match &main_components[..] {
        [(main_id, main_component, custom_gates)] => {
            // TODO: This calls FillMeta::fill a second time.
            match ProgramArchive::new(
                file_library,
                *main_id,
                main_component,
                &definitions,
                *custom_gates,
            ) {
                Ok(program_archive) => ParseResult::Program(Box::new(program_archive), reports),
                Err((file_library, mut errors)) => {
                    reports.append(&mut errors);
                    let template_library = TemplateLibrary::new(definitions, file_library);
                    ParseResult::Library(Box::new(template_library), reports)
                }
            }
        }
        [] => {
            // TODO: Maybe use a flag to ensure that a main component must be present.
            let template_library = TemplateLibrary::new(definitions, file_library);
            ParseResult::Library(Box::new(template_library), reports)
        }
        _ => {
            reports.push(errors::MultipleMainError::produce_report());
            let template_library = TemplateLibrary::new(definitions, file_library);
            ParseResult::Library(Box::new(template_library), reports)
        }
    };
    // Remove anonymous components and tuples.
    //
    // TODO: This could be moved to the lifting phase.
    match &mut result {
        ParseResult::Program(program_archive, reports) => {
            if program_archive.main_expression().is_anonymous_component() {
                reports.push(
                    errors::AnonymousComponentError::new(
                        Some(program_archive.main_expression().meta()),
                        "The main component cannot contain an anonymous call.",
                        Some("Main component defined here."),
                    )
                    .into_report(),
                );
            }
            let (new_templates, new_functions) = syntax_sugar_remover::remove_syntactic_sugar(
                &program_archive.templates,
                &program_archive.functions,
                &program_archive.file_library,
                reports,
            );
            program_archive.templates = new_templates;
            program_archive.functions = new_functions;
        }
        ParseResult::Library(template_library, reports) => {
            let (new_templates, new_functions) = syntax_sugar_remover::remove_syntactic_sugar(
                &template_library.templates,
                &template_library.functions,
                &template_library.file_library,
                reports,
            );
            template_library.templates = new_templates;
            template_library.functions = new_functions;
        }
    }
    result
}

pub fn parse_file(
    file_path: &PathBuf,
    file_stack: &mut FileStack,
    file_library: &mut FileLibrary,
    compiler_version: &Version,
) -> Result<(FileID, AST, ReportCollection), Box<Report>> {
    let mut reports = ReportCollection::new();

    debug!("reading file `{}`", file_path.display());
    let (path_str, file_content) = open_file(file_path)?;
    let is_user_input = file_stack.is_user_input(file_path);
    let file_id = file_library.add_file(path_str, file_content.clone(), is_user_input);

    debug!("parsing file `{}`", file_path.display());
    let program = parser_logic::parse_file(&file_content, file_id)?;
    match check_compiler_version(file_path, program.compiler_version, compiler_version) {
        Ok(warnings) => reports.extend(warnings),
        Err(error) => reports.push(*error),
    }
    for include in &program.includes {
        if let Err(report) = file_stack.add_include(include) {
            reports.push(*report);
        }
    }
    Ok((file_id, program, reports))
}

fn open_file(file_path: &PathBuf) -> Result<(String, String), Box<Report>> /* path, src*/ {
    use errors::FileOsError;
    use std::fs::read_to_string;
    let path_str = format!("{}", file_path.display());
    read_to_string(file_path)
        .map(|contents| (path_str.clone(), contents))
        .map_err(|_| FileOsError { path: path_str.clone() })
        .map_err(|error| Box::new(error.into_report()))
}

fn check_compiler_version(
    file_path: &Path,
    required_version: Option<Version>,
    compiler_version: &Version,
) -> Result<ReportCollection, Box<Report>> {
    use errors::{CompilerVersionError, NoCompilerVersionWarning};
    if let Some(required_version) = required_version {
        if (required_version.0 == compiler_version.0 && required_version.1 < compiler_version.1)
            || (required_version.0 == compiler_version.0
                && required_version.1 == compiler_version.1
                && required_version.2 <= compiler_version.2)
        {
            Ok(vec![])
        } else {
            let error = CompilerVersionError {
                path: format!("{}", file_path.display()),
                required_version,
                version: *compiler_version,
            };
            Err(Box::new(error.into_report()))
        }
    } else {
        let report = NoCompilerVersionWarning::produce_report(NoCompilerVersionWarning {
            path: format!("{}", file_path.display()),
            version: *compiler_version,
        });
        Ok(vec![report])
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use crate::check_compiler_version;

    #[test]
    fn test_compiler_version() {
        let path = PathBuf::from("example.circom");

        assert!(check_compiler_version(&path, None, &(2, 1, 2)).is_ok());
        assert!(check_compiler_version(&path, Some((2, 0, 0)), &(2, 1, 2)).is_ok());
        assert!(check_compiler_version(&path, Some((2, 0, 8)), &(2, 1, 2)).is_ok());
        assert!(check_compiler_version(&path, Some((2, 1, 2)), &(2, 1, 2)).is_ok());

        // We don't support Circom 1.
        assert!(check_compiler_version(&path, Some((1, 0, 0)), &(2, 0, 8)).is_err());
        assert!(check_compiler_version(&path, Some((2, 1, 2)), &(2, 0, 8)).is_err());
        assert!(check_compiler_version(&path, Some((2, 1, 4)), &(2, 1, 2)).is_err());
    }
}


================================================
FILE: parser/src/parser_logic.rs
================================================
use super::errors::{ParsingError, UnclosedCommentError};
use super::lang;

use program_structure::ast::AST;
use program_structure::report::Report;
use program_structure::file_definition::FileID;

pub fn preprocess(expr: &str, file_id: FileID) -> Result<String, Box<Report>> {
    let mut pp = String::new();
    let mut state = 0;
    let mut loc = 0;
    let mut block_start = 0;

    let mut it = expr.chars();
    while let Some(c0) = it.next() {
        loc += 1;
        match (state, c0) {
            (0, '/') => {
                loc += 1;
                match it.next() {
                    Some('/') => {
                        state = 1;
                        pp.push(' ');
                        pp.push(' ');
                    }
                    Some('*') => {
                        block_start = loc;
                        state = 2;
                        pp.push(' ');
                        pp.push(' ');
                    }
                    Some(c1) => {
                        pp.push(c0);
                        pp.push(c1);
                    }
                    None => {
                        pp.push(c0);
                        break;
                    }
                }
            }
            (0, _) => pp.push(c0),
            (1, '\n') => {
                pp.push(c0);
                state = 0;
            }
            (2, '*') => {
                loc += 1;
                match it.next() {
                    Some('/') => {
                        pp.push(' ');
                        pp.push(' ');
                        state = 0;
                    }
                    Some(c) => {
                        pp.push(' ');
                        for _i in 0..c.len_utf8() {
                            pp.push(' ');
                        }
                    }
                    None => {
                        let error =
                            UnclosedCommentError { location: block_start..block_start, file_id };
                        return Err(Box::new(error.into_report()));
                    }
                }
            }
            (_, c) => {
                for _i in 0..c.len_utf8() {
                    pp.push(' ');
                }
            }
        }
    }
    Ok(pp)
}

pub fn parse_file(src: &str, file_id: FileID) -> Result<AST, Box<Report>> {
    use lalrpop_util::ParseError::*;
    lang::ParseAstParser::new()
        .parse(&preprocess(src, file_id)?)
        .map(|mut ast| {
            // Set file ID for better error reporting.
            for include in &mut ast.includes {
                include.meta.set_file_id(file_id);
            }
            ast
        })
        .map_err(|parse_error| match parse_error {
            InvalidToken { location } => ParsingError {
                file_id,
                message: "Invalid token found.".to_string(),
                location: location..location,
            },
            UnrecognizedToken { ref token, ref expected } => ParsingError {
                file_id,
                message: format!(
                    "Unrecognized token `{}` found.{}",
                    token.1,
                    format_expected(expected)
                ),
                location: token.0..token.2,
            },
            ExtraToken { ref token } => ParsingError {
                file_id,
                message: format!("Extra token `{}` found.", token.2),
                location: token.0..token.2,
            },
            _ => ParsingError { file_id, message: format!("{parse_error}"), location: 0..0 },
        })
        .map_err(|error| Box::new(error.into_report()))
}

pub fn parse_string(src: &str) -> Option<AST> {
    let src = preprocess(src, 0).ok()?;
    lang::ParseAstParser::new().parse(&src).ok()
}

/// Parse a single (function or template) definition for testing purposes.
use program_structure::ast::Definition;

pub fn parse_definition(src: &str) -> Option<Definition> {
    match parse_string(src) {
        Some(AST { mut definitions, .. }) if definitions.len() == 1 => definitions.pop(),
        _ => None,
    }
}

#[must_use]
fn format_expected(tokens: &[String]) -> String {
    if tokens.is_empty() {
        String::new()
    } else {
        let tokens = tokens
            .iter()
            .enumerate()
            .map(|(index, token)| {
                if index == 0 {
                    token.replace('\"', "`")
                } else if index < tokens.len() - 1 {
                    format!(", {}", token.replace('\"', "`"))
                } else {
                    format!(" or {}", token.replace('\"', "`"))
                }
            })
            .collect::<Vec<_>>()
            .join("");
        format!(" Expected one of {tokens}.")
    }
}

#[cfg(test)]
mod tests {
    use super::parse_string;

    #[test]
    fn test_parse_string() {
        let function = r#"
            function f(m) {
                // This is a comment.
                var x = 1024;
                var y = 16;
                while (x < m) {
                    x += y;
                }
                if (x == m) {
                    x = 0;
                }
                /* This is another comment. */
                return x;
            }
        "#;
        let _ = parse_string(function);

        let template = r#"
            template T(m) {
                signal input in[m];
                signal output out;

                var sum = 0;
                for (var i = 0; i < m; i++) {
                    sum += in[i];
                }
                out <== sum;
            }
        "#;
        let _ = parse_string(template);
    }
}


================================================
FILE: parser/src/syntax_sugar_remover.rs
================================================
use program_structure::ast::*;
use program_structure::statement_builders::{build_block, build_substitution};
use program_structure::report::{Report, ReportCollection};
use program_structure::expression_builders::{build_call, build_tuple, build_parallel_op};
use program_structure::file_definition::FileLibrary;
use program_structure::statement_builders::{
    build_declaration, build_log_call, build_assert, build_return, build_constraint_equality,
    build_initialization_block,
};
use program_structure::template_data::TemplateData;
use program_structure::function_data::FunctionData;
use std::collections::HashMap;
use num_bigint::BigInt;

use crate::errors::{AnonymousComponentError, TupleError};
use crate::syntax_sugar_traits::ContainsExpression;

/// This functions desugars all anonymous components and tuples.
#[must_use]
pub(crate) fn remove_syntactic_sugar(
    templates: &HashMap<String, TemplateData>,
    functions: &HashMap<String, FunctionData>,
    file_library: &FileLibrary,
    reports: &mut ReportCollection,
) -> (HashMap<String, TemplateData>, HashMap<String, FunctionData>) {
    // Remove anonymous components and tuples from templates.
    let mut new_templates = HashMap::new();
    for (name, template) in templates {
        let body = template.get_body().clone();
        let (new_body, declarations) =
            match remove_anonymous_from_statement(templates, file_library, body, &None) {
                Ok(result) => result,
                Err(report) => {
                    // If we encounter an error we simply report the error and continue.
                    // This means that the template is dropped and no more analysis is
                    // performed on it.
                    //
                    // TODO: If we want to do inter-procedural analysis we need to track
                    // removed templates.
                    reports.push(*report);
                    continue;
                }
            };
        if let Statement::Block { meta, mut stmts } = new_body {
            let (component_decs, variable_decs, mut substitutions) =
                separate_declarations_in_comp_var_subs(declarations);
            let mut init_block = vec![
                build_initialization_block(meta.clone(), VariableType::Var, variable_decs),
                build_initialization_block(meta.clone(), VariableType::Component, component_decs),
            ];
            init_block.append(&mut substitutions);
            init_block.append(&mut stmts);
            let new_body_with_inits = build_block(meta, init_block);
            let new_body = match remove_tuples_from_statement(new_body_with_inits) {
                Ok(result) => result,
                Err(report) => {
                    // If we encounter an error we simply report the error and continue.
                    // This means that the template is dropped and no more analysis is
                    // performed on it.
                    //
                    // TODO: If we want to do inter-procedural analysis we need to track
                    // removed templates.
                    reports.push(*report);
                    continue;
                }
            };
            let mut new_template = template.clone();
            *new_template.get_mut_body() = new_body;
            new_templates.insert(name.clone(), new_template);
        } else {
            unreachable!()
        }
    }

    // Drop any functions containing anonymous components or tuples.
    let mut new_functions = HashMap::new();
    for (name, function) in functions {
        let body = function.get_body();
        if body.contains_tuple(Some(reports)) {
            continue;
        }
        if body.contains_anonymous_component(Some(reports)) {
            continue;
        }
        new_functions.insert(name.clone(), function.clone());
    }
    (new_templates, new_functions)
}

fn remove_anonymous_from_statement(
    templates: &HashMap<String, TemplateData>,
    file_library: &FileLibrary,
    stmt: Statement,
    var_access: &Option<Expression>,
) -> Result<(Statement, Vec<Statement>), Box<Report>> {
    match stmt {
        Statement::MultiSubstitution { meta, lhe, op, rhe } => {
            if lhe.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    lhe.meta(),
                    "An anonymous component cannot occur as the left-hand side of an assignment",
                ));
            } else {
                let (mut stmts, declarations, new_rhe) =
                    remove_anonymous_from_expression(templates, file_library, rhe, var_access)?;
                let subs =
                    Statement::MultiSubstitution { meta: meta.clone(), lhe, op, rhe: new_rhe };
                let mut substs = Vec::new();
                if stmts.is_empty() {
                    Ok((subs, declarations))
                } else {
                    substs.append(&mut stmts);
                    substs.push(subs);
                    Ok((Statement::Block { meta, stmts: substs }, declarations))
                }
            }
        }
        Statement::IfThenElse { meta, cond, if_case, else_case } => {
            if cond.contains_anonymous_component(None) {
                Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "Anonymous components cannot be used inside conditions.",
                ))
            } else {
                let (new_if_case, mut declarations) =
                    remove_anonymous_from_statement(templates, file_library, *if_case, var_access)?;
                match else_case {
                    Some(else_case) => {
                        let (new_else_case, mut new_declarations) =
                            remove_anonymous_from_statement(
                                templates,
                                file_library,
                                *else_case,
                                var_access,
                            )?;
                        declarations.append(&mut new_declarations);
                        Ok((
                            Statement::IfThenElse {
                                meta,
                                cond,
                                if_case: Box::new(new_if_case),
                                else_case: Some(Box::new(new_else_case)),
                            },
                            declarations,
                        ))
                    }
                    None => Ok((
                        Statement::IfThenElse {
                            meta,
                            cond,
                            if_case: Box::new(new_if_case),
                            else_case: None,
                        },
                        declarations,
                    )),
                }
            }
        }
        Statement::While { meta, cond, stmt } => {
            if cond.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    cond.meta(),
                    "Anonymous components cannot be used inside conditions.",
                ));
            } else {
                let id_var_while = "anon_var_".to_string()
                    + &file_library.get_line(meta.start, meta.get_file_id()).unwrap().to_string()
                    + "_"
                    + &meta.start.to_string();
                let var_access = Expression::Variable {
                    meta: meta.clone(),
                    name: id_var_while.clone(),
                    access: Vec::new(),
                };
                let mut declarations = vec![];
                let (new_stmt, mut new_declarations) = remove_anonymous_from_statement(
                    templates,
                    file_library,
                    *stmt,
                    &Some(var_access.clone()),
                )?;
                let boxed_stmt = if !new_declarations.is_empty() {
                    declarations.push(build_declaration(
                        meta.clone(),
                        VariableType::Var,
                        id_var_while.clone(),
                        Vec::new(),
                    ));
                    declarations.push(build_substitution(
                        meta.clone(),
                        id_var_while.clone(),
                        vec![],
                        AssignOp::AssignVar,
                        Expression::Number(meta.clone(), BigInt::from(0)),
                    ));
                    declarations.append(&mut new_declarations);
                    let next_access = Expression::InfixOp {
                        meta: meta.clone(),
                        infix_op: ExpressionInfixOpcode::Add,
                        lhe: Box::new(var_access),
                        rhe: Box::new(Expression::Number(meta.clone(), BigInt::from(1))),
                    };
                    let subs_access = Statement::Substitution {
                        meta: meta.clone(),
                        var: id_var_while,
                        access: Vec::new(),
                        op: AssignOp::AssignVar,
                        rhe: next_access,
                    };

                    let new_block =
                        Statement::Block { meta: meta.clone(), stmts: vec![new_stmt, subs_access] };
                    Box::new(new_block)
                } else {
                    Box::new(new_stmt)
                };

                Ok((Statement::While { meta, cond, stmt: boxed_stmt }, declarations))
            }
        }
        Statement::LogCall { meta, args } => {
            for arg in &args {
                if let program_structure::ast::LogArgument::LogExp(exp) = arg {
                    if exp.contains_anonymous_component(None) {
                        return Err(AnonymousComponentError::boxed_report(
                            &meta,
                            "An anonymous component cannot be used inside a log statement.",
                        ));
                    }
                }
            }
            Ok((build_log_call(meta, args), Vec::new()))
        }
        Statement::Assert { meta, arg } => Ok((build_assert(meta, arg), Vec::new())),
        Statement::Return { meta, value: arg } => {
            if arg.contains_anonymous_component(None) {
                Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "An anonymous component cannot be used as a return value.",
                ))
            } else {
                Ok((build_return(meta, arg), Vec::new()))
            }
        }
        Statement::ConstraintEquality { meta, lhe, rhe } => {
            if lhe.contains_anonymous_component(None) || rhe.contains_anonymous_component(None) {
                Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "Anonymous components cannot be used together with the constraint equality operator `===`.",
                ))
            } else {
                Ok((build_constraint_equality(meta, lhe, rhe), Vec::new()))
            }
        }
        Statement::Declaration { meta, xtype, name, dimensions, .. } => {
            for exp in dimensions.clone() {
                if exp.contains_anonymous_component(None) {
                    return Err(AnonymousComponentError::boxed_report(
                        exp.meta(),
                        "An anonymous component cannot be used to define the dimensions of an array.",
                    ));
                }
            }
            Ok((build_declaration(meta, xtype, name, dimensions), Vec::new()))
        }
        Statement::InitializationBlock { meta, xtype, initializations } => {
            let mut new_inits = Vec::new();
            let mut declarations = Vec::new();
            for stmt in initializations {
                let (stmt_ok, mut declaration) =
                    remove_anonymous_from_statement(templates, file_library, stmt, var_access)?;
                new_inits.push(stmt_ok);
                declarations.append(&mut declaration)
            }
            Ok((
                Statement::InitializationBlock { meta, xtype, initializations: new_inits },
                declarations,
            ))
        }
        Statement::Block { meta, stmts } => {
            let mut new_stmts = Vec::new();
            let mut declarations = Vec::new();
            for stmt in stmts {
                let (stmt_ok, mut declaration) =
                    remove_anonymous_from_statement(templates, file_library, stmt, var_access)?;
                new_stmts.push(stmt_ok);
                declarations.append(&mut declaration);
            }
            Ok((Statement::Block { meta, stmts: new_stmts }, declarations))
        }
        Statement::Substitution { meta, var, op, rhe, access } => {
            let (mut stmts, declarations, new_rhe) =
                remove_anonymous_from_expression(templates, file_library, rhe, var_access)?;
            let subs =
                Statement::Substitution { meta: meta.clone(), var, access, op, rhe: new_rhe };
            let mut substs = Vec::new();
            if stmts.is_empty() {
                Ok((subs, declarations))
            } else {
                substs.append(&mut stmts);
                substs.push(subs);
                Ok((Statement::Block { meta, stmts: substs }, declarations))
            }
        }
    }
}

// returns a block with the substitutions, the declarations and finally the output expression
fn remove_anonymous_from_expression(
    templates: &HashMap<String, TemplateData>,
    file_library: &FileLibrary,
    expr: Expression,
    var_access: &Option<Expression>, // in case the call is inside a loop, variable used to control the access
) -> Result<(Vec<Statement>, Vec<Statement>, Expression), Box<Report>> {
    use Expression::*;
    match expr.clone() {
        ArrayInLine { values, .. } => {
            for value in values {
                if value.contains_anonymous_component(None) {
                    return Err(AnonymousComponentError::boxed_report(
                        value.meta(),
                        "An anonymous component cannot be used to define the dimensions of an array.",
                    ));
                }
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        Number(_, _) => Ok((Vec::new(), Vec::new(), expr)),
        Variable { meta, .. } => {
            if expr.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "An anonymous component cannot be used to access an array.",
                ));
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        InfixOp { meta, lhe, rhe, .. } => {
            if lhe.contains_anonymous_component(None) || rhe.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "Anonymous components cannot be used in arithmetic or boolean expressions.",
                ));
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        PrefixOp { meta, rhe, .. } => {
            if rhe.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "Anonymous components cannot be used in arithmetic or boolean expressions.",
                ));
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        InlineSwitchOp { meta, cond, if_true, if_false } => {
            if cond.contains_anonymous_component(None)
                || if_true.contains_anonymous_component(None)
                || if_false.contains_anonymous_component(None)
            {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "An anonymous component cannot be used inside an inline switch expression.",
                ));
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        Call { meta, args, .. } => {
            for value in args {
                if value.contains_anonymous_component(None) {
                    return Err(AnonymousComponentError::boxed_report(
                        &meta,
                        "An anonymous component cannot be used as an argument to a template call.",
                    ));
                }
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
        AnonymousComponent { meta, id, params, signals, names, is_parallel } => {
            let template = templates.get(&id);
            let mut declarations = Vec::new();
            if template.is_none() {
                return Err(Box::new(
                    AnonymousComponentError::new(
                        Some(&meta),
                        &format!("The template `{id}` does not exist."),
                        Some(&format!("Unknown template `{id}` instantiated here.")),
                    )
                    .into_report(),
                ));
            }
            let mut i = 0;
            let mut seq_substs = Vec::new();
            let id_anon_temp = id.to_string()
                + "_"
                + &file_library.get_line(meta.start, meta.get_file_id()).unwrap().to_string()
                + "_"
                + &meta.start.to_string();
            if var_access.is_none() {
                declarations.push(build_declaration(
                    meta.clone(),
                    VariableType::Component,
                    id_anon_temp.clone(),
                    Vec::new(),
                ));
            } else {
                declarations.push(build_declaration(
                    meta.clone(),
                    VariableType::AnonymousComponent,
                    id_anon_temp.clone(),
                    vec![var_access.as_ref().unwrap().clone()],
                ));
            }
            let call = build_call(meta.clone(), id, params);
            if call.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "An anonymous component cannot be used as a argument to a template call.",
                ));
            }

            let exp_with_call =
                if is_parallel { build_parallel_op(meta.clone(), call) } else { call };
            let access = if var_access.is_none() {
                Vec::new()
            } else {
                vec![build_array_access(var_access.as_ref().unwrap().clone())]
            };
            let sub = build_substitution(
                meta.clone(),
                id_anon_temp.clone(),
                access,
                AssignOp::AssignVar,
                exp_with_call,
            );
            seq_substs.push(sub);
            let inputs = template.unwrap().get_declaration_inputs();
            let mut new_signals = Vec::new();
            let mut new_operators = Vec::new();
            if let Some(m) = names {
                let (operators, names): (Vec<AssignOp>, Vec<String>) = m.iter().cloned().unzip();
                for inp in inputs {
                    if !names.contains(&inp.0) {
                        return Err(AnonymousComponentError::boxed_report(
                            &meta,
                            &format!("The input signal `{}` is not assigned by the anonymous component call.", inp.0),
                        ));
                    } else {
                        let pos = names.iter().position(|r| *r == inp.0).unwrap();
                        new_signals.push(signals.get(pos).unwrap().clone());
                        new_operators.push(*operators.get(pos).unwrap());
                    }
                }
            } else {
                new_signals.clone_from(&signals);
                for _ in 0..signals.len() {
                    new_operators.push(AssignOp::AssignConstraintSignal);
                }
            }
            if inputs.len() != new_signals.len() || inputs.len() != signals.len() {
                return Err(AnonymousComponentError::boxed_report(&meta, "The number of input arguments must be equal to the number of input signals of the template."));
            }
            for inp in inputs {
                let mut acc = if var_access.is_none() {
                    Vec::new()
                } else {
                    vec![build_array_access(var_access.as_ref().unwrap().clone())]
                };
                acc.push(Access::ComponentAccess(inp.0.clone()));
                let (mut stmts, mut new_declarations, new_expr) = remove_anonymous_from_expression(
                    templates,
                    file_library,
                    new_signals.get(i).unwrap().clone(),
                    var_access,
                )?;
                if new_expr.contains_anonymous_component(None) {
                    return Err(AnonymousComponentError::boxed_report(
                        new_expr.meta(),
                        "The inputs to an anonymous component cannot contain anonymous components.",
                    ));
                }
                seq_substs.append(&mut stmts);
                declarations.append(&mut new_declarations);
                let subs = Statement::Substitution {
                    meta: meta.clone(),
                    var: id_anon_temp.clone(),
                    access: acc,
                    op: *new_operators.get(i).unwrap(),
                    rhe: new_expr,
                };
                i += 1;
                seq_substs.push(subs);
            }
            let outputs = template.unwrap().get_declaration_outputs();
            if outputs.len() == 1 {
                let output = outputs[0].0.clone();
                let mut acc = if var_access.is_none() {
                    Vec::new()
                } else {
                    vec![build_array_access(var_access.as_ref().unwrap().clone())]
                };

                acc.push(Access::ComponentAccess(output));
                let out_exp =
                    Expression::Variable { meta: meta.clone(), name: id_anon_temp, access: acc };
                Ok((vec![Statement::Block { meta, stmts: seq_substs }], declarations, out_exp))
            } else {
                let mut new_values = Vec::new();
                for output in outputs {
                    let mut acc = if var_access.is_none() {
                        Vec::new()
                    } else {
                        vec![build_array_access(var_access.as_ref().unwrap().clone())]
                    };
                    acc.push(Access::ComponentAccess(output.0.clone()));
                    let out_exp = Expression::Variable {
                        meta: meta.clone(),
                        name: id_anon_temp.clone(),
                        access: acc,
                    };
                    new_values.push(out_exp);
                }
                let out_exp = Tuple { meta: meta.clone(), values: new_values };
                Ok((vec![Statement::Block { meta, stmts: seq_substs }], declarations, out_exp))
            }
        }
        Tuple { meta, values } => {
            let mut new_values = Vec::new();
            let mut new_stmts: Vec<Statement> = Vec::new();
            let mut declarations: Vec<Statement> = Vec::new();
            for val in values {
                let result =
                    remove_anonymous_from_expression(templates, file_library, val, var_access);
                match result {
                    Ok((mut stm, mut declaration, val2)) => {
                        new_stmts.append(&mut stm);
                        new_values.push(val2);
                        declarations.append(&mut declaration);
                    }
                    Err(er) => {
                        return Err(er);
                    }
                }
            }
            Ok((new_stmts, declarations, build_tuple(meta, new_values)))
        }
        ParallelOp { meta, rhe } => {
            if !rhe.is_call()
                && !rhe.is_anonymous_component()
                && rhe.contains_anonymous_component(None)
            {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "Invalid use of the parallel operator together with an anonymous component.",
                ));
            } else if rhe.is_call() && rhe.contains_anonymous_component(None) {
                return Err(AnonymousComponentError::boxed_report(
                    &meta,
                    "An anonymous component cannot be used as a parameter in a template call.",
                ));
            } else if rhe.is_anonymous_component() {
                let rhe2 = rhe.make_anonymous_parallel();
                return remove_anonymous_from_expression(templates, file_library, rhe2, var_access);
            }
            Ok((Vec::new(), Vec::new(), expr))
        }
    }
}

fn separate_declarations_in_comp_var_subs(
    declarations: Vec<Statement>,
) -> (Vec<Statement>, Vec<Statement>, Vec<Statement>) {
    let mut components_dec = Vec::new();
    let mut variables_dec = Vec::new();
    let mut substitutions = Vec::new();
    for dec in declarations {
        if let Statement::Declaration { ref xtype, .. } = dec {
            if matches!(xtype, VariableType::Component | VariableType::AnonymousComponent) {
                components_dec.push(dec);
            } else if VariableType::Var.eq(xtype) {
                variables_dec.push(dec);
            } else {
                unreachable!();
            }
        } else if let Statement::Substitution { .. } = dec {
            substitutions.push(dec);
        } else {
            unreachable!();
        }
    }
    (components_dec, variables_dec, substitutions)
}

fn remove_tuples_from_statement(stmt: Statement) -> Result<Statement, Box<Report>> {
    match stmt {
        Statement::MultiSubstitution { meta, lhe, op, rhe } => {
            let new_lhe = remove_tuple_from_expression(lhe)?;
            let new_rhe = remove_tuple_from_expression(rhe)?;
            match (new_lhe, new_rhe) {
                (
                    Expression::Tuple { values: mut lhe_values, .. },
                    Expression::Tuple { values: mut rhe_values, .. },
                ) => {
                    if lhe_values.len() == rhe_values.len() {
                        let mut substs = Vec::new();
                        while !lhe_values.is_empty() {
                            let lhe = lhe_values.remove(0);
                            if let Expression::Variable { meta, name, access } = lhe {
                                let rhe = rhe_values.remove(0);
                                if name != "_" {
                                    substs.push(build_substitution(
                                        meta.clone(),
                                        name.clone(),
                                        access.to_vec(),
                                        op,
                                        rhe,
                                    ));
                                }
                            } else {
                                return Err(TupleError::boxed_report(&meta, "The elements of the destination tuple must be either signals or variables."));
                            }
                        }
                        Ok(build_block(meta, substs))
                    } else if !lhe_values.is_empty() {
                        Err(TupleError::boxed_report(
                            &meta,
                            "The two tuples do not have the same length.",
                        ))
                    } else {
                        Err(TupleError::boxed_report(
                            &meta,
                            "This expression must be the right-hand side of an assignment.",
                        ))
                    }
                }
                (lhe, rhe) => {
                    if lhe.is_tuple() || lhe.is_variable() {
                        return Err(TupleError::boxed_report(
                            rhe.meta(),
                            "This expression must be a tuple or an anonymous component.",
                        ));
                    } else {
                        return Err(TupleError::boxed_report(
                            lhe.meta(),
                            "This expression must be a tuple, a component, a signal or a variable.",
                        ));
                    }
                }
            }
        }
        Statement::IfThenElse { meta, cond, if_case, else_case } => {
            if cond.contains_tuple(None) {
                Err(TupleError::boxed_report(&meta, "Tuples cannot be used in conditions."))
            } else {
                let new_if_case = remove_tuples_from_statement(*if_case)?;
                match else_case {
                    Some(else_case) => {
                        let new_else_case = remove_tuples_from_statement(*else_case)?;
                        Ok(Statement::IfThenElse {
                            meta,
                            cond,
                            if_case: Box::new(new_if_case),
                            else_case: Some(Box::new(new_else_case)),
                        })
                    }
                    None => Ok(Statement::IfThenElse {
                        meta,
                        cond,
                        if_case: Box::new(new_if_case),
                        else_case: None,
                    }),
                }
            }
        }
        Statement::While { meta, cond, stmt } => {
            if cond.contains_tuple(None) {
                Err(TupleError::boxed_report(&meta, "Tuples cannot be used in conditions."))
            } else {
                let new_stmt = remove_tuples_from_statement(*stmt)?;
                Ok(Statement::While { meta, cond, stmt: Box::new(new_stmt) })
            }
        }
        Statement::LogCall { meta, args } => {
            let mut new_args = Vec::new();
            for arg in args {
                match arg {
                    LogArgument::LogStr(str) => {
                        new_args.push(LogArgument::LogStr(str));
                    }
                    LogArgument::LogExp(exp) => {
                        let mut sep_args = separate_tuple_for_log_call(vec![exp]);
                        new_args.append(&mut sep_args);
                    }
                }
            }
            Ok(build_log_call(meta, new_args))
        }
        Statement::Assert { meta, arg } => Ok(build_assert(meta, arg)),
        Statement::Return { meta, value } => {
            if value.contains_tuple(None) {
                Err(TupleError::boxed_report(&meta, "Tuple cannot be used in return values."))
            } else {
                Ok(build_return(meta, value))
            }
        }
        Statement::ConstraintEquality { meta, lhe, rhe } => {
            if lhe.contains_tuple(None) || rhe.contains_tuple(None) {
                Err(TupleError::boxed_report(
                    &meta,
                    "Tuples cannot be used together with the constraint equality operator `===`.",
                ))
            } else {
                Ok(build_constraint_equality(meta, lhe, rhe))
            }
        }
        Statement::Declaration { meta, xtype, name, dimensions, .. } => {
            for expr in &dimensions {
                if expr.contains_tuple(None) {
                    return Err(TupleError::boxed_report(
                        &meta,
                        "A tuple cannot be used to define the dimensions of an array.",
                    ));
                }
            }
            Ok(build_declaration(meta, xtype, name, dimensions))
        }
        Statement::InitializationBlock { meta, xtype, initializations } => {
            let mut new_inits = Vec::new();
            for stmt in initializations {
                let new_stmt = remove_tuples_from_statement(stmt)?;
                new_inits.push(new_stmt);
            }
            Ok(Statement::InitializationBlock { meta, xtype, initializations: new_inits })
        }
        Statement::Block { meta, stmts } => {
            let mut new_stmts = Vec::new();
            for stmt in stmts {
                let new_stmt = remove_tuples_from_statement(stmt)?;
                new_stmts.push(new_stmt);
            }
            Ok(Statement::Block { meta, stmts: new_stmts })
        }
        Statement::Substitution { meta, var, op, rhe, access } => {
            let new_rhe = remove_tuple_from_expression(rhe)?;
            if new_rhe.is_tuple() {
                return Err(TupleError::boxed_report(
                    &meta,
                    "Left-hand side of the statement is not a tuple.",
                ));
            }
            for access in &access {
                if let Access::ArrayAccess(index) = access {
                    if index.contains_tuple(None) {
                        return Err(TupleError::boxed_report(
                            index.meta(),
                            "A tuple cannot be used to access an array.",
                        ));
                    }
                }
            }
            if var != "_" {
                Ok(Statement::Substitution { meta, var, access, op, rhe: new_rhe })
            } else {
                // Since expressions cannot have side effects, we can ignore this.
                Ok(build_block(meta, Vec::new()))
            }
        }
    }
}

fn separate_tuple_for_log_call(values: Vec<Expression>) -> Vec<LogArgument> {
    let mut new_values = Vec::new();
    for value in values {
        if let Expression::Tuple { values: values2, .. } = value {
            new_values.push(LogArgument::LogStr("(".to_string()));
            let mut sep_values = separate_tuple_for_log_call(values2);
            new_values.append(&mut sep_values);
            new_values.push(LogArgument::LogStr(")".to_string()));
        } else {
            new_values.push(LogArgument::LogExp(value));
        }
    }
    new_values
}

fn remove_tuple_from_expression(expr: Expression) -> Result<Expression, Box<Report>> {
    use Expression::*;
    match expr.clone() {
        ArrayInLine { meta, values } => {
            for value in values {
                if value.contains_tuple(None) {
                    return Err(TupleError::boxed_report(
                        &meta,
                        "A tuple cannot be used to define the dimensions of an array.",
                    ));
                }
            }
            Ok(expr)
        }
        Number(_, _) => Ok(expr),
        Variable { meta, .. } => {
            if expr.contains_tuple(None) {
                return Err(TupleError::boxed_report(
                    &meta,
                    "A tuple cannot be used to access an array.",
                ));
            }
            Ok(expr)
        }
        InfixOp { meta, lhe, rhe, .. } => {
            if lhe.contains_tuple(None) || rhe.contains_tuple(None) {
                return Err(TupleError::boxed_report(
                    &meta,
                    "Tuples cannot be used in arithmetic or boolean expressions.",
                ));
            }
            Ok(expr)
        }
        PrefixOp { meta, rhe, .. } => {
            if rhe.contains_tuple(None) {
                return Err(TupleError::boxed_report(
                    &meta,
                    "Tuples cannot be used in arithmetic or boolean expressions.",
                ));
            }
            Ok(expr)
        }
        InlineSwitchOp { meta, cond, if_true, if_false } => {
            if cond.contains_tuple(None)
                || if_true.contains_tuple(None)
                || if_false.contains_tuple(None)
            {
                return Err(TupleError::boxed_report(
                    &meta,
                    "Tuples cannot be used inside an inline switch expression.",
                ));
            }
            Ok(expr)
        }
        Call { meta, args, .. } => {
            for value in args {
                if value.contains_tuple(None) {
                    return Err(TupleError::boxed_report(
                        &meta,
                        "Tuples cannot be used as an argument to a function call.",
                    ));
                }
            }
            Ok(expr)
        }
        AnonymousComponent { .. } => {
            // This is called after anonymous components have been removed.
            unreachable!();
        }
        Tuple { meta, values } => {
            let mut unfolded_values = Vec::new();
            for value in values {
                let new_value = remove_tuple_from_expression(value)?;
                if let Tuple { values: mut inner, .. } = new_value {
                    unfolded_values.append(&mut inner);
                } else {
                    unfolded_values.push(new_value);
                }
            }
            Ok(build_tuple(meta, unfolded_values))
        }
        ParallelOp { meta, rhe } => {
            if rhe.contains_tuple(None) {
                return Err(TupleError::boxed_report(
                    &meta,
                    "Tuples cannot be used in parallel operators.",
                ));
            }
            Ok(expr)
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::parse_definition;

    use super::*;

    #[test]
    fn test_desugar_multi_sub() {
        let src = [
            r#"
            template Anonymous(n) {
                signal input a;
                signal input b;
                signal output c;
                signal output d;
                signal output e;

                (c, d, e) <== (a + 1, b + 2, c + 3);
            }
        "#,
            r#"
            template Test(n) {
                signal input a;
                signal input b;
                signal output c;
                signal output d;

                (c, _, d) <== Anonymous(n)(a, b);
            }
        "#,
        ];
        validate_ast(&src, 0);
    }

    #[test]
    fn test_nested_tuples() {
        let src = [r#"
            template Test(n) {
                signal input a;
                signal input b;
                signal output c;
                signal output d;
                signal output e;

                ((c, d), (_)) <== ((a + 1, b + 2), (c + 3));
            }
        "#];
        validate_ast(&src, 0);

        // TODO: Invalid, but is currently accepted by the compiler.
        let src = [r#"
            template Test(n) {
                signal input a;
                signal input b;
                signal output c;
                signal output d;
                signal output e;

                ((c, d), e) <== (a + 1, (b + 2, c + 3));
            }
        "#];
        validate_ast(&src, 0);

        // TODO: Invalid, but is currently accepted by the compiler.
        let src = [r#"
            template Test(n) {
                signal input a;
                signal input b;
                signal output c;

                (((c))) <== (a + b);
            }
        "#];
        validate_ast(&src, 0);
    }

    #[test]
    fn test_invalid_tuples() {
        let src = [r#"
            template Test(n) {
                signal input a;
                signal input b;
                signal output c;
                signal output d;
                signal output e;

                ((c, d), e) <== (b + 2, c + 3);
            }
        "#];
        validate_ast(&src, 1);
    }

    fn validate_ast(src: &[&str], errors: usize) {
        let mut reports = ReportCollection::new();
        let (templates, file_library) = parse_templates(src);

        // Verify that `remove_syntactic_sugar` is successful.
        let (templates, _) =
            remove_syntactic_sugar(&templates, &HashMap::new(), &file_library, &mut reports);
        assert_eq!(reports.len(), errors);

        // Ensure that no template contains a tuple or an anonymous component.
        for template in templates.values() {
            assert!(!template.get_body().contains_tuple(None));
            assert!(!template.get_body().contains_anonymous_component(None));
        }
    }

    fn parse_templates(src: &[&str]) -> (HashMap<String, TemplateData>, FileLibrary) {
        let mut templates = HashMap::new();
        let mut file_library = FileLibrary::new();
        let mut elem_id = 0;
        for src in src {
            let file_id = file_library.add_file("memory".to_string(), src.to_string(), true);
            let definition = parse_definition(src).unwrap();
            let Definition::Template {
                name,
                args,
                arg_location,
                body,
                parallel,
                is_custom_gate,
                ..
            } = definition
            else {
                unreachable!();
            };
            let template = TemplateData::new(
                name.clone(),
                file_id,
                body,
                args.len(),
                args,
                arg_location,
                &mut elem_id,
                parallel,
                is_custom_gate,
            );
            templates.insert(name, template);
        }
        (templates, file_library)
    }
}


================================================
FILE: parser/src/syntax_sugar_traits.rs
================================================
use program_structure::ast::*;
use program_structure::report::ReportCollection;

use crate::errors::TupleError;

pub(crate) trait ContainsExpression {
    /// Returns true if `self` contains `expr` such that `matcher(expr)`
    /// evaluates to true. If the callback is not `None` it is invoked on
    /// `expr.meta()` for each matching expression.
    fn contains_expr(
        &self,
        matcher: &impl Fn(&Expression) -> bool,
        callback: &mut impl FnMut(&Meta),
    ) -> bool;

    /// Returns true if the node contains a tuple. If `reports` is not `None`, a
    /// report is generated for each occurrence.
    fn contains_tuple(&self, reports: Option<&mut ReportCollection>) -> bool {
        let matcher = |expr: &Expression| expr.is_tuple();
        if let Some(reports) = reports {
            let mut callback = |meta: &Meta| {
                let error = TupleError::new(
                    Some(meta),
                    "Tuples are not allowed in functions.",
                    Some("Tuple instantiated here."),
                );
                reports.push(error.into_report());
            };
            self.contains_expr(&matcher, &mut callback)
        } else {
            // We need to pass a dummy callback because rustc isn't smart enough
            // to infer the type parameter to `Option` if we use options here.
            let mut dummy = |_: &Meta| {};
            self.contains_expr(&matcher, &mut dummy)
        }
    }

    /// Returns true if the node contains an anonymous component. If `reports`
    /// is not `None`, a report is generated for each occurrence.
    fn contains_anonymous_component(&self, reports: Option<&mut ReportCollection>) -> bool {
        let matcher = |expr: &Expression| expr.is_anonymous_component();
        if let Some(reports) = reports {
            let mut callback = |meta: &Meta| {
                let error = TupleError::new(
                    Some(meta),
                    "Anonymous components are not allowed in functions.",
                    Some("Anonymous component instantiated here."),
                );
                reports.push(error.into_report());
            };
            self.contains_expr(&matcher, &mut callback)
        } else {
            // We need to pass a dummy callback because rustc isn't smart enough
            // to infer the type parameter to `Option` if we use options here.
            let mut dummy = |_: &Meta| {};
            self.contains_expr(&matcher, &mut dummy)
        }
    }
}

impl ContainsExpression for Expression {
    fn contains_expr(
        &self,
        matcher: &impl Fn(&Expression) -> bool,
        callback: &mut impl FnMut(&Meta),
    ) -> bool {
        use Expression::*;
        // Check if the current expression matches and invoke the callback if
        // defined.
        if matcher(self) {
            callback(self.meta());
            return true;
        }
        let mut result = false;
        match &self {
            InfixOp { lhe, rhe, .. } => {
                result = lhe.contains_expr(matcher, callback) || result;
                result = rhe.contains_expr(matcher, callback) || result;
                result
            }
            PrefixOp { rhe, .. } => rhe.contains_expr(matcher, callback),
            InlineSwitchOp { cond, if_true, if_false, .. } => {
                result = cond.contains_expr(matcher, callback) || result;
                result = if_true.contains_expr(matcher, callback) || result;
                result = if_false.contains_expr(matcher, callback) || result;
                result
            }
            Call { args, .. } => {
                for arg in args {
                    result = arg.contains_expr(matcher, callback) || result;
                }
                result
            }
            ArrayInLine { values, .. } => {
                for value in values {
                    result = value.contains_expr(matcher, callback) || result;
                }
                result
            }
            AnonymousComponent { params, signals, .. } => {
                for param in params {
                    result = param.contains_expr(matcher, callback) || result;
                }
                for signal in signals {
                    result = signal.contains_expr(matcher, callback) || result;
                }
                result
            }
            Variable { access, .. } => {
                for access in access {
                    if let Access::ArrayAccess(index) = access {
                        result = index.contains_expr(matcher, callback) || result;
                    }
                }
                result
            }
            Number(_, _) => false,
            Tuple { values, .. } => {
                for value in values {
                    result = value.contains_expr(matcher, callback) || result;
                }
                result
            }
            ParallelOp { rhe, .. } => rhe.contains_expr(matcher, callback),
        }
    }
}

impl ContainsExpression for Statement {
    fn contains_expr(
        &self,
        matcher: &impl Fn(&Expression) -> bool,
        callback: &mut impl FnMut(&Meta),
    ) -> bool {
        use LogArgument::*;
        use Statement::*;
        use Access::*;
        let mut result = false;
        match self {
            IfThenElse { cond, if_case, else_case, .. } => {
                result = cond.contains_expr(matcher, callback) || result;
                result = if_case.contains_expr(matcher, callback) || result;
                if let Some(else_case) = else_case {
                    result = else_case.contains_expr(matcher, callback) || result;
                }
                result
            }
            While { cond, stmt, .. } => {
                result = cond.contains_expr(matcher, callback) || result;
                result = stmt.contains_expr(matcher, callback) || result;
                result
            }
            Return { value, .. } => value.contains_expr(matcher, callback),
            InitializationBlock { initializations, .. } => {
                for init in initializations {
                    result = init.contains_expr(matcher, callback) || result;
                }
                result
            }
            Block { stmts, .. } => {
                for stmt in stmts {
                    result = stmt.contains_expr(matcher, callback) || result;
                }
                result
            }
            Declaration { dimensions, .. } => {
                for size in dimensions {
                    result = size.contains_expr(matcher, callback) || result;
                }
                result
            }
            Substitution { access, rhe, .. } => {
                for access in access {
                    if let ArrayAccess(index) = access {
                        result = index.contains_expr(matcher, callback) || result;
                    }
                }
                result = rhe.contains_expr(matcher, callback) || result;
                result
            }
            MultiSubstitution { lhe, rhe, .. } => {
                result = lhe.contains_expr(matcher, callback) || result;
                result = rhe.contains_expr(matcher, callback) || result;
                result
            }
            ConstraintEquality { lhe, rhe, .. } => {
                result = lhe.contains_expr(matcher, callback) || result;
                result = rhe.contains_expr(matcher, callback) || result;
                result
            }
            LogCall { args, .. } => {
                for arg in args {
                    if let LogExp(expr) = arg {
                        result = expr.contains_expr(matcher, callback) || result;
                    }
                }
                result
            }
            Assert { arg, .. } => arg.contains_expr(matcher, callback),
        }
    }
}


================================================
FILE: program_analysis/Cargo.toml
================================================
[package]
name = "circomspect-program-analysis"
version = "0.8.2"
edition = "2021"
rust-version = "1.65"
license = "LGPL-3.0-only"
authors = ["Trail of Bits"]
description = "Support crate for the Circomspect static analyzer"
repository = "https://github.com/trailofbits/circomspect"

[dependencies]
anyhow = "1.0"
log = "0.4"
num-bigint-dig = "0.8"
num-traits = "0.2"
thiserror = "1.0"
parser = { package = "circomspect-parser", version = "2.2.0", path = "../parser" }
program_structure = { package = "circomspect-program-structure", version = "2.1.4", path = "../program_structure" }

[dev-dependencies]
parser = { package = "circomspect-parser", version = "2.2.0", path = "../parser" }
program_structure = { package = "circomspect-program-structure", version = "2.1.4", path = "../program_structure" }


================================================
FILE: program_analysis/src/analysis_context.rs
================================================
use thiserror::Error;

use program_structure::{
    file_definition::{FileID, FileLocation},
    cfg::Cfg,
};

/// Errors returned by the analysis context.
#[derive(Debug, Error)]
pub enum AnalysisError {
    /// This function has no corresponding AST.
    #[error("Unknown function `{name}`.")]
    UnknownFunction { name: String },
    /// This template has no corresponding AST.
    #[error("Unknown template `{name}`.")]
    UnknownTemplate { name: String },
    /// This function has an AST, but we failed to lift it to a corresponding
    /// CFG.
    #[error("Failed to lift the function `{name}`.")]
    FailedToLiftFunction { name: String },
    /// This template has an AST, but we failed to lift it to a corresponding
    /// CFG.
    #[error("Failed to lift the template `{name}`.")]
    FailedToLiftTemplate { name: String },
    /// The file ID does not correspond to a known file.
    #[error("Unknown file ID `{file_id}`.")]
    UnknownFile { file_id: FileID },
    /// The location does not exist in the file with the given ID.
    #[error("The location `{}:{}` is not valid for the file with file ID `{file_id}`.", file_location.start, file_location.end)]
    InvalidLocation { file_id: FileID, file_location: FileLocation },
}

/// Context passed to each analysis pass.
pub trait AnalysisContext {
    /// Returns true if the context knows of a function with the given name.
    /// This method does not compute the CFG of the function which saves time
    /// compared to `AnalysisContext::function`.
    fn is_function(&self, name: &str) -> bool;

    /// Returns true if the context knows of a template with the given name.
    /// This method does not compute the CFG of the template which saves time
    /// compared to `AnalysisContext::template`.
    fn is_template(&self, name: &str) -> bool;

    /// Returns the CFG for the function with the given name.
    fn function(&mut self, name: &str) -> Result<&Cfg, AnalysisError>;

    /// Returns the CFG for the template with the given name.
    fn template(&mut self, name: &str) -> Result<&Cfg, AnalysisError>;

    /// Returns the string corresponding to the given file ID and location.
    fn underlying_str(
        &self,
        file_id: &FileID,
        file_location: &FileLocation,
    ) -> Result<String, AnalysisError>;
}


================================================
FILE: program_analysis/src/analysis_runner.rs
================================================
use log::{debug, trace};
use std::path::PathBuf;
use std::collections::HashMap;

use parser::ParseResult;

use program_structure::{
    writers::{LogWriter, ReportWriter},
    template_data::TemplateInfo,
    function_data::FunctionInfo,
    file_definition::{FileLibrary, FileLocation, FileID},
    cfg::{Cfg, IntoCfg},
    constants::Curve,
    report::{ReportCollection, Report},
};

#[cfg(test)]
use program_structure::template_library::TemplateLibrary;

use crate::{
    analysis_context::{AnalysisContext, AnalysisError},
    get_analysis_passes, config,
};

type CfgCache = HashMap<String, Cfg>;
type ReportCache = HashMap<String, ReportCollection>;

/// A type responsible for caching CFGs and running analysis passes over all
/// functions and templates.
#[derive(Default)]
pub struct AnalysisRunner {
    curve: Curve,
    libraries: Vec<PathBuf>,
    /// The corresponding file library including file includes.
    file_library: FileLibrary,
    /// Template ASTs generated by the parser.
    template_asts: TemplateInfo,
    /// Function ASTs generated by the parser.
    function_asts: FunctionInfo,
    /// Cached template CFGs generated on demand.
    template_cfgs: CfgCache,
    /// Cached function CFGs generated on demand.
    function_cfgs: CfgCache,
    /// Reports created during CFG generation.
    template_reports: ReportCache,
    /// Reports created during CFG generation.
    function_reports: ReportCache,
}

impl AnalysisRunner {
    pub fn new(curve: Curve) -> Self {
        AnalysisRunner { curve, ..Default::default() }
    }

    pub fn with_libraries(mut self, libraries: &[PathBuf]) -> Self {
        self.libraries.extend_from_slice(libraries);
        self
    }

    pub fn with_files(mut self, input_files: &[PathBuf]) -> (Self, ReportCollection) {
        let reports =
            match parser::parse_files(input_files, &self.libraries, &config::COMPILER_VERSION) {
                ParseResult::Program(program, warnings) => {
                    self.template_asts = program.templates;
                    self.function_asts = program.functions;
                    self.file_library = program.file_library;
                    warnings
                }
                ParseResult::Library(library, warnings) => {
                    self.template_asts = library.templates;
                    self.function_asts = library.functions;
                    self.file_library = library.file_library;
                    warnings
                }
            };
        (self, reports)
    }

    /// Convenience method used to generate a runner for testing purposes.
    #[cfg(test)]
    pub fn with_src(mut self, file_contents: &[&str]) -> Self {
        use parser::parse_definition;

        let mut library_contents = HashMap::new();
        let mut file_library = FileLibrary::default();
        for (file_index, file_source) in file_contents.iter().enumerate() {
            let file_name = format!("file-{file_index}.circom");
            let file_id = file_library.add_file(file_name, file_source.to_string(), true);
            library_contents.insert(file_id, vec![parse_definition(file_source).unwrap()]);
        }
        let template_library = TemplateLibrary::new(library_contents, file_library.clone());
        self.template_asts = template_library.templates;
        self.function_asts = template_library.functions;
        self.file_library = template_library.file_library;

        self
    }

    pub fn file_library(&self) -> &FileLibrary {
        &self.file_library
    }

    pub fn template_names(&self, user_input_only: bool) -> Vec<String> {
        // Clone template names to avoid holding multiple references to `self`.
        self.template_asts
            .iter()
            .filter_map(|(name, ast)| {
                if !user_input_only || self.file_library.is_user_input(ast.get_file_id()) {
                    Some(name)
                } else {
                    None
                }
            })
            .cloned()
            .collect()
    }

    pub fn function_names(&self, user_input_only: bool) -> Vec<String> {
        // Clone function names to avoid holding multiple references to `self`.
        self.function_asts
            .iter()
            .filter_map(|(name, ast)| {
                if !user_input_only || self.file_library.is_user_input(ast.get_file_id()) {
                    Some(name)
                } else {
                    None
                }
            })
            .cloned()
            .collect()
    }

    fn analyze_template<W: LogWriter + ReportWriter>(&mut self, name: &str, writer: &mut W) {
        writer.write_message(&format!("analyzing template '{name}'"));

        // We take ownership of the CFG and any previously generated reports
        // here to avoid holding multiple mutable and immutable references to
        // `self`. This may lead to the CFG being regenerated during analysis if
        // the template is invoked recursively. If it is then ¯\_(ツ)_/¯.
        let mut reports = self.take_template_reports(name);
        if let Ok(cfg) = self.take_template(name) {
            for analysis_pass in get_analysis_passes() {
                reports.append(&mut analysis_pass(self, &cfg));
            }
            // Re-insert the CFG into the hash map.
            if self.replace_template(name, cfg) {
                debug!("template `{name}` CFG was regenerated during analysis");
            }
        }
        writer.write_reports(&reports, &self.file_library);
    }

    pub fn analyze_templates<W: LogWriter + ReportWriter>(
        &mut self,
        writer: &mut W,
        user_input_only: bool,
    ) {
        for name in self.template_names(user_input_only) {
            self.analyze_template(&name, writer);
        }
    }

    fn analyze_function<W: LogWriter + ReportWriter>(&mut self, name: &str, writer: &mut W) {
        writer.write_message(&format!("analyzing function '{name}'"));

        // We take ownership of the CFG and any previously generated reports
        // here to avoid holding multiple mutable and immutable references to
        // `self`. This may lead to the CFG being regenerated during analysis if
        // the function is invoked recursively. If it is then ¯\_(ツ)_/¯.
        let mut reports = self.take_function_reports(name);
        if let Ok(cfg) = self.take_function(name) {
            for analysis_pass in get_analysis_passes() {
                reports.append(&mut analysis_pass(self, &cfg));
            }
            // Re-insert the CFG into the hash map.
            if self.replace_function(name, cfg) {
                debug!("function `{name}` CFG was regenerated during analysis");
            }
        }
        writer.write_reports(&reports, &self.file_library);
    }

    pub fn analyze_functions<W: LogWriter + ReportWriter>(
        &mut self,
        writer: &mut W,
        user_input_only: bool,
    ) {
        for name in self.function_names(user_input_only) {
            self.analyze_function(&name, writer);
        }
    }

    /// Report cache from CFG generation. These will be emitted when the
    /// template is analyzed.
    fn append_template_reports(&mut self, name: &str, reports: &mut ReportCollection) {
        self.template_reports.entry(name.to_string()).or_default().append(reports);
    }

    /// Report cache from CFG generation. These will be emitted when the
    /// template is analyzed.
    fn take_template_reports(&mut self, name: &str) -> ReportCollection {
        self.template_reports.remove(name).unwrap_or_default()
    }

    /// Report cache from CFG generation. These will be emitted when the
    /// function is analyzed.
    fn append_function_reports(&mut self, name: &str, reports: &mut ReportCollection) {
        self.function_reports.entry(name.to_string()).or_default().append(reports);
    }

    /// Report cache from CFG generation. These will be emitted when the
    /// function is analyzed.
    fn take_function_reports(&mut self, name: &str) -> ReportCollection {
        self.function_reports.remove(name).unwrap_or_default()
    }

    fn cache_template(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
        if !self.template_cfgs.contains_key(name) {
            // The template CFG needs to be generated from the AST.
            if self.template_reports.contains_key(name) {
                // We have already failed to generate the CFG.
                return Err(AnalysisError::FailedToLiftTemplate { name: name.to_string() });
            }
            // Get the AST corresponding to the template.
            let Some(ast) = self.template_asts.get(name) else {
                trace!("failed to lift unknown template `{name}`");
                return Err(AnalysisError::UnknownTemplate { name: name.to_string() });
            };
            // Generate the template CFG from the AST. Cache any reports.
            let mut reports = ReportCollection::new();
            let cfg = generate_cfg(ast, &self.curve, &mut reports).map_err(|report| {
                reports.push(*report);
                trace!("failed to lift template `{name}`");
                AnalysisError::FailedToLiftTemplate { name: name.to_string() }
            })?;
            self.append_template_reports(name, &mut reports);
            self.template_cfgs.insert(name.to_string(), cfg);
            trace!("successfully lifted template `{name}`");
        }
        Ok(self.template_cfgs.get(name).unwrap())
    }

    fn cache_function(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
        if !self.function_cfgs.contains_key(name) {
            // The function CFG needs to be generated from the AST.
            if self.function_reports.contains_key(name) {
                // We have already failed to generate the CFG.
                return Err(AnalysisError::FailedToLiftFunction { name: name.to_string() });
            }
            // Get the AST corresponding to the function.
            let Some(ast) = self.function_asts.get(name) else {
                trace!("failed to lift unknown function `{name}`");
                return Err(AnalysisError::UnknownFunction { name: name.to_string() });
            };
            // Generate the function CFG from the AST. Cache any reports.
            let mut reports = ReportCollection::new();
            let cfg = generate_cfg(ast, &self.curve, &mut reports).map_err(|report| {
                reports.push(*report);
                trace!("failed to lift function `{name}`");
                AnalysisError::FailedToLiftFunction { name: name.to_string() }
            })?;
            self.append_function_reports(name, &mut reports);
            self.function_cfgs.insert(name.to_string(), cfg);
            trace!("successfully lifted function `{name}`");
        }
        Ok(self.function_cfgs.get(name).unwrap())
    }

    pub fn take_template(&mut self, name: &str) -> Result<Cfg, AnalysisError> {
        self.cache_template(name)?;
        // The CFG must be available since caching was successful.
        Ok(self.template_cfgs.remove(name).unwrap())
    }

    pub fn take_function(&mut self, name: &str) -> Result<Cfg, AnalysisError> {
        self.cache_function(name)?;
        // The CFG must be available since caching was successful.
        Ok(self.function_cfgs.remove(name).unwrap())
    }

    pub fn replace_template(&mut self, name: &str, cfg: Cfg) -> bool {
        self.template_cfgs.insert(name.to_string(), cfg).is_some()
    }

    pub fn replace_function(&mut self, name: &str, cfg: Cfg) -> bool {
        self.function_cfgs.insert(name.to_string(), cfg).is_some()
    }
}

impl AnalysisContext for AnalysisRunner {
    fn is_template(&self, name: &str) -> bool {
        self.template_asts.contains_key(name)
    }

    fn is_function(&self, name: &str) -> bool {
        self.function_asts.contains_key(name)
    }

    fn template(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
        self.cache_template(name)
    }

    fn function(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
        self.cache_function(name)
    }

    fn underlying_str(
        &self,
        file_id: &FileID,
        file_location: &FileLocation,
    ) -> Result<String, AnalysisError> {
        let Ok(file) = self.file_library.to_storage().get(*file_id) else {
            return Err(AnalysisError::UnknownFile { file_id: *file_id });
        };
        if file_location.end <= file.source().len() {
            Ok(file.source()[file_location.start..file_location.end].to_stri
Download .txt
gitextract_dgkomprs/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .rustfmt.toml
├── CHANGELOG.md
├── CODEOWNERS
├── Cargo.toml
├── LICENSE
├── README.md
├── TODO.md
├── circom_algebra/
│   ├── Cargo.toml
│   └── src/
│       ├── lib.rs
│       └── modular_arithmetic.rs
├── cli/
│   ├── Cargo.toml
│   └── src/
│       └── main.rs
├── doc/
│   ├── analysis_passes.md
│   └── demo.cast
├── parser/
│   ├── Cargo.toml
│   ├── build.rs
│   └── src/
│       ├── errors.rs
│       ├── include_logic.rs
│       ├── lang.lalrpop
│       ├── lib.rs
│       ├── parser_logic.rs
│       ├── syntax_sugar_remover.rs
│       └── syntax_sugar_traits.rs
├── program_analysis/
│   ├── Cargo.toml
│   └── src/
│       ├── analysis_context.rs
│       ├── analysis_runner.rs
│       ├── bitwise_complement.rs
│       ├── bn254_specific_circuit.rs
│       ├── config.rs
│       ├── constant_conditional.rs
│       ├── constraint_analysis.rs
│       ├── definition_complexity.rs
│       ├── field_arithmetic.rs
│       ├── field_comparisons.rs
│       ├── lib.rs
│       ├── nonstrict_binary_conversion.rs
│       ├── side_effect_analysis.rs
│       ├── signal_assignments.rs
│       ├── taint_analysis.rs
│       ├── unconstrained_division.rs
│       ├── unconstrained_less_than.rs
│       ├── under_constrained_signals.rs
│       └── unused_output_signal.rs
├── program_structure/
│   ├── Cargo.toml
│   └── src/
│       ├── abstract_syntax_tree/
│       │   ├── assign_op_impl.rs
│       │   ├── ast.rs
│       │   ├── ast_impl.rs
│       │   ├── ast_shortcuts.rs
│       │   ├── expression_builders.rs
│       │   ├── expression_impl.rs
│       │   ├── mod.rs
│       │   ├── statement_builders.rs
│       │   └── statement_impl.rs
│       ├── control_flow_graph/
│       │   ├── basic_block.rs
│       │   ├── cfg.rs
│       │   ├── errors.rs
│       │   ├── lifting.rs
│       │   ├── mod.rs
│       │   ├── parameters.rs
│       │   ├── ssa_impl.rs
│       │   └── unique_vars.rs
│       ├── intermediate_representation/
│       │   ├── declarations.rs
│       │   ├── degree_meta.rs
│       │   ├── errors.rs
│       │   ├── expression_impl.rs
│       │   ├── ir.rs
│       │   ├── lifting.rs
│       │   ├── mod.rs
│       │   ├── statement_impl.rs
│       │   ├── type_meta.rs
│       │   ├── value_meta.rs
│       │   └── variable_meta.rs
│       ├── lib.rs
│       ├── program_library/
│       │   ├── file_definition.rs
│       │   ├── function_data.rs
│       │   ├── mod.rs
│       │   ├── program_archive.rs
│       │   ├── program_merger.rs
│       │   ├── report.rs
│       │   ├── report_code.rs
│       │   ├── template_data.rs
│       │   └── template_library.rs
│       ├── static_single_assignment/
│       │   ├── dominator_tree.rs
│       │   ├── errors.rs
│       │   ├── mod.rs
│       │   └── traits.rs
│       └── utils/
│           ├── constants.rs
│           ├── environment.rs
│           ├── mod.rs
│           ├── nonempty_vec.rs
│           ├── sarif_conversion.rs
│           └── writers.rs
└── program_structure_tests/
    ├── Cargo.toml
    └── src/
        ├── control_flow_graph.rs
        ├── lib.rs
        └── static_single_assignment.rs
Download .txt
SYMBOL INDEX (1364 symbols across 72 files)

FILE: circom_algebra/src/modular_arithmetic.rs
  type ArithmeticError (line 4) | pub enum ArithmeticError {
  function modulus (line 9) | fn modulus(a: &BigInt, b: &BigInt) -> BigInt {
  function bit_representation (line 14) | fn bit_representation(elem: &BigInt) -> (Sign, Vec<u8>) {
  function mask (line 18) | fn mask(field: &BigInt) -> BigInt {
  function add (line 26) | pub fn add(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function mul (line 31) | pub fn mul(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function sub (line 36) | pub fn sub(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function div (line 41) | pub fn div(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<BigI...
  function idiv (line 46) | pub fn idiv(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<Big...
  function mod_op (line 56) | pub fn mod_op(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<B...
  function pow (line 61) | pub fn pow(base: &BigInt, exp: &BigInt, field: &BigInt) -> BigInt {
  function prefix_sub (line 64) | pub fn prefix_sub(elem: &BigInt, field: &BigInt) -> BigInt {
  function complement_256 (line 72) | pub fn complement_256(elem: &BigInt, field: &BigInt) -> BigInt {
  function shift_l (line 87) | pub fn shift_l(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<...
  function shift_r (line 98) | pub fn shift_r(left: &BigInt, right: &BigInt, field: &BigInt) -> Result<...
  function bit_or (line 109) | pub fn bit_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function bit_and (line 112) | pub fn bit_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function bit_xor (line 115) | pub fn bit_xor(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function constant_true (line 120) | fn constant_true() -> BigInt {
  function constant_false (line 123) | fn constant_false() -> BigInt {
  function val (line 126) | fn val(elem: &BigInt, field: &BigInt) -> BigInt {
  function comparable_element (line 134) | fn comparable_element(elem: &BigInt, field: &BigInt) -> BigInt {
  function normalize (line 137) | fn normalize(elem: &BigInt, field: &BigInt) -> BigInt {
  function as_bool (line 146) | pub fn as_bool(elem: &BigInt, field: &BigInt) -> bool {
  function not (line 149) | pub fn not(elem: &BigInt, field: &BigInt) -> BigInt {
  function bool_or (line 152) | pub fn bool_or(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function bool_and (line 155) | pub fn bool_and(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function eq (line 158) | pub fn eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function lesser (line 167) | pub fn lesser(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function not_eq (line 176) | pub fn not_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function lesser_eq (line 179) | pub fn lesser_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function greater (line 182) | pub fn greater(left: &BigInt, right: &BigInt, field: &BigInt) -> BigInt {
  function greater_eq (line 185) | pub fn greater_eq(left: &BigInt, right: &BigInt, field: &BigInt) -> BigI...
  constant FIELD (line 192) | const FIELD: &str = "257";
  function mod_check (line 194) | fn mod_check() {
  function comparison_check (line 201) | fn comparison_check() {
  function mod_operation_check (line 210) | fn mod_operation_check() {
  function complement_of_complement_is_the_original_test (line 222) | fn complement_of_complement_is_the_original_test() {
  function lesser_eq_test (line 233) | fn lesser_eq_test() {

FILE: cli/src/main.rs
  type Cli (line 18) | struct Cli {
  function cli_styles (line 49) | fn cli_styles() -> clap::builder::Styles {
  function filter_by_file (line 61) | fn filter_by_file(report: &Report, user_inputs: &HashSet<FileID>) -> bool {
  function filter_by_level (line 67) | fn filter_by_level(report: &Report, output_level: &MessageCategory) -> b...
  function filter_by_id (line 72) | fn filter_by_id(report: &Report, allow_list: &[String]) -> bool {
  function main (line 76) | fn main() -> ExitCode {

FILE: parser/build.rs
  function main (line 3) | fn main() {

FILE: parser/src/errors.rs
  type UnclosedCommentError (line 6) | pub struct UnclosedCommentError {
    method into_report (line 12) | pub fn into_report(self) -> Report {
  type ParsingError (line 19) | pub struct ParsingError {
    method into_report (line 26) | pub fn into_report(self) -> Report {
  type FileOsError (line 37) | pub struct FileOsError {
    method into_report (line 41) | pub fn into_report(self) -> Report {
  type IncludeError (line 46) | pub struct IncludeError {
    method into_report (line 52) | pub fn into_report(self) -> Report {
  type MultipleMainError (line 62) | pub struct MultipleMainError;
    method produce_report (line 64) | pub fn produce_report() -> Report {
  type CompilerVersionError (line 72) | pub struct CompilerVersionError {
    method into_report (line 78) | pub fn into_report(self) -> Report {
  type NoCompilerVersionWarning (line 89) | pub struct NoCompilerVersionWarning {
    method produce_report (line 94) | pub fn produce_report(error: Self) -> Report {
  type AnonymousComponentError (line 106) | pub struct AnonymousComponentError {
    method new (line 113) | pub fn new(meta: Option<&Meta>, message: &str, primary: Option<&str>) ...
    method into_report (line 121) | pub fn into_report(self) -> Report {
    method boxed_report (line 130) | pub fn boxed_report(meta: &Meta, message: &str) -> Box<Report> {
  type TupleError (line 135) | pub struct TupleError {
    method new (line 142) | pub fn new(meta: Option<&Meta>, message: &str, primary: Option<&str>) ...
    method into_report (line 150) | pub fn into_report(self) -> Report {
    method boxed_report (line 159) | pub fn boxed_report(meta: &Meta, message: &str) -> Box<Report> {
  function version_string (line 164) | fn version_string(version: &Version) -> String {

FILE: parser/src/include_logic.rs
  type FileStack (line 13) | pub struct FileStack {
    method new (line 28) | pub fn new(paths: &[PathBuf], libs: &[PathBuf], reports: &mut ReportCo...
    method add_libraries (line 43) | fn add_libraries(&mut self, libs: &[PathBuf], reports: &mut ReportColl...
    method add_files (line 63) | fn add_files(&mut self, paths: &[PathBuf], reports: &mut ReportCollect...
    method add_include (line 87) | pub fn add_include(&mut self, include: &Include) -> Result<(), Box<Rep...
    method include_library (line 102) | fn include_library(&mut self, include: &Include) -> Result<(), Box<Rep...
    method take_next (line 142) | pub fn take_next(&mut self) -> Option<PathBuf> {
    method is_user_input (line 160) | pub fn is_user_input(&self, path: &PathBuf) -> bool {
  type Library (line 22) | struct Library {

FILE: parser/src/lib.rs
  type ParseResult (line 32) | pub enum ParseResult {
  function parse_files (line 39) | pub fn parse_files(
  function parse_file (line 136) | pub fn parse_file(
  function open_file (line 163) | fn open_file(file_path: &PathBuf) -> Result<(String, String), Box<Report...
  function check_compiler_version (line 173) | fn check_compiler_version(
  function test_compiler_version (line 210) | fn test_compiler_version() {

FILE: parser/src/parser_logic.rs
  function preprocess (line 8) | pub fn preprocess(expr: &str, file_id: FileID) -> Result<String, Box<Rep...
  function parse_file (line 78) | pub fn parse_file(src: &str, file_id: FileID) -> Result<AST, Box<Report>> {
  function parse_string (line 114) | pub fn parse_string(src: &str) -> Option<AST> {
  function parse_definition (line 122) | pub fn parse_definition(src: &str) -> Option<Definition> {
  function format_expected (line 130) | fn format_expected(tokens: &[String]) -> String {
  function test_parse_string (line 157) | fn test_parse_string() {

FILE: parser/src/syntax_sugar_remover.rs
  function remove_syntactic_sugar (line 20) | pub(crate) fn remove_syntactic_sugar(
  function remove_anonymous_from_statement (line 90) | fn remove_anonymous_from_statement(
  function remove_anonymous_from_expression (line 309) | fn remove_anonymous_from_expression(
  function separate_declarations_in_comp_var_subs (line 570) | fn separate_declarations_in_comp_var_subs(
  function remove_tuples_from_statement (line 594) | fn remove_tuples_from_statement(stmt: Statement) -> Result<Statement, Bo...
  function separate_tuple_for_log_call (line 771) | fn separate_tuple_for_log_call(values: Vec<Expression>) -> Vec<LogArgume...
  function remove_tuple_from_expression (line 786) | fn remove_tuple_from_expression(expr: Expression) -> Result<Expression, ...
  function test_desugar_multi_sub (line 886) | fn test_desugar_multi_sub() {
  function test_nested_tuples (line 914) | fn test_nested_tuples() {
  function test_invalid_tuples (line 956) | fn test_invalid_tuples() {
  function validate_ast (line 971) | fn validate_ast(src: &[&str], errors: usize) {
  function parse_templates (line 987) | fn parse_templates(src: &[&str]) -> (HashMap<String, TemplateData>, File...

FILE: parser/src/syntax_sugar_traits.rs
  type ContainsExpression (line 6) | pub(crate) trait ContainsExpression {
    method contains_expr (line 10) | fn contains_expr(
    method contains_tuple (line 18) | fn contains_tuple(&self, reports: Option<&mut ReportCollection>) -> bo...
    method contains_anonymous_component (line 40) | fn contains_anonymous_component(&self, reports: Option<&mut ReportColl...
    method contains_expr (line 62) | fn contains_expr(
    method contains_expr (line 130) | fn contains_expr(

FILE: program_analysis/src/analysis_context.rs
  type AnalysisError (line 10) | pub enum AnalysisError {
  type AnalysisContext (line 34) | pub trait AnalysisContext {
    method is_function (line 38) | fn is_function(&self, name: &str) -> bool;
    method is_template (line 43) | fn is_template(&self, name: &str) -> bool;
    method function (line 46) | fn function(&mut self, name: &str) -> Result<&Cfg, AnalysisError>;
    method template (line 49) | fn template(&mut self, name: &str) -> Result<&Cfg, AnalysisError>;
    method underlying_str (line 52) | fn underlying_str(

FILE: program_analysis/src/analysis_runner.rs
  type CfgCache (line 25) | type CfgCache = HashMap<String, Cfg>;
  type ReportCache (line 26) | type ReportCache = HashMap<String, ReportCollection>;
  type AnalysisRunner (line 31) | pub struct AnalysisRunner {
    method new (line 51) | pub fn new(curve: Curve) -> Self {
    method with_libraries (line 55) | pub fn with_libraries(mut self, libraries: &[PathBuf]) -> Self {
    method with_files (line 60) | pub fn with_files(mut self, input_files: &[PathBuf]) -> (Self, ReportC...
    method with_src (line 81) | pub fn with_src(mut self, file_contents: &[&str]) -> Self {
    method file_library (line 99) | pub fn file_library(&self) -> &FileLibrary {
    method template_names (line 103) | pub fn template_names(&self, user_input_only: bool) -> Vec<String> {
    method function_names (line 118) | pub fn function_names(&self, user_input_only: bool) -> Vec<String> {
    method analyze_template (line 133) | fn analyze_template<W: LogWriter + ReportWriter>(&mut self, name: &str...
    method analyze_templates (line 153) | pub fn analyze_templates<W: LogWriter + ReportWriter>(
    method analyze_function (line 163) | fn analyze_function<W: LogWriter + ReportWriter>(&mut self, name: &str...
    method analyze_functions (line 183) | pub fn analyze_functions<W: LogWriter + ReportWriter>(
    method append_template_reports (line 195) | fn append_template_reports(&mut self, name: &str, reports: &mut Report...
    method take_template_reports (line 201) | fn take_template_reports(&mut self, name: &str) -> ReportCollection {
    method append_function_reports (line 207) | fn append_function_reports(&mut self, name: &str, reports: &mut Report...
    method take_function_reports (line 213) | fn take_function_reports(&mut self, name: &str) -> ReportCollection {
    method cache_template (line 217) | fn cache_template(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
    method cache_function (line 243) | fn cache_function(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
    method take_template (line 269) | pub fn take_template(&mut self, name: &str) -> Result<Cfg, AnalysisErr...
    method take_function (line 275) | pub fn take_function(&mut self, name: &str) -> Result<Cfg, AnalysisErr...
    method replace_template (line 281) | pub fn replace_template(&mut self, name: &str, cfg: Cfg) -> bool {
    method replace_function (line 285) | pub fn replace_function(&mut self, name: &str, cfg: Cfg) -> bool {
  method is_template (line 291) | fn is_template(&self, name: &str) -> bool {
  method is_function (line 295) | fn is_function(&self, name: &str) -> bool {
  method template (line 299) | fn template(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
  method function (line 303) | fn function(&mut self, name: &str) -> Result<&Cfg, AnalysisError> {
  method underlying_str (line 307) | fn underlying_str(
  function generate_cfg (line 326) | fn generate_cfg<Ast: IntoCfg>(
  function test_function (line 344) | fn test_function() {
  function test_template (line 373) | fn test_template() {
  function test_underlying_str (line 404) | fn test_underlying_str() {

FILE: program_analysis/src/bitwise_complement.rs
  type BitwiseComplementWarning (line 9) | pub struct BitwiseComplementWarning {
    method into_report (line 15) | pub fn into_report(self) -> Report {
  function find_bitwise_complement (line 34) | pub fn find_bitwise_complement(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 46) | fn visit_statement(stmt: &Statement, reports: &mut ReportCollection) {
  function visit_expression (line 73) | fn visit_expression(expr: &Expression, reports: &mut ReportCollection) {
  function build_report (line 121) | fn build_report(meta: &Meta) -> Report {
  function test_bitwise_complement (line 134) | fn test_bitwise_complement() {
  function validate_reports (line 150) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/bn254_specific_circuit.rs
  constant PROBLEMATIC_GOLDILOCK_TEMPLATES (line 12) | const PROBLEMATIC_GOLDILOCK_TEMPLATES: [&str; 26] = [
  constant PROBLEMATIC_BLS12_381_TEMPLATES (line 41) | const PROBLEMATIC_BLS12_381_TEMPLATES: [&str; 13] = [
  type Bn254SpecificCircuitWarning (line 57) | pub struct Bn254SpecificCircuitWarning {
    method into_report (line 64) | pub fn into_report(self) -> Report {
  function find_bn254_specific_circuits (line 117) | pub fn find_bn254_specific_circuits(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 137) | fn visit_statement(
  function build_report (line 162) | fn build_report(meta: &Meta, name: &str) -> Report {
  function test_num2bits_strict (line 179) | fn test_num2bits_strict() {
  function validate_reports (line 209) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/config.rs
  constant COMPILER_VERSION (line 3) | pub const COMPILER_VERSION: Version = (2, 1, 4);
  constant DEFAULT_LEVEL (line 4) | pub const DEFAULT_LEVEL: &str = "WARNING";
  constant DEFAULT_CURVE (line 5) | pub const DEFAULT_CURVE: &str = "BN254";

FILE: program_analysis/src/constant_conditional.rs
  type ConstantBranchConditionWarning (line 10) | pub struct ConstantBranchConditionWarning {
    method into_report (line 17) | pub fn into_report(self) -> Report {
  function find_constant_conditional_statement (line 35) | pub fn find_constant_conditional_statement(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 47) | fn visit_statement(stmt: &Statement, reports: &mut ReportCollection) {
  function build_report (line 58) | fn build_report(meta: &Meta, value: bool) -> Report {
  function test_constant_conditional (line 75) | fn test_constant_conditional() {
  function validate_reports (line 105) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/constraint_analysis.rs
  type ConstraintAnalysis (line 14) | pub struct ConstraintAnalysis {
    method new (line 21) | fn new() -> ConstraintAnalysis {
    method add_definition (line 26) | fn add_definition(&mut self, var: &VariableUse) {
    method get_definition (line 42) | pub fn get_definition(&self, var: &VariableName) -> Option<VariableUse> {
    method definitions (line 46) | pub fn definitions(&self) -> impl Iterator<Item = &VariableUse> {
    method add_declaration (line 51) | fn add_declaration(&mut self, var: &VariableUse) {
    method get_declaration (line 56) | pub fn get_declaration(&self, var: &VariableName) -> Option<VariableUs...
    method declarations (line 60) | pub fn declarations(&self) -> impl Iterator<Item = &VariableUse> {
    method add_constraint_step (line 65) | fn add_constraint_step(&mut self, source: &VariableName, sink: &Variab...
    method single_step_constraint (line 71) | pub fn single_step_constraint(&self, source: &VariableName) -> HashSet...
    method multi_step_constraint (line 76) | pub fn multi_step_constraint(&self, source: &VariableName) -> HashSet<...
    method constrains_any (line 87) | pub fn constrains_any(&self, source: &VariableName, sinks: &HashSet<Va...
    method constrained_variables (line 93) | pub fn constrained_variables(&self) -> HashSet<VariableName> {
  function run_constraint_analysis (line 98) | pub fn run_constraint_analysis(cfg: &Cfg) -> ConstraintAnalysis {
  function test_single_step_constraint (line 149) | fn test_single_step_constraint() {
  function validate_constraints (line 189) | fn validate_constraints(src: &str, sources: &[VariableName], sinks: &[us...

FILE: program_analysis/src/definition_complexity.rs
  type TooManyArgumentsWarning (line 6) | pub struct TooManyArgumentsWarning {
    method into_report (line 14) | pub fn into_report(self) -> Report {
  type CyclomaticComplexityWarning (line 33) | pub struct CyclomaticComplexityWarning {
    method into_report (line 39) | pub fn into_report(self) -> Report {
  constant MAX_NOF_PARAMETERS (line 51) | const MAX_NOF_PARAMETERS: usize = 7;
  constant MAX_CYCLOMATIC_COMPLEXITY (line 52) | const MAX_CYCLOMATIC_COMPLEXITY: usize = 20;
  function run_complexity_analysis (line 54) | pub fn run_complexity_analysis(cfg: &Cfg) -> ReportCollection {
  function test_small_template (line 100) | fn test_small_template() {
  function validate_reports (line 111) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/field_arithmetic.rs
  type FieldElementArithmeticWarning (line 9) | pub struct FieldElementArithmeticWarning {
    method into_report (line 15) | pub fn into_report(self) -> Report {
  function find_field_element_arithmetic (line 35) | pub fn find_field_element_arithmetic(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 47) | fn visit_statement(stmt: &Statement, reports: &mut ReportCollection) {
  function visit_expression (line 74) | fn visit_expression(expr: &Expression, reports: &mut ReportCollection) {
  function is_arithmetic_infix_op (line 121) | fn is_arithmetic_infix_op(op: &ExpressionInfixOpcode) -> bool {
  function may_overflow (line 129) | fn may_overflow(op: &ExpressionInfixOpcode) -> bool {
  function build_report (line 135) | fn build_report(meta: &Meta) -> Report {
  function test_field_arithmetic (line 148) | fn test_field_arithmetic() {
  function validate_reports (line 159) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/field_comparisons.rs
  type FieldElementComparisonWarning (line 9) | pub struct FieldElementComparisonWarning {
    method into_report (line 15) | pub fn into_report(self) -> Report {
  function find_field_element_comparisons (line 44) | pub fn find_field_element_comparisons(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 56) | fn visit_statement(stmt: &Statement, reports: &mut ReportCollection) {
  function visit_expression (line 83) | fn visit_expression(expr: &Expression, reports: &mut ReportCollection) {
  function is_comparison_op (line 130) | fn is_comparison_op(op: &ExpressionInfixOpcode) -> bool {
  function build_report (line 135) | fn build_report(meta: &Meta) -> Report {
  function test_field_comparisons (line 148) | fn test_field_comparisons() {
  function validate_reports (line 165) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/lib.rs
  type AnalysisPass (line 33) | type AnalysisPass = dyn Fn(&mut dyn AnalysisContext, &Cfg) -> ReportColl...
  function get_analysis_passes (line 35) | pub fn get_analysis_passes() -> Vec<Box<AnalysisPass>> {

FILE: program_analysis/src/nonstrict_binary_conversion.rs
  type NonStrictBinaryConversionWarning (line 12) | pub enum NonStrictBinaryConversionWarning {
    method into_report (line 18) | pub fn into_report(self) -> Report {
  function find_nonstrict_binary_conversion (line 64) | pub fn find_nonstrict_binary_conversion(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 86) | fn visit_statement(stmt: &Statement, prime_size: &BigInt, reports: &mut ...
  function build_num2bits (line 129) | fn build_num2bits(meta: &Meta) -> Report {
  function build_bits2num (line 137) | fn build_bits2num(meta: &Meta) -> Report {
  function test_nonstrict_num2bits (line 153) | fn test_nonstrict_num2bits() {
  function validate_reports (line 184) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/side_effect_analysis.rs
  type UnusedVariableWarning (line 16) | pub struct UnusedVariableWarning {
    method into_report (line 21) | pub fn into_report(self) -> Report {
  type UnconstrainedSignalWarning (line 39) | pub struct UnconstrainedSignalWarning {
    method into_report (line 47) | pub fn into_report(self) -> Report {
  type UnusedSignalWarning (line 82) | pub struct UnusedSignalWarning {
    method into_report (line 90) | pub fn into_report(self) -> Report {
  type UnusedParameterWarning (line 125) | pub struct UnusedParameterWarning {
    method into_report (line 131) | pub fn into_report(self) -> Report {
  type VariableWithoutSideEffectsWarning (line 151) | pub struct VariableWithoutSideEffectsWarning {
    method into_report (line 157) | pub fn into_report(self) -> Report {
  type ParamWithoutSideEffectsWarning (line 184) | pub struct ParamWithoutSideEffectsWarning {
    method into_report (line 190) | pub fn into_report(self) -> Report {
  function run_side_effect_analysis (line 228) | pub fn run_side_effect_analysis(cfg: &Cfg) -> ReportCollection {
  function build_unused_variable (line 383) | fn build_unused_variable(definition: &VariableUse) -> Report {
  function build_unused_param (line 387) | fn build_unused_param(definition: &VariableUse, cfg_name: &str) -> Report {
  function build_unused_signal (line 392) | fn build_unused_signal(declaration: &Declaration) -> Report {
  function build_unconstrained_signal (line 402) | fn build_unconstrained_signal(declaration: &Declaration) -> Report {
  function build_variable_without_side_effect (line 412) | fn build_variable_without_side_effect(
  function build_param_without_side_effect (line 420) | fn build_param_without_side_effect(definition: &VariableUse, cfg_type: &...
  function dimensions_to_string (line 425) | fn dimensions_to_string(dimensions: &[Expression]) -> String {
  function test_side_effect_analysis (line 442) | fn test_side_effect_analysis() {
  function validate_reports (line 558) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/signal_assignments.rs
  type SignalAssignmentWarning (line 12) | pub struct SignalAssignmentWarning {
    method into_report (line 20) | pub fn into_report(self) -> Report {
  type UnecessarySignalAssignmentWarning (line 63) | pub struct UnecessarySignalAssignmentWarning {
    method into_report (line 70) | pub fn into_report(self) -> Report {
  type AssignmentSet (line 96) | type AssignmentSet = HashSet<Assignment>;
  type Assignment (line 99) | struct Assignment {
    method new (line 107) | fn new(
    method is_quadratic (line 121) | fn is_quadratic(&self) -> bool {
  type ConstraintSet (line 130) | type ConstraintSet = HashSet<Constraint>;
  type Constraint (line 133) | struct Constraint {
    method new (line 140) | fn new(meta: &Meta, lhe: &Expression, rhe: &Expression) -> Constraint {
  type SignalUse (line 148) | struct SignalUse {
    method new (line 155) | fn new() -> SignalUse {
    method add_assignment (line 160) | fn add_assignment(
    method add_constraint (line 172) | fn add_constraint(&mut self, lhe: &Expression, rhe: &Expression, meta:...
    method get_assignments (line 178) | fn get_assignments(&self) -> &AssignmentSet {
    method get_constraints (line 183) | fn get_constraints(&self, signal: &VariableName, access: &Vec<AccessTy...
    method get_constraint_metas (line 197) | fn get_constraint_metas(&self, signal: &VariableName, access: &Vec<Acc...
  function find_signal_assignments (line 208) | pub fn find_signal_assignments(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 245) | fn visit_statement(stmt: &Statement, signal_use: &mut SignalUse) {
  function build_unecessary_assignment_report (line 273) | fn build_unecessary_assignment_report(
  function build_assignment_report (line 286) | fn build_assignment_report(
  function access_to_string (line 302) | fn access_to_string(access: &[AccessType]) -> String {
  function test_signal_assignments (line 314) | fn test_signal_assignments() {
  function validate_reports (line 358) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/taint_analysis.rs
  type TaintAnalysis (line 12) | pub struct TaintAnalysis {
    method new (line 19) | fn new(parameters: &Parameters) -> TaintAnalysis {
    method add_definition (line 32) | fn add_definition(&mut self, var: &VariableUse) {
    method get_definition (line 48) | pub fn get_definition(&self, var: &VariableName) -> Option<VariableUse> {
    method definitions (line 52) | pub fn definitions(&self) -> impl Iterator<Item = &VariableUse> {
    method add_declaration (line 57) | fn add_declaration(&mut self, var: &VariableUse) {
    method get_declaration (line 62) | pub fn get_declaration(&self, var: &VariableName) -> Option<VariableUs...
    method declarations (line 66) | pub fn declarations(&self) -> impl Iterator<Item = &VariableUse> {
    method add_taint_step (line 71) | fn add_taint_step(&mut self, source: &VariableName, sink: &VariableNam...
    method single_step_taint (line 77) | pub fn single_step_taint(&self, source: &VariableName) -> HashSet<Vari...
    method multi_step_taint (line 82) | pub fn multi_step_taint(&self, source: &VariableName) -> HashSet<Varia...
    method taints_any (line 93) | pub fn taints_any(&self, source: &VariableName, sinks: &HashSet<Variab...
  function run_taint_analysis (line 98) | pub fn run_taint_analysis(cfg: &Cfg) -> TaintAnalysis {
  function test_taint_analysis (line 181) | fn test_taint_analysis() {
  function validate_taint (line 233) | fn validate_taint(src: &str, taint_map: &HashMap<&str, HashSet<String>>) {

FILE: program_analysis/src/unconstrained_division.rs
  type UnconstrainedDivisionWarning (line 15) | pub struct UnconstrainedDivisionWarning {
    method into_report (line 22) | pub fn into_report(self) -> Report {
  type VariableAccess (line 39) | struct VariableAccess {
    method new (line 45) | fn new(var: &VariableName, access: &[AccessType]) -> Self {
  type Component (line 53) | struct Component {
    method new (line 59) | fn new() -> Self {
    method ensures_nonzero (line 63) | fn ensures_nonzero(&self, expr: &Expression) -> bool {
    method output (line 74) | fn output(&self) -> Option<bool> {
  function find_unconstrained_division (line 100) | pub fn find_unconstrained_division(cfg: &Cfg) -> ReportCollection {
  function update_divisors (line 135) | fn update_divisors(stmt: &Statement, divisors: &mut Vec<Expression>) {
  function update_constraints (line 160) | fn update_constraints(stmt: &Statement, constraints: &mut HashMap<Variab...
  function build_report (line 242) | fn build_report(divisor: &Expression) -> Report {
  function vec_to_display (line 252) | fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {
  function test_unconstrained_less_than (line 264) | fn test_unconstrained_less_than() {
  function validate_reports (line 368) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/unconstrained_less_than.rs
  type UnconstrainedLessThanWarning (line 13) | pub struct UnconstrainedLessThanWarning {
    method primary_meta (line 18) | fn primary_meta(&self) -> &Meta {
    method into_report (line 22) | pub fn into_report(self) -> Report {
  type VariableAccess (line 47) | struct VariableAccess {
    method new (line 53) | fn new(var: &VariableName, access: &[AccessType]) -> Self {
  type Component (line 61) | enum Component {
    method less_than (line 67) | fn less_than() -> Self {
    method num_2_bits (line 71) | fn num_2_bits(bit_size: &Expression) -> Self {
  type ComponentInput (line 78) | enum ComponentInput {
    method less_than (line 84) | fn less_than(value: &Expression) -> Self {
    method num_2_bits (line 88) | fn num_2_bits(value: &Expression, bit_size: &Expression) -> Self {
  type ConstraintData (line 95) | struct ConstraintData {
  function find_unconstrained_less_than (line 117) | pub fn find_unconstrained_less_than(cfg: &Cfg) -> ReportCollection {
  function update_components (line 175) | fn update_components(stmt: &Statement, components: &mut HashMap<Variable...
  function update_inputs (line 212) | fn update_inputs(
  function build_report (line 264) | fn build_report(value: &Expression, data: &ConstraintData) -> Report {
  function vec_to_display (line 273) | fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {
  function test_unconstrained_less_than (line 285) | fn test_unconstrained_less_than() {
  function validate_reports (line 372) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/under_constrained_signals.rs
  constant MIN_CONSTRAINT_COUNT (line 13) | const MIN_CONSTRAINT_COUNT: usize = 2;
  type ConstraintLocation (line 16) | enum ConstraintLocation {
    method file_location (line 22) | fn file_location(&self) -> Option<FileLocation> {
  type ConstraintLocations (line 31) | type ConstraintLocations = HashMap<VariableName, Vec<ConstraintLocation>>;
  type UnderConstrainedSignalWarning (line 33) | pub struct UnderConstrainedSignalWarning {
    method into_report (line 42) | pub fn into_report(self) -> Report {
  function find_under_constrained_signals (line 90) | pub fn find_under_constrained_signals(cfg: &Cfg) -> ReportCollection {
  function visit_statement (line 143) | fn visit_statement(
  function build_report (line 171) | fn build_report(
  function test_under_constrained_signals (line 196) | fn test_under_constrained_signals() {
  function validate_reports (line 260) | fn validate_reports(src: &str, expected_len: usize) {

FILE: program_analysis/src/unused_output_signal.rs
  constant ALLOW_LIST (line 17) | const ALLOW_LIST: [&str; 1] = ["Num2Bits"];
  type UnusedOutputSignalWarning (line 19) | struct UnusedOutputSignalWarning {
    method into_report (line 32) | pub fn into_report(self) -> Report {
  type VariableAccess (line 52) | struct VariableAccess {
    method new (line 58) | fn new(var: &VariableName, access: &[AccessType]) -> Self {
  type MaybeEqual (line 66) | trait MaybeEqual {
    method maybe_equal (line 67) | fn maybe_equal(&self, other: &Self) -> bool;
    method maybe_equal (line 80) | fn maybe_equal(&self, other: &VariableAccess) -> bool {
  type MaybeContains (line 119) | trait MaybeContains<T> {
    method maybe_contains (line 120) | fn maybe_contains(&self, element: &T) -> bool;
  function maybe_contains (line 127) | fn maybe_contains(&self, element: &T) -> bool {
  type ComponentData (line 132) | struct ComponentData {
    method new (line 140) | pub fn new(
  type SignalData (line 155) | struct SignalData {
    method new (line 163) | pub fn new(
  function find_unused_output_signals (line 178) | pub fn find_unused_output_signals(
  function maybe_accesses (line 256) | fn maybe_accesses(accesses: &Vec<VariableAccess>, signal_access: &Variab...
  function visit_statement (line 272) | fn visit_statement(
  function visit_expression (line 309) | fn visit_expression(expr: &Expression, accesses: &mut Vec<VariableAccess...
  function build_report (line 345) | fn build_report(
  function test_maybe_equal (line 379) | fn test_maybe_equal() {
  function test_maybe_accesses (line 439) | fn test_maybe_accesses() {}
  function test_unused_output_signal (line 442) | fn test_unused_output_signal() {
  function validate_reports (line 613) | fn validate_reports(name: &str, src: &[&str], expected_len: usize) {

FILE: program_structure/src/abstract_syntax_tree/assign_op_impl.rs
  method is_signal_operator (line 4) | pub fn is_signal_operator(self) -> bool {

FILE: program_structure/src/abstract_syntax_tree/ast.rs
  type FillMeta (line 5) | pub trait FillMeta {
    method fill (line 6) | fn fill(&mut self, file_id: usize, elem_id: &mut usize);
  type MainComponent (line 9) | pub type MainComponent = (Vec<String>, Expression);
  function build_main_component (line 11) | pub fn build_main_component(public: Vec<String>, call: Expression) -> Ma...
  type Version (line 15) | pub type Version = (usize, usize, usize);
  type TagList (line 16) | pub type TagList = Vec<String>;
  type Include (line 19) | pub struct Include {
  function build_include (line 24) | pub fn build_include(meta: Meta, path: String) -> Include {
  type Meta (line 29) | pub struct Meta {
    method new (line 40) | pub fn new(start: usize, end: usize) -> Meta {
    method change_location (line 52) | pub fn change_location(&mut self, location: FileLocation, file_id: Opt...
    method get_start (line 56) | pub fn get_start(&self) -> usize {
    method get_end (line 59) | pub fn get_end(&self) -> usize {
    method get_file_id (line 62) | pub fn get_file_id(&self) -> usize {
    method get_memory_knowledge (line 69) | pub fn get_memory_knowledge(&self) -> &MemoryKnowledge {
    method get_type_knowledge (line 72) | pub fn get_type_knowledge(&self) -> &TypeKnowledge {
    method get_mut_memory_knowledge (line 75) | pub fn get_mut_memory_knowledge(&mut self) -> &mut MemoryKnowledge {
    method get_mut_type_knowledge (line 78) | pub fn get_mut_type_knowledge(&mut self) -> &mut TypeKnowledge {
    method file_location (line 81) | pub fn file_location(&self) -> FileLocation {
    method set_file_id (line 84) | pub fn set_file_id(&mut self, file_id: usize) {
  type AST (line 90) | pub struct AST {
    method new (line 100) | pub fn new(
  type Definition (line 124) | pub enum Definition {
    method name (line 165) | pub fn name(&self) -> String {
  function build_template (line 142) | pub fn build_template(
  function build_function (line 154) | pub fn build_function(
  type Statement (line 174) | pub enum Statement {
  type SignalElementType (line 235) | pub enum SignalElementType {
  type SignalType (line 242) | pub enum SignalType {
  type VariableType (line 249) | pub enum VariableType {
  type Expression (line 257) | pub enum Expression {
  type Access (line 316) | pub enum Access {
  function build_component_access (line 320) | pub fn build_component_access(acc: String) -> Access {
  function build_array_access (line 323) | pub fn build_array_access(expr: Expression) -> Access {
  type AssignOp (line 328) | pub enum AssignOp {
  type ExpressionInfixOpcode (line 335) | pub enum ExpressionInfixOpcode {
  type ExpressionPrefixOpcode (line 359) | pub enum ExpressionPrefixOpcode {
  type LogArgument (line 366) | pub enum LogArgument {
  function build_log_string (line 371) | pub fn build_log_string(acc: String) -> LogArgument {
  function build_log_expression (line 375) | pub fn build_log_expression(expr: Expression) -> LogArgument {
  type TypeReduction (line 381) | pub enum TypeReduction {
  type TypeKnowledge (line 389) | pub struct TypeKnowledge {
    method new (line 393) | pub fn new() -> TypeKnowledge {
    method set_reduces_to (line 396) | pub fn set_reduces_to(&mut self, reduces_to: TypeReduction) {
    method reduces_to (line 399) | pub fn reduces_to(&self) -> TypeReduction {
    method is_var (line 406) | pub fn is_var(&self) -> bool {
    method is_component (line 409) | pub fn is_component(&self) -> bool {
    method is_signal (line 412) | pub fn is_signal(&self) -> bool {
    method is_tag (line 415) | pub fn is_tag(&self) -> bool {
  type MemoryKnowledge (line 421) | pub struct MemoryKnowledge {
    method new (line 427) | pub fn new() -> MemoryKnowledge {
    method set_concrete_dimensions (line 430) | pub fn set_concrete_dimensions(&mut self, value: Vec<usize>) {
    method set_abstract_memory_address (line 434) | pub fn set_abstract_memory_address(&mut self, value: usize) {
    method concrete_dimensions (line 437) | pub fn concrete_dimensions(&self) -> &[usize] {
    method full_length (line 444) | pub fn full_length(&self) -> usize {
    method abstract_memory_address (line 451) | pub fn abstract_memory_address(&self) -> usize {

FILE: program_structure/src/abstract_syntax_tree/ast_impl.rs
  method get_includes (line 4) | pub fn get_includes(&self) -> &Vec<Include> {
  method get_version (line 8) | pub fn get_version(&self) -> &Option<Version> {
  method get_definitions (line 12) | pub fn get_definitions(&self) -> &Vec<Definition> {
  method decompose (line 15) | pub fn decompose(

FILE: program_structure/src/abstract_syntax_tree/ast_shortcuts.rs
  type Symbol (line 8) | pub struct Symbol {
  type TupleInit (line 14) | pub struct TupleInit {
  function assign_with_op_shortcut (line 18) | pub fn assign_with_op_shortcut(
  function plusplus (line 30) | pub fn plusplus(meta: Meta, variable: (String, Vec<Access>)) -> Statement {
  function subsub (line 35) | pub fn subsub(meta: Meta, variable: (String, Vec<Access>)) -> Statement {
  function for_into_while (line 40) | pub fn for_into_while(
  function split_declaration_into_single_nodes (line 52) | pub fn split_declaration_into_single_nodes(
  function split_declaration_into_single_nodes_and_multi_substitution (line 90) | pub fn split_declaration_into_single_nodes_and_multi_substitution(

FILE: program_structure/src/abstract_syntax_tree/expression_builders.rs
  function build_infix (line 5) | pub fn build_infix(
  function build_prefix (line 14) | pub fn build_prefix(meta: Meta, prefix_op: ExpressionPrefixOpcode, rhe: ...
  function build_inline_switch_op (line 18) | pub fn build_inline_switch_op(
  function build_parallel_op (line 32) | pub fn build_parallel_op(meta: Meta, rhe: Expression) -> Expression {
  function build_variable (line 36) | pub fn build_variable(meta: Meta, name: String, access: Vec<Access>) -> ...
  function build_number (line 40) | pub fn build_number(meta: Meta, value: BigInt) -> Expression {
  function build_call (line 44) | pub fn build_call(meta: Meta, id: String, args: Vec<Expression>) -> Expr...
  function build_anonymous_component (line 48) | pub fn build_anonymous_component(
  function build_array_in_line (line 59) | pub fn build_array_in_line(meta: Meta, values: Vec<Expression>) -> Expre...
  function build_tuple (line 63) | pub fn build_tuple(meta: Meta, values: Vec<Expression>) -> Expression {
  function unzip_3 (line 67) | pub fn unzip_3(

FILE: program_structure/src/abstract_syntax_tree/expression_impl.rs
  method meta (line 7) | pub fn meta(&self) -> &Meta {
  method meta_mut (line 22) | pub fn meta_mut(&mut self) -> &mut Meta {
  method is_array (line 38) | pub fn is_array(&self) -> bool {
  method is_infix (line 43) | pub fn is_infix(&self) -> bool {
  method is_prefix (line 48) | pub fn is_prefix(&self) -> bool {
  method is_switch (line 53) | pub fn is_switch(&self) -> bool {
  method is_variable (line 58) | pub fn is_variable(&self) -> bool {
  method is_number (line 63) | pub fn is_number(&self) -> bool {
  method is_call (line 68) | pub fn is_call(&self) -> bool {
  method is_parallel (line 73) | pub fn is_parallel(&self) -> bool {
  method is_tuple (line 78) | pub fn is_tuple(&self) -> bool {
  method is_anonymous_component (line 83) | pub fn is_anonymous_component(&self) -> bool {
  method make_anonymous_parallel (line 88) | pub fn make_anonymous_parallel(self) -> Expression {
  method fill (line 100) | fn fill(&mut self, file_id: usize, element_id: &mut usize) {
  function fill_number (line 125) | fn fill_number(meta: &mut Meta, file_id: usize, _element_id: &mut usize) {
  function fill_variable (line 129) | fn fill_variable(meta: &mut Meta, access: &mut [Access], file_id: usize,...
  function fill_infix (line 138) | fn fill_infix(
  function fill_prefix (line 150) | fn fill_prefix(meta: &mut Meta, rhe: &mut Expression, file_id: usize, el...
  function fill_inline_switch_op (line 155) | fn fill_inline_switch_op(
  function fill_call (line 169) | fn fill_call(meta: &mut Meta, args: &mut [Expression], file_id: usize, e...
  function fill_array_inline (line 176) | fn fill_array_inline(
  function fill_anonymous_component (line 188) | fn fill_anonymous_component(
  function fill_tuple (line 204) | fn fill_tuple(meta: &mut Meta, values: &mut [Expression], file_id: usize...
  function fill_parallel (line 211) | fn fill_parallel(meta: &mut Meta, rhe: &mut Expression, file_id: usize, ...
  method fmt (line 217) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 235) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 263) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 291) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 302) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  function vec_to_string (line 311) | fn vec_to_string(elems: &[Expression]) -> String {
  function signals_to_string (line 315) | fn signals_to_string(names: &Option<Vec<(AssignOp, String)>>, signals: &...

FILE: program_structure/src/abstract_syntax_tree/statement_builders.rs
  function build_conditional_block (line 4) | pub fn build_conditional_block(
  function build_while_block (line 13) | pub fn build_while_block(meta: Meta, cond: Expression, stmt: Statement) ...
  function build_initialization_block (line 17) | pub fn build_initialization_block(
  function build_block (line 24) | pub fn build_block(meta: Meta, stmts: Vec<Statement>) -> Statement {
  function build_return (line 28) | pub fn build_return(meta: Meta, value: Expression) -> Statement {
  function build_declaration (line 32) | pub fn build_declaration(
  function build_substitution (line 42) | pub fn build_substitution(
  function build_constraint_equality (line 52) | pub fn build_constraint_equality(meta: Meta, lhe: Expression, rhe: Expre...
  function build_log_call (line 56) | pub fn build_log_call(meta: Meta, args: Vec<LogArgument>) -> Statement {
  function split_string (line 71) | fn split_string(str: String) -> Vec<LogArgument> {
  function build_assert (line 83) | pub fn build_assert(meta: Meta, arg: Expression) -> Statement {
  function build_multi_substitution (line 87) | pub fn build_multi_substitution(
  function build_anonymous_component_statement (line 96) | pub fn build_anonymous_component_statement(meta: Meta, arg: Expression) ...

FILE: program_structure/src/abstract_syntax_tree/statement_impl.rs
  method get_meta (line 6) | pub fn get_meta(&self) -> &Meta {
  method get_mut_meta (line 22) | pub fn get_mut_meta(&mut self) -> &mut Meta {
  method is_if_then_else (line 39) | pub fn is_if_then_else(&self) -> bool {
  method is_while (line 44) | pub fn is_while(&self) -> bool {
  method is_return (line 49) | pub fn is_return(&self) -> bool {
  method is_initialization_block (line 54) | pub fn is_initialization_block(&self) -> bool {
  method is_declaration (line 59) | pub fn is_declaration(&self) -> bool {
  method is_substitution (line 64) | pub fn is_substitution(&self) -> bool {
  method is_multi_substitution (line 69) | pub fn is_multi_substitution(&self) -> bool {
  method is_constraint_equality (line 74) | pub fn is_constraint_equality(&self) -> bool {
  method is_log_call (line 79) | pub fn is_log_call(&self) -> bool {
  method is_block (line 84) | pub fn is_block(&self) -> bool {
  method is_assert (line 89) | pub fn is_assert(&self) -> bool {
  method fill (line 96) | fn fill(&mut self, file_id: usize, element_id: &mut usize) {
  function fill_conditional (line 128) | fn fill_conditional(
  function fill_while (line 144) | fn fill_while(
  function fill_return (line 156) | fn fill_return(meta: &mut Meta, value: &mut Expression, file_id: usize, ...
  function fill_initialization (line 161) | fn fill_initialization(
  function fill_declaration (line 173) | fn fill_declaration(
  function fill_substitution (line 185) | fn fill_substitution(
  function fill_multi_substitution (line 201) | fn fill_multi_substitution(
  function fill_constraint_equality (line 213) | fn fill_constraint_equality(
  function fill_log_call (line 225) | fn fill_log_call(
  function fill_block (line 239) | fn fill_block(meta: &mut Meta, stmts: &mut [Statement], file_id: usize, ...
  function fill_assert (line 246) | fn fill_assert(meta: &mut Meta, arg: &mut Expression, file_id: usize, el...
  method fmt (line 252) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 271) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
  method fmt (line 299) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 310) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 334) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  method fmt (line 345) | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
  function vec_to_string (line 354) | fn vec_to_string<T: ToString>(elems: &[T]) -> String {

FILE: program_structure/src/control_flow_graph/basic_block.rs
  type Index (line 13) | type Index = usize;
  type IndexSet (line 14) | type IndexSet = HashSet<Index>;
  type BasicBlock (line 17) | pub struct BasicBlock {
    method new (line 28) | pub fn new(meta: Meta, index: Index, loop_depth: usize) -> BasicBlock {
    method from_raw_parts (line 41) | pub fn from_raw_parts(
    method len (line 53) | pub fn len(&self) -> usize {
    method is_empty (line 58) | pub fn is_empty(&self) -> bool {
    method in_loop (line 63) | pub fn in_loop(&self) -> bool {
    method loop_depth (line 68) | pub fn loop_depth(&self) -> usize {
    method iter (line 72) | pub fn iter(&self) -> impl Iterator<Item = &Statement> {
    method iter_mut (line 76) | pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut Stateme...
    method index (line 80) | pub fn index(&self) -> Index {
    method meta (line 85) | pub fn meta(&self) -> &Meta {
    method meta_mut (line 90) | pub(crate) fn meta_mut(&mut self) -> &mut Meta {
    method statements (line 95) | pub fn statements(&self) -> &Vec<Statement> {
    method statements_mut (line 100) | pub(crate) fn statements_mut(&mut self) -> &mut Vec<Statement> {
    method prepend_statement (line 104) | pub(crate) fn prepend_statement(&mut self, stmt: Statement) {
    method append_statement (line 108) | pub(crate) fn append_statement(&mut self, stmt: Statement) {
    method predecessors (line 113) | pub fn predecessors(&self) -> &IndexSet {
    method successors (line 118) | pub fn successors(&self) -> &IndexSet {
    method add_predecessor (line 122) | pub(crate) fn add_predecessor(&mut self, predecessor: Index) {
    method add_successor (line 127) | pub(crate) fn add_successor(&mut self, successor: Index) {
    method propagate_degrees (line 132) | pub fn propagate_degrees(&mut self, env: &mut DegreeEnvironment) -> bo...
    method propagate_values (line 141) | pub fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
    method propagate_types (line 150) | pub fn propagate_types(&mut self, vars: &Declarations) {
    method fmt (line 236) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  method index (line 159) | fn index(&self) -> Index {
  method predecessors (line 162) | fn predecessors(&self) -> &IndexSet {
  method successors (line 165) | fn successors(&self) -> &IndexSet {
  method cache_variable_use (line 171) | fn cache_variable_use(&mut self) {
  method locals_read (line 210) | fn locals_read(&self) -> &VariableUses {
  method locals_written (line 214) | fn locals_written(&self) -> &VariableUses {
  method signals_read (line 218) | fn signals_read(&self) -> &VariableUses {
  method signals_written (line 222) | fn signals_written(&self) -> &VariableUses {
  method components_read (line 226) | fn components_read(&self) -> &VariableUses {
  method components_written (line 230) | fn components_written(&self) -> &VariableUses {

FILE: program_structure/src/control_flow_graph/cfg.rs
  type Index (line 23) | pub type Index = usize;
  constant MAX_ANALYSIS_DURATION (line 25) | const MAX_ANALYSIS_DURATION: Duration = Duration::from_secs(10);
  type DefinitionType (line 28) | pub enum DefinitionType {
    method fmt (line 35) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  type Cfg (line 44) | pub struct Cfg {
    method new (line 55) | pub(crate) fn new(
    method entry_block (line 76) | pub fn entry_block(&self) -> &BasicBlock {
    method get_basic_block (line 81) | pub fn get_basic_block(&self, index: Index) -> Option<&BasicBlock> {
    method len (line 87) | pub fn len(&self) -> usize {
    method is_empty (line 93) | pub fn is_empty(&self) -> bool {
    method into_ssa (line 98) | pub fn into_ssa(mut self) -> SSAResult<Cfg> {
    method name (line 140) | pub fn name(&self) -> &str {
    method file_id (line 146) | pub fn file_id(&self) -> &Option<FileID> {
    method definition_type (line 151) | pub fn definition_type(&self) -> &DefinitionType {
    method constants (line 156) | pub fn constants(&self) -> &UsefulConstants {
    method parameters (line 162) | pub fn parameters(&self) -> &Parameters {
    method declarations (line 168) | pub fn declarations(&self) -> &Declarations {
    method variables (line 173) | pub fn variables(&self) -> impl Iterator<Item = &VariableName> {
    method input_signals (line 178) | pub fn input_signals(&self) -> impl Iterator<Item = &VariableName> {
    method output_signals (line 190) | pub fn output_signals(&self) -> impl Iterator<Item = &VariableName> {
    method get_declaration (line 203) | pub fn get_declaration(&self, name: &VariableName) -> Option<&Declarat...
    method get_type (line 209) | pub fn get_type(&self, name: &VariableName) -> Option<&VariableType> {
    method iter (line 215) | pub fn iter(&self) -> impl Iterator<Item = &BasicBlock> {
    method iter_mut (line 220) | pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BasicBlock> {
    method get_dominators (line 228) | pub fn get_dominators(&self, basic_block: &BasicBlock) -> Vec<&BasicBl...
    method get_immediate_dominator (line 239) | pub fn get_immediate_dominator(&self, basic_block: &BasicBlock) -> Opt...
    method get_dominator_successors (line 248) | pub fn get_dominator_successors(&self, basic_block: &BasicBlock) -> Ve...
    method get_dominance_frontier (line 261) | pub fn get_dominance_frontier(&self, basic_block: &BasicBlock) -> Vec<...
    method get_predecessors (line 270) | pub fn get_predecessors(&self, basic_block: &BasicBlock) -> Vec<&Basic...
    method get_successors (line 295) | pub fn get_successors(&self, basic_block: &BasicBlock) -> Vec<&BasicBl...
    method get_interval (line 322) | pub fn get_interval(
    method get_true_branch (line 376) | pub fn get_true_branch(&self, header_block: &BasicBlock) -> Vec<&Basic...
    method get_false_branch (line 406) | pub fn get_false_branch(&self, header_block: &BasicBlock) -> Vec<&Basi...
    method cache_variable_use (line 440) | pub(crate) fn cache_variable_use(&mut self) {
    method propagate_degrees (line 448) | pub(crate) fn propagate_degrees(&mut self) {
    method propagate_values (line 480) | pub(crate) fn propagate_values(&mut self) {
    method propagate_types (line 500) | pub(crate) fn propagate_types(&mut self) {
    method fmt (line 512) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

FILE: program_structure/src/control_flow_graph/errors.rs
  type CFGError (line 10) | pub enum CFGError {
    method into_report (line 34) | pub fn into_report(self) -> Report {
    method from (line 113) | fn from(error: IRError) -> CFGError {
  type CFGResult (line 31) | pub type CFGResult<T> = Result<T, CFGError>;
  method from (line 126) | fn from(error: CFGError) -> Report {

FILE: program_structure/src/control_flow_graph/lifting.rs
  type Index (line 27) | type Index = usize;
  type IndexSet (line 28) | type IndexSet = HashSet<Index>;
  type BasicBlockVec (line 29) | type BasicBlockVec = NonEmptyVec<BasicBlock>;
  type IntoCfg (line 34) | pub trait IntoCfg {
    method into_cfg (line 35) | fn into_cfg(self, curve: &Curve, reports: &mut ReportCollection) -> CF...
    method into_cfg (line 42) | fn into_cfg(self, curve: &Curve, reports: &mut ReportCollection) -> CF...
  method from (line 49) | fn from(params: &Parameters) -> LiftingEnvironment {
  type IR (line 66) | type IR = Cfg;
  type Error (line 67) | type Error = CFGError;
  function try_lift (line 69) | fn try_lift(
  type IR (line 88) | type IR = Cfg;
  type Error (line 89) | type Error = CFGError;
  function try_lift (line 91) | fn try_lift(
  type IR (line 106) | type IR = Cfg;
  type Error (line 107) | type Error = CFGError;
  method try_lift (line 109) | fn try_lift(
  function try_lift_impl (line 146) | fn try_lift_impl(
  function build_basic_blocks (line 187) | pub(crate) fn build_basic_blocks(
  function visit_statement (line 212) | fn visit_statement(
  function complete_basic_block (line 371) | fn complete_basic_block(

FILE: program_structure/src/control_flow_graph/parameters.rs
  type Parameters (line 8) | pub struct Parameters {
    method new (line 16) | pub fn new(
    method file_id (line 29) | pub fn file_id(&self) -> &Option<FileID> {
    method file_location (line 34) | pub fn file_location(&self) -> &FileLocation {
    method len (line 39) | pub fn len(&self) -> usize {
    method is_empty (line 44) | pub fn is_empty(&self) -> bool {
    method iter (line 48) | pub fn iter(&self) -> impl Iterator<Item = &VariableName> {
    method iter_mut (line 52) | pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut VariableName> {
    method contains (line 56) | pub fn contains(&self, param_name: &VariableName) -> bool {
    method from (line 62) | fn from(function: &FunctionData) -> Parameters {
    method from (line 72) | fn from(template: &TemplateData) -> Parameters {
    method from (line 82) | fn from(definition: &Definition) -> Parameters {

FILE: program_structure/src/control_flow_graph/ssa_impl.rs
  type Version (line 16) | type Version = usize;
  type Config (line 18) | pub struct Config {}
  type Version (line 21) | type Version = Version;
  type Variable (line 22) | type Variable = VariableName;
  type Environment (line 23) | type Environment = Environment;
  type Statement (line 24) | type Statement = Statement;
  type BasicBlock (line 25) | type BasicBlock = BasicBlock;
  type Environment (line 30) | pub struct Environment {
    method new (line 45) | pub fn new(parameters: &Parameters, declarations: &Declarations) -> En...
    method get_current_version (line 58) | pub fn get_current_version(&self, name: &VariableName) -> Option<Versi...
    method get_version_range (line 65) | pub fn get_version_range(&self, name: &VariableName) -> Option<Range<V...
    method get_next_version (line 72) | fn get_next_version(&mut self, name: &VariableName) -> Version {
    method is_local (line 87) | fn is_local(&self, name: &VariableName) -> bool {
  method add_variable_scope (line 94) | fn add_variable_scope(&mut self) {
  method remove_variable_scope (line 99) | fn remove_variable_scope(&mut self) {
  method prepend_statement (line 105) | fn prepend_statement(&mut self, stmt: Statement) {
  method statements (line 109) | fn statements<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Statement> + ...
  method statements_mut (line 113) | fn statements_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut S...
  method variables_written (line 119) | fn variables_written(&self) -> HashSet<VariableName> {
  method new_phi_statement (line 123) | fn new_phi_statement(name: &VariableName, env: &Environment) -> Self {
  method is_phi_statement (line 147) | fn is_phi_statement(&self) -> bool {
  method is_phi_statement_for (line 153) | fn is_phi_statement_for(&self, name: &VariableName) -> bool {
  method ensure_phi_argument (line 162) | fn ensure_phi_argument(&mut self, env: &Environment) {
  method insert_ssa_variables (line 189) | fn insert_ssa_variables(&mut self, env: &mut Environment) -> SSAResult<(...
  function visit_expression (line 244) | fn visit_expression(expr: &mut Expression, env: &mut Environment) -> SSA...
  function update_declarations (line 369) | pub fn update_declarations(

FILE: program_structure/src/control_flow_graph/unique_vars.rs
  type Version (line 12) | type Version = usize;
  type Declaration (line 15) | struct Declaration {
    method new (line 21) | fn new(file_id: Option<FileID>, file_location: FileLocation) -> Declar...
    method file_id (line 25) | fn file_id(&self) -> Option<FileID> {
    method file_location (line 29) | fn file_location(&self) -> FileLocation {
  type DeclarationEnvironment (line 34) | struct DeclarationEnvironment {
    method new (line 49) | pub fn new() -> DeclarationEnvironment {
    method get_declaration (line 58) | pub fn get_declaration(&self, name: &str) -> Option<&Declaration> {
    method add_declaration (line 63) | pub fn add_declaration(
    method get_current_version (line 74) | pub fn get_current_version(&self, name: &str) -> Option<&Version> {
    method get_next_version (line 79) | fn get_next_version(&mut self, name: &str) -> Option<Version> {
    method add_variable_block (line 103) | pub fn add_variable_block(&mut self) {
    method remove_variable_block (line 109) | pub fn remove_variable_block(&mut self) {
    type Error (line 116) | type Error = CFGError;
    method try_from (line 118) | fn try_from(params: &Parameters) -> CFGResult<Self> {
  function ensure_unique_variables (line 178) | pub fn ensure_unique_variables(
  function visit_statement (line 191) | fn visit_statement(
  function visit_expression (line 284) | fn visit_expression(expr: &mut Expression, env: &DeclarationEnvironment) {
  function build_report (line 354) | fn build_report(name: &str, primary_meta: &Meta, secondary_decl: &Declar...

FILE: program_structure/src/intermediate_representation/declarations.rs
  type Declarations (line 8) | pub struct Declarations(HashMap<VariableName, Declaration>);
    method new (line 12) | pub fn new() -> Declarations {
    method add_declaration (line 16) | pub fn add_declaration(&mut self, declaration: &Declaration) {
    method get_declaration (line 25) | pub fn get_declaration(&self, name: &VariableName) -> Option<&Declarat...
    method get_type (line 30) | pub fn get_type(&self, name: &VariableName) -> Option<&VariableType> {
    method iter (line 34) | pub fn iter(&self) -> impl Iterator<Item = (&VariableName, &Declaratio...
    method len (line 39) | pub fn len(&self) -> usize {
    method is_empty (line 44) | pub fn is_empty(&self) -> bool {
  type Declaration (line 52) | pub struct Declaration {
    method new (line 61) | pub fn new(
    method file_id (line 78) | pub fn file_id(&self) -> Option<FileID> {
    method file_location (line 83) | pub fn file_location(&self) -> FileLocation {
    method variable_name (line 88) | pub fn variable_name(&self) -> &VariableName {
    method variable_type (line 93) | pub fn variable_type(&self) -> &VariableType {
    method dimensions (line 98) | pub fn dimensions(&self) -> &Vec<Expression> {

FILE: program_structure/src/intermediate_representation/degree_meta.rs
  type Degree (line 10) | pub enum Degree {
    method partial_cmp (line 19) | fn partial_cmp(&self, other: &Degree) -> Option<Ordering> {
    method add (line 47) | pub fn add(&self, other: &Degree) -> Degree {
    method infix_sub (line 51) | pub fn infix_sub(&self, other: &Degree) -> Degree {
    method mul (line 55) | pub fn mul(&self, other: &Degree) -> Degree {
    method pow (line 65) | pub fn pow(&self, other: &Degree) -> Degree {
    method div (line 74) | pub fn div(&self, other: &Degree) -> Degree {
    method int_div (line 83) | pub fn int_div(&self, other: &Degree) -> Degree {
    method modulo (line 92) | pub fn modulo(&self, other: &Degree) -> Degree {
    method shift_left (line 101) | pub fn shift_left(&self, other: &Degree) -> Degree {
    method shift_right (line 110) | pub fn shift_right(&self, other: &Degree) -> Degree {
    method lesser (line 119) | pub fn lesser(&self, other: &Degree) -> Degree {
    method greater (line 128) | pub fn greater(&self, other: &Degree) -> Degree {
    method lesser_eq (line 137) | pub fn lesser_eq(&self, other: &Degree) -> Degree {
    method greater_eq (line 146) | pub fn greater_eq(&self, other: &Degree) -> Degree {
    method equal (line 154) | pub fn equal(&self, other: &Degree) -> Degree {
    method not_equal (line 163) | pub fn not_equal(&self, other: &Degree) -> Degree {
    method bit_or (line 172) | pub fn bit_or(&self, other: &Degree) -> Degree {
    method bit_and (line 181) | pub fn bit_and(&self, other: &Degree) -> Degree {
    method bit_xor (line 190) | pub fn bit_xor(&self, other: &Degree) -> Degree {
    method bool_or (line 199) | pub fn bool_or(&self, other: &Degree) -> Degree {
    method bool_and (line 208) | pub fn bool_and(&self, other: &Degree) -> Degree {
    method prefix_sub (line 217) | pub fn prefix_sub(&self) -> Degree {
    method complement (line 221) | pub fn complement(&self) -> Degree {
    method bool_not (line 226) | pub fn bool_not(&self) -> Degree {
    method fmt (line 233) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  method cmp (line 26) | fn cmp(&self, other: &Degree) -> Ordering {
  type DegreeRange (line 246) | pub struct DegreeRange(Degree, Degree);
    method new (line 250) | pub fn new(start: Degree, end: Degree) -> DegreeRange {
    method start (line 255) | pub fn start(&self) -> Degree {
    method end (line 260) | pub fn end(&self) -> Degree {
    method contains (line 265) | pub fn contains(&self, degree: Degree) -> bool {
    method is_constant (line 271) | pub fn is_constant(&self) -> bool {
    method is_linear (line 277) | pub fn is_linear(&self) -> bool {
    method is_quadratic (line 283) | pub fn is_quadratic(&self) -> bool {
    method inf (line 290) | pub fn inf(&self, other: &DegreeRange) -> DegreeRange {
    method iter_inf (line 300) | pub fn iter_inf<'a, T: IntoIterator<Item = &'a DegreeRange>>(ranges: T...
    method iter_opt (line 316) | pub fn iter_opt<'a, T: IntoIterator<Item = Option<&'a DegreeRange>>>(
    method add (line 326) | pub fn add(&self, other: &DegreeRange) -> DegreeRange {
    method infix_sub (line 330) | pub fn infix_sub(&self, other: &DegreeRange) -> DegreeRange {
    method mul (line 334) | pub fn mul(&self, other: &DegreeRange) -> DegreeRange {
    method pow (line 338) | pub fn pow(&self, other: &DegreeRange) -> DegreeRange {
    method div (line 342) | pub fn div(&self, other: &DegreeRange) -> DegreeRange {
    method int_div (line 346) | pub fn int_div(&self, other: &DegreeRange) -> DegreeRange {
    method modulo (line 350) | pub fn modulo(&self, other: &DegreeRange) -> DegreeRange {
    method shift_left (line 354) | pub fn shift_left(&self, other: &DegreeRange) -> DegreeRange {
    method shift_right (line 361) | pub fn shift_right(&self, other: &DegreeRange) -> DegreeRange {
    method lesser (line 368) | pub fn lesser(&self, other: &DegreeRange) -> DegreeRange {
    method greater (line 372) | pub fn greater(&self, other: &DegreeRange) -> DegreeRange {
    method lesser_eq (line 376) | pub fn lesser_eq(&self, other: &DegreeRange) -> DegreeRange {
    method greater_eq (line 380) | pub fn greater_eq(&self, other: &DegreeRange) -> DegreeRange {
    method equal (line 386) | pub fn equal(&self, other: &DegreeRange) -> DegreeRange {
    method not_equal (line 390) | pub fn not_equal(&self, other: &DegreeRange) -> DegreeRange {
    method bit_or (line 394) | pub fn bit_or(&self, other: &DegreeRange) -> DegreeRange {
    method bit_and (line 398) | pub fn bit_and(&self, other: &DegreeRange) -> DegreeRange {
    method bit_xor (line 402) | pub fn bit_xor(&self, other: &DegreeRange) -> DegreeRange {
    method bool_or (line 406) | pub fn bool_or(&self, other: &DegreeRange) -> DegreeRange {
    method bool_and (line 410) | pub fn bool_and(&self, other: &DegreeRange) -> DegreeRange {
    method prefix_sub (line 414) | pub fn prefix_sub(&self) -> DegreeRange {
    method complement (line 418) | pub fn complement(&self) -> DegreeRange {
    method bool_not (line 422) | pub fn bool_not(&self) -> DegreeRange {
    method from (line 429) | fn from(degree: Degree) -> DegreeRange {
    method fmt (line 435) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  type DegreeEnvironment (line 443) | pub struct DegreeEnvironment {
    method new (line 452) | pub fn new() -> DegreeEnvironment {
    method set_degree (line 459) | pub fn set_degree(&mut self, var: &VariableName, range: &DegreeRange) ...
    method set_type (line 469) | pub fn set_type(&mut self, var: &VariableName, var_type: &VariableType) {
    method degree (line 477) | pub fn degree(&self, var: &VariableName) -> Option<&DegreeRange> {
    method is_local (line 483) | pub fn is_local(&self, var: &VariableName) -> bool {
  type DegreeMeta (line 488) | pub trait DegreeMeta {
    method propagate_degrees (line 491) | fn propagate_degrees(&mut self, env: &DegreeEnvironment) -> bool;
    method degree (line 495) | fn degree(&self) -> Option<&DegreeRange>;
  type DegreeKnowledge (line 499) | pub struct DegreeKnowledge {
    method new (line 506) | pub fn new() -> DegreeKnowledge {
    method set_degree (line 510) | pub fn set_degree(&mut self, range: &DegreeRange) -> bool {
    method degree (line 517) | pub fn degree(&self) -> Option<&DegreeRange> {
    method is_constant (line 524) | pub fn is_constant(&self) -> bool {
    method is_linear (line 535) | pub fn is_linear(&self) -> bool {
    method is_quadratic (line 546) | pub fn is_quadratic(&self) -> bool {
  function test_value_knowledge (line 560) | fn test_value_knowledge() {

FILE: program_structure/src/intermediate_representation/errors.rs
  type IRError (line 9) | pub enum IRError {
    method produce_report (line 19) | pub fn produce_report(error: Self) -> Report {
  type IRResult (line 16) | pub type IRResult<T> = Result<T, IRError>;
  method from (line 55) | fn from(error: IRError) -> Report {

FILE: program_structure/src/intermediate_representation/expression_impl.rs
  method meta (line 18) | pub fn meta(&self) -> &Meta {
  method meta_mut (line 35) | pub fn meta_mut(&mut self) -> &mut Meta {
  method eq (line 54) | fn eq(&self, other: &Expression) -> bool {
  method hash (line 97) | fn hash<H: Hasher>(&self, state: &mut H) {
  method propagate_degrees (line 141) | fn propagate_degrees(&mut self, env: &DegreeEnvironment) -> bool {
  method degree (line 270) | fn degree(&self) -> Option<&DegreeRange> {
  method propagate_types (line 276) | fn propagate_types(&mut self, vars: &Declarations) {
  method is_local (line 334) | fn is_local(&self) -> bool {
  method is_signal (line 338) | fn is_signal(&self) -> bool {
  method is_component (line 342) | fn is_component(&self) -> bool {
  method variable_type (line 346) | fn variable_type(&self) -> Option<&VariableType> {
  method cache_variable_use (line 352) | fn cache_variable_use(&mut self) {
  method locals_read (line 507) | fn locals_read(&self) -> &VariableUses {
  method locals_written (line 511) | fn locals_written(&self) -> &VariableUses {
  method signals_read (line 515) | fn signals_read(&self) -> &VariableUses {
  method signals_written (line 519) | fn signals_written(&self) -> &VariableUses {
  method components_read (line 523) | fn components_read(&self) -> &VariableUses {
  method components_written (line 527) | fn components_written(&self) -> &VariableUses {
  method propagate_values (line 533) | fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
  method is_constant (line 652) | fn is_constant(&self) -> bool {
  method is_boolean (line 656) | fn is_boolean(&self) -> bool {
  method is_field_element (line 660) | fn is_field_element(&self) -> bool {
  method value (line 664) | fn value(&self) -> Option<&ValueReduction> {
  method propagate_degrees (line 670) | fn propagate_degrees(
  method propagate_values (line 704) | fn propagate_values(
  method propagate_degrees (line 807) | fn propagate_degrees(&self, range: Option<&DegreeRange>) -> Option<Degre...
  method propagate_values (line 820) | fn propagate_values(
  method fmt (line 862) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 904) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 937) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 965) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 976) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 986) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  function vec_to_debug (line 996) | fn vec_to_debug<T: fmt::Debug>(elems: &[T], sep: &str) -> String {
  function vec_to_display (line 1001) | fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {
  function test_propagate_values (line 1012) | fn test_propagate_values() {

FILE: program_structure/src/intermediate_representation/ir.rs
  type Index (line 12) | type Index = usize;
  type Version (line 13) | type Version = usize;
  type Meta (line 16) | pub struct Meta {
    method new (line 27) | pub fn new(location: &FileLocation, file_id: &Option<FileID>) -> Meta {
    method start (line 39) | pub fn start(&self) -> usize {
    method end (line 44) | pub fn end(&self) -> usize {
    method file_id (line 49) | pub fn file_id(&self) -> Option<FileID> {
    method file_location (line 54) | pub fn file_location(&self) -> FileLocation {
    method degree_knowledge (line 59) | pub fn degree_knowledge(&self) -> &DegreeKnowledge {
    method type_knowledge (line 64) | pub fn type_knowledge(&self) -> &TypeKnowledge {
    method value_knowledge (line 69) | pub fn value_knowledge(&self) -> &ValueKnowledge {
    method variable_knowledge (line 74) | pub fn variable_knowledge(&self) -> &VariableKnowledge {
    method degree_knowledge_mut (line 79) | pub fn degree_knowledge_mut(&mut self) -> &mut DegreeKnowledge {
    method type_knowledge_mut (line 84) | pub fn type_knowledge_mut(&mut self) -> &mut TypeKnowledge {
    method value_knowledge_mut (line 89) | pub fn value_knowledge_mut(&mut self) -> &mut ValueKnowledge {
    method variable_knowledge_mut (line 94) | pub fn variable_knowledge_mut(&mut self) -> &mut VariableKnowledge {
    method hash (line 100) | fn hash<H>(&self, state: &mut H)
  method eq (line 111) | fn eq(&self, other: &Meta) -> bool {
  type Statement (line 121) | pub enum Statement {
  type Expression (line 167) | pub enum Expression {
  type TagList (line 208) | pub type TagList = Vec<String>;
  type VariableType (line 211) | pub enum VariableType {
    method fmt (line 219) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  type SignalType (line 242) | pub enum SignalType {
    method fmt (line 249) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  type VariableName (line 265) | pub struct VariableName {
    method from_string (line 281) | pub fn from_string<N: ToString>(name: N) -> VariableName {
    method name (line 286) | pub fn name(&self) -> &String {
    method suffix (line 291) | pub fn suffix(&self) -> &Option<String> {
    method version (line 296) | pub fn version(&self) -> &Option<Version> {
    method with_suffix (line 302) | pub fn with_suffix<S: ToString>(&self, suffix: S) -> VariableName {
    method with_version (line 310) | pub fn with_version(&self, version: Version) -> VariableName {
    method without_suffix (line 318) | pub fn without_suffix(&self) -> VariableName {
    method without_version (line 326) | pub fn without_version(&self) -> VariableName {
    method fmt (line 335) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
    method fmt (line 342) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  type AccessType (line 355) | pub enum AccessType {
  type AssignOp (line 361) | pub enum AssignOp {
  type ExpressionInfixOpcode (line 371) | pub enum ExpressionInfixOpcode {
  type ExpressionPrefixOpcode (line 395) | pub enum ExpressionPrefixOpcode {
  type LogArgument (line 402) | pub enum LogArgument {

FILE: program_structure/src/intermediate_representation/lifting.rs
  type TryLift (line 12) | pub(crate) trait TryLift<Context> {
    method try_lift (line 17) | fn try_lift(
  type LiftingEnvironment (line 25) | pub(crate) struct LiftingEnvironment {
    method new (line 32) | pub fn new() -> LiftingEnvironment {
    method add_declaration (line 36) | pub fn add_declaration(&mut self, declaration: &Declaration) {
  method from (line 42) | fn from(env: LiftingEnvironment) -> Declarations {
  type IR (line 52) | type IR = ir::Statement;
  type Error (line 53) | type Error = IRError;
  function try_lift (line 55) | fn try_lift(&self, _: (), reports: &mut ReportCollection) -> IRResult<Se...
  type IR (line 129) | type IR = ir::Expression;
  type Error (line 130) | type Error = IRError;
  function try_lift (line 132) | fn try_lift(&self, _: (), reports: &mut ReportCollection) -> IRResult<Se...
  type IR (line 201) | type IR = ir::Meta;
  type Error (line 202) | type Error = IRError;
  function try_lift (line 204) | fn try_lift(&self, _: (), _: &mut ReportCollection) -> IRResult<Self::IR> {
  type IR (line 211) | type IR = ir::VariableType;
  type Error (line 212) | type Error = IRError;
  function try_lift (line 214) | fn try_lift(&self, _: (), reports: &mut ReportCollection) -> IRResult<Se...
  type IR (line 228) | type IR = ir::SignalType;
  type Error (line 229) | type Error = IRError;
  function try_lift (line 231) | fn try_lift(&self, _: (), _: &mut ReportCollection) -> IRResult<Self::IR> {
  type IR (line 242) | type IR = ir::VariableName;
  type Error (line 243) | type Error = IRError;
  method try_lift (line 245) | fn try_lift(&self, meta: &ast::Meta, _: &mut ReportCollection) -> IRResu...
  type IR (line 265) | type IR = ir::AccessType;
  type Error (line 266) | type Error = IRError;
  function try_lift (line 268) | fn try_lift(&self, _: (), reports: &mut ReportCollection) -> IRResult<Se...
  type IR (line 280) | type IR = ir::AssignOp;
  type Error (line 281) | type Error = IRError;
  function try_lift (line 283) | fn try_lift(&self, _: (), _: &mut ReportCollection) -> IRResult<Self::IR> {
  type IR (line 294) | type IR = ir::ExpressionPrefixOpcode;
  type Error (line 295) | type Error = IRError;
  function try_lift (line 297) | fn try_lift(&self, _: (), _: &mut ReportCollection) -> IRResult<Self::IR> {
  type IR (line 308) | type IR = ir::ExpressionInfixOpcode;
  type Error (line 309) | type Error = IRError;
  function try_lift (line 311) | fn try_lift(&self, _: (), _: &mut ReportCollection) -> IRResult<Self::IR> {
  type IR (line 338) | type IR = ir::LogArgument;
  type Error (line 339) | type Error = IRError;
  method try_lift (line 341) | fn try_lift(&self, _: (), reports: &mut ReportCollection) -> IRResult<Se...

FILE: program_structure/src/intermediate_representation/statement_impl.rs
  method meta (line 13) | pub fn meta(&self) -> &Meta {
  method meta_mut (line 27) | pub fn meta_mut(&mut self) -> &mut Meta {
  method propagate_degrees (line 40) | pub fn propagate_degrees(&mut self, env: &mut DegreeEnvironment) -> bool {
  method propagate_values (line 87) | pub fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool {
  method propagate_types (line 130) | pub fn propagate_types(&mut self, vars: &Declarations) {
  method fmt (line 173) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  method fmt (line 206) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  method fmt (line 242) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 253) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method fmt (line 263) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  method cache_variable_use (line 273) | fn cache_variable_use(&mut self) {
  method locals_read (line 376) | fn locals_read(&self) -> &VariableUses {
  method locals_written (line 380) | fn locals_written(&self) -> &VariableUses {
  method signals_read (line 384) | fn signals_read(&self) -> &VariableUses {
  method signals_written (line 388) | fn signals_written(&self) -> &VariableUses {
  method components_read (line 392) | fn components_read(&self) -> &VariableUses {
  method components_written (line 396) | fn components_written(&self) -> &VariableUses {
  function vec_to_debug (line 402) | fn vec_to_debug<T: fmt::Debug>(elems: &[T], sep: &str) -> String {
  function vec_to_display (line 407) | fn vec_to_display<T: fmt::Display>(elems: &[T], sep: &str) -> String {

FILE: program_structure/src/intermediate_representation/type_meta.rs
  type TypeMeta (line 4) | pub trait TypeMeta {
    method propagate_types (line 6) | fn propagate_types(&mut self, vars: &Declarations);
    method is_local (line 10) | fn is_local(&self) -> bool;
    method is_signal (line 14) | fn is_signal(&self) -> bool;
    method is_component (line 18) | fn is_component(&self) -> bool;
    method variable_type (line 23) | fn variable_type(&self) -> Option<&VariableType>;
  type TypeKnowledge (line 27) | pub struct TypeKnowledge {
    method new (line 33) | pub fn new() -> TypeKnowledge {
    method set_variable_type (line 38) | pub fn set_variable_type(&mut self, var_type: &VariableType) {
    method variable_type (line 45) | pub fn variable_type(&self) -> Option<&VariableType> {
    method is_local (line 51) | pub fn is_local(&self) -> bool {
    method is_signal (line 57) | pub fn is_signal(&self) -> bool {
    method is_component (line 63) | pub fn is_component(&self) -> bool {

FILE: program_structure/src/intermediate_representation/value_meta.rs
  type ValueEnvironment (line 10) | pub struct ValueEnvironment {
    method new (line 16) | pub fn new(constants: &UsefulConstants) -> ValueEnvironment {
    method add_variable (line 26) | pub fn add_variable(&mut self, name: &VariableName, value: &ValueReduc...
    method get_variable (line 36) | pub fn get_variable(&self, name: &VariableName) -> Option<&ValueReduct...
    method prime (line 41) | pub fn prime(&self) -> &BigInt {
  type ValueMeta (line 46) | pub trait ValueMeta {
    method propagate_values (line 49) | fn propagate_values(&mut self, env: &mut ValueEnvironment) -> bool;
    method is_constant (line 53) | fn is_constant(&self) -> bool;
    method is_boolean (line 57) | fn is_boolean(&self) -> bool;
    method is_field_element (line 61) | fn is_field_element(&self) -> bool;
    method value (line 65) | fn value(&self) -> Option<&ValueReduction>;
  type ValueReduction (line 69) | pub enum ValueReduction {
    method fmt (line 75) | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  type ValueKnowledge (line 85) | pub struct ValueKnowledge {
    method new (line 91) | pub fn new() -> ValueKnowledge {
    method set_reduces_to (line 97) | pub fn set_reduces_to(&mut self, reduces_to: ValueReduction) -> bool {
    method get_reduces_to (line 105) | pub fn get_reduces_to(&self) -> Option<&ValueReduction> {
    method is_constant (line 111) | pub fn is_constant(&self) -> bool {
    method is_boolean (line 117) | pub fn is_boolean(&self) -> bool {
    method is_field_element (line 124) | pub fn is_field_element(&self) -> bool {
  function test_value_knowledge (line 139) | fn test_value_knowledge() {

FILE: program_structure/src/intermediate_representation/variable_meta.rs
  type VariableUse (line 8) | pub struct VariableUse {
    method new (line 15) | pub fn new(meta: &Meta, name: &VariableName, access: &[AccessType]) ->...
    method meta (line 19) | pub fn meta(&self) -> &Meta {
    method name (line 23) | pub fn name(&self) -> &VariableName {
    method access (line 27) | pub fn access(&self) -> &Vec<AccessType> {
    method fmt (line 33) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  type VariableUses (line 42) | pub type VariableUses = HashSet<VariableUse>;
  type VariableMeta (line 44) | pub trait VariableMeta {
    method cache_variable_use (line 49) | fn cache_variable_use(&mut self);
    method locals_read (line 53) | fn locals_read(&self) -> &VariableUses;
    method locals_written (line 57) | fn locals_written(&self) -> &VariableUses;
    method signals_read (line 62) | fn signals_read(&self) -> &VariableUses;
    method signals_written (line 67) | fn signals_written(&self) -> &VariableUses;
    method components_read (line 72) | fn components_read(&self) -> &VariableUses;
    method components_written (line 78) | fn components_written(&self) -> &VariableUses;
    method variables_read (line 83) | fn variables_read<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Variabl...
    method variables_written (line 93) | fn variables_written<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Vari...
    method variables_used (line 102) | fn variables_used<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Variabl...
  type VariableKnowledge (line 108) | pub struct VariableKnowledge {
    method new (line 119) | pub fn new() -> VariableKnowledge {
    method set_locals_read (line 123) | pub fn set_locals_read(&mut self, uses: &VariableUses) -> &mut Variabl...
    method set_locals_written (line 128) | pub fn set_locals_written(&mut self, uses: &VariableUses) -> &mut Vari...
    method set_signals_read (line 133) | pub fn set_signals_read(&mut self, uses: &VariableUses) -> &mut Variab...
    method set_signals_written (line 138) | pub fn set_signals_written(&mut self, uses: &VariableUses) -> &mut Var...
    method set_components_read (line 143) | pub fn set_components_read(&mut self, uses: &VariableUses) -> &mut Var...
    method set_components_written (line 148) | pub fn set_components_written(&mut self, uses: &VariableUses) -> &mut ...
    method locals_read (line 154) | pub fn locals_read(&self) -> &VariableUses {
    method locals_written (line 159) | pub fn locals_written(&self) -> &VariableUses {
    method signals_read (line 166) | pub fn signals_read(&self) -> &VariableUses {
    method signals_written (line 173) | pub fn signals_written(&self) -> &VariableUses {
    method components_read (line 180) | pub fn components_read(&self) -> &VariableUses {
    method components_written (line 187) | pub fn components_written(&self) -> &VariableUses {
    method variables_read (line 194) | pub fn variables_read<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Var...
    method variables_written (line 202) | pub fn variables_written<'a>(&'a self) -> Box<dyn Iterator<Item = &'a ...
    method variables_used (line 210) | pub fn variables_used<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Var...

FILE: program_structure/src/program_library/file_definition.rs
  type FileSource (line 4) | pub type FileSource = String;
  type FilePath (line 5) | pub type FilePath = String;
  type FileID (line 6) | pub type FileID = usize;
  type FileLocation (line 7) | pub type FileLocation = Range<usize>;
  type FileStorage (line 8) | type FileStorage = SimpleFiles<FilePath, FileSource>;
  type FileLibrary (line 11) | pub struct FileLibrary {
    method new (line 23) | pub fn new() -> FileLibrary {
    method add_file (line 26) | pub fn add_file(
    method get_line (line 39) | pub fn get_line(&self, start: usize, file_id: FileID) -> Option<usize> {
    method to_storage (line 43) | pub fn to_storage(&self) -> &FileStorage {
    method user_inputs (line 47) | pub fn user_inputs(&self) -> &HashSet<FileID> {
    method is_user_input (line 51) | pub fn is_user_input(&self, file_id: FileID) -> bool {
    method get_files (line 55) | fn get_files(&self) -> &FileStorage {
    method get_mut_files (line 59) | fn get_mut_files(&mut self) -> &mut FileStorage {
  method default (line 17) | fn default() -> Self {
  function generate_file_location (line 64) | pub fn generate_file_location(start: usize, end: usize) -> FileLocation {

FILE: program_structure/src/program_library/function_data.rs
  type FunctionInfo (line 6) | pub type FunctionInfo = HashMap<String, FunctionData>;
  type FunctionData (line 9) | pub struct FunctionData {
    method new (line 19) | pub fn new(
    method get_file_id (line 31) | pub fn get_file_id(&self) -> FileID {
    method get_body (line 34) | pub fn get_body(&self) -> &Statement {
    method get_body_as_vec (line 37) | pub fn get_body_as_vec(&self) -> &Vec<Statement> {
    method get_mut_body (line 43) | pub fn get_mut_body(&mut self) -> &mut Statement {
    method replace_body (line 46) | pub fn replace_body(&mut self, new: Statement) -> Statement {
    method get_mut_body_as_vec (line 49) | pub fn get_mut_body_as_vec(&mut self) -> &mut Vec<Statement> {
    method get_param_location (line 55) | pub fn get_param_location(&self) -> FileLocation {
    method get_num_of_params (line 58) | pub fn get_num_of_params(&self) -> usize {
    method get_name_of_params (line 61) | pub fn get_name_of_params(&self) -> &Vec<String> {
    method get_name (line 64) | pub fn get_name(&self) -> &str {

FILE: program_structure/src/program_library/program_archive.rs
  type Contents (line 10) | type Contents = HashMap<FileID, Vec<Definition>>;
  type ProgramArchive (line 13) | pub struct ProgramArchive {
    method new (line 26) | pub fn new(
    method get_file_id_main (line 69) | pub fn get_file_id_main(&self) -> &FileID {
    method contains_template (line 73) | pub fn contains_template(&self, template_name: &str) -> bool {
    method get_template_data (line 76) | pub fn get_template_data(&self, template_name: &str) -> &TemplateData {
    method get_mut_template_data (line 80) | pub fn get_mut_template_data(&mut self, template_name: &str) -> &mut T...
    method get_template_names (line 84) | pub fn get_template_names(&self) -> &HashSet<String> {
    method get_templates (line 87) | pub fn get_templates(&self) -> &TemplateInfo {
    method get_mut_templates (line 90) | pub fn get_mut_templates(&mut self) -> &mut TemplateInfo {
    method remove_template (line 94) | pub fn remove_template(&mut self, id: &str) {
    method contains_function (line 100) | pub fn contains_function(&self, function_name: &str) -> bool {
    method get_function_data (line 103) | pub fn get_function_data(&self, function_name: &str) -> &FunctionData {
    method get_mut_function_data (line 107) | pub fn get_mut_function_data(&mut self, function_name: &str) -> &mut F...
    method get_function_names (line 111) | pub fn get_function_names(&self) -> &HashSet<String> {
    method get_functions (line 114) | pub fn get_functions(&self) -> &FunctionInfo {
    method get_mut_functions (line 117) | pub fn get_mut_functions(&mut self) -> &mut FunctionInfo {
    method remove_function (line 120) | pub fn remove_function(&mut self, id: &str) {
    method get_public_inputs_main_component (line 126) | pub fn get_public_inputs_main_component(&self) -> &Vec<String> {
    method main_expression (line 129) | pub fn main_expression(&self) -> &Expression {
    method get_file_library (line 133) | pub fn get_file_library(&self) -> &FileLibrary {

FILE: program_structure/src/program_library/program_merger.rs
  type Merger (line 9) | pub struct Merger {
    method new (line 16) | pub fn new() -> Merger {
    method add_definitions (line 20) | pub fn add_definitions(
    method contains_function (line 92) | pub fn contains_function(&self, function_name: &str) -> bool {
    method get_function_info (line 95) | fn get_function_info(&self) -> &FunctionInfo {
    method get_mut_function_info (line 98) | fn get_mut_function_info(&mut self) -> &mut FunctionInfo {
    method contains_template (line 102) | pub fn contains_template(&self, template_name: &str) -> bool {
    method get_template_info (line 105) | fn get_template_info(&self) -> &TemplateInfo {
    method get_mut_template_info (line 108) | fn get_mut_template_info(&mut self) -> &mut TemplateInfo {
    method decompose (line 112) | pub fn decompose(self) -> (usize, FunctionInfo, TemplateInfo) {

FILE: program_structure/src/program_library/report.rs
  type ReportCollection (line 11) | pub type ReportCollection = Vec<Report>;
  type DiagnosticCode (line 12) | pub type DiagnosticCode = String;
  type ReportLabel (line 13) | pub type ReportLabel = Label<FileID>;
  type ReportNote (line 14) | type ReportNote = String;
  type MessageCategory (line 17) | pub enum MessageCategory {
    method to_level (line 74) | pub fn to_level(&self) -> String {
  method partial_cmp (line 25) | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
  method cmp (line 31) | fn cmp(&self, other: &Self) -> Ordering {
  method fmt (line 49) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  type Err (line 60) | type Err = anyhow::Error;
  method from_str (line 62) | fn from_str(category: &str) -> Result<MessageCategory, Self::Err> {
  type Report (line 86) | pub struct Report {
    method new (line 97) | fn new(category: MessageCategory, message: String, code: ReportCode) -...
    method error (line 109) | pub fn error(message: String, code: ReportCode) -> Report {
    method warning (line 113) | pub fn warning(message: String, code: ReportCode) -> Report {
    method info (line 117) | pub fn info(message: String, code: ReportCode) -> Report {
    method add_primary (line 121) | pub fn add_primary(
    method add_secondary (line 133) | pub fn add_secondary(
    method add_note (line 147) | pub fn add_note(&mut self, note: String) -> &mut Self {
    method to_diagnostic (line 152) | pub fn to_diagnostic(&self, verbose: bool) -> Diagnostic<FileID> {
    method primary_file_ids (line 179) | pub fn primary_file_ids(&self) -> &Vec<FileID> {
    method primary_file_ids_mut (line 183) | fn primary_file_ids_mut(&mut self) -> &mut Vec<FileID> {
    method category (line 187) | pub fn category(&self) -> &MessageCategory {
    method message (line 191) | pub fn message(&self) -> &String {
    method primary (line 195) | pub fn primary(&self) -> &Vec<ReportLabel> {
    method primary_mut (line 199) | fn primary_mut(&mut self) -> &mut Vec<ReportLabel> {
    method secondary (line 203) | pub fn secondary(&self) -> &Vec<ReportLabel> {
    method secondary_mut (line 207) | fn secondary_mut(&mut self) -> &mut Vec<ReportLabel> {
    method notes (line 211) | pub fn notes(&self) -> &Vec<ReportNote> {
    method notes_mut (line 215) | fn notes_mut(&mut self) -> &mut Vec<ReportNote> {
    method code (line 219) | pub fn code(&self) -> &ReportCode {
    method id (line 223) | pub fn id(&self) -> String {
    method name (line 227) | pub fn name(&self) -> String {

FILE: program_structure/src/program_library/report_code.rs
  constant DOC_URL (line 1) | const DOC_URL: &str = "https://github.com/trailofbits/circomspect/blob/m...
  type ReportCode (line 4) | pub enum ReportCode {
    method id (line 94) | pub fn id(&self) -> String {
    method name (line 187) | pub fn name(&self) -> String {
    method url (line 278) | pub fn url(&self) -> Option<String> {

FILE: program_structure/src/program_library/template_data.rs
  type TagInfo (line 7) | pub type TagInfo = HashSet<String>;
  type TemplateInfo (line 8) | pub type TemplateInfo = HashMap<String, TemplateData>;
  type SignalInfo (line 9) | type SignalInfo = BTreeMap<String, (usize, TagInfo)>;
  type SignalDeclarationOrder (line 10) | type SignalDeclarationOrder = Vec<(String, usize)>;
  type TemplateData (line 13) | pub struct TemplateData {
    method new (line 31) | pub fn new(
    method get_file_id (line 70) | pub fn get_file_id(&self) -> FileID {
    method get_body (line 74) | pub fn get_body(&self) -> &Statement {
    method get_body_as_vec (line 78) | pub fn get_body_as_vec(&self) -> &Vec<Statement> {
    method get_mut_body (line 85) | pub fn get_mut_body(&mut self) -> &mut Statement {
    method get_mut_body_as_vec (line 89) | pub fn get_mut_body_as_vec(&mut self) -> &mut Vec<Statement> {
    method get_num_of_params (line 96) | pub fn get_num_of_params(&self) -> usize {
    method get_param_location (line 100) | pub fn get_param_location(&self) -> FileLocation {
    method get_name_of_params (line 104) | pub fn get_name_of_params(&self) -> &Vec<String> {
    method get_input_info (line 108) | pub fn get_input_info(&self, name: &str) -> Option<&(usize, TagInfo)> {
    method get_output_info (line 112) | pub fn get_output_info(&self, name: &str) -> Option<&(usize, TagInfo)> {
    method get_inputs (line 115) | pub fn get_inputs(&self) -> &SignalInfo {
    method get_outputs (line 118) | pub fn get_outputs(&self) -> &SignalInfo {
    method get_declaration_inputs (line 121) | pub fn get_declaration_inputs(&self) -> &SignalDeclarationOrder {
    method get_declaration_outputs (line 124) | pub fn get_declaration_outputs(&self) -> &SignalDeclarationOrder {
    method get_name (line 127) | pub fn get_name(&self) -> &str {
    method is_parallel (line 130) | pub fn is_parallel(&self) -> bool {
    method is_custom_gate (line 133) | pub fn is_custom_gate(&self) -> bool {
  function fill_inputs_and_outputs (line 138) | fn fill_inputs_and_outputs(

FILE: program_structure/src/program_library/template_library.rs
  type Contents (line 8) | type Contents = HashMap<FileID, Vec<Definition>>;
  type TemplateLibrary (line 10) | pub struct TemplateLibrary {
    method new (line 17) | pub fn new(library_contents: Contents, file_library: FileLibrary) -> T...
    method contains_template (line 69) | pub fn contains_template(&self, template_name: &str) -> bool {
    method get_template (line 73) | pub fn get_template(&self, template_name: &str) -> &TemplateData {
    method get_template_mut (line 78) | pub fn get_template_mut(&mut self, template_name: &str) -> &mut Templa...
    method get_templates (line 83) | pub fn get_templates(&self) -> &TemplateInfo {
    method get_templates_mut (line 87) | pub fn get_templates_mut(&mut self) -> &mut TemplateInfo {
    method contains_function (line 92) | pub fn contains_function(&self, function_name: &str) -> bool {
    method get_function (line 96) | pub fn get_function(&self, function_name: &str) -> &FunctionData {
    method get_function_mut (line 101) | pub fn get_function_mut(&mut self, function_name: &str) -> &mut Functi...
    method get_functions (line 106) | pub fn get_functions(&self) -> &FunctionInfo {
    method get_functions_mut (line 110) | pub fn get_functions_mut(&mut self) -> &mut FunctionInfo {
    method get_file_library (line 114) | pub fn get_file_library(&self) -> &FileLibrary {

FILE: program_structure/src/static_single_assignment/dominator_tree.rs
  type Index (line 7) | type Index = usize;
  type DominatorInfo (line 8) | type DominatorInfo = Vec<HashSet<Index>>;
  type ImmediateDominatorInfo (line 9) | type ImmediateDominatorInfo = Vec<Option<Index>>;
  type DominatorTree (line 12) | pub struct DominatorTree<T: DirectedGraphNode> {
  function new (line 21) | pub fn new(basic_blocks: &[T]) -> DominatorTree<T> {
  function entry_block (line 37) | pub fn entry_block(&self) -> Index {
  function get_dominators (line 41) | pub fn get_dominators(&self, i: Index) -> HashSet<Index> {
  function get_immediate_dominator (line 45) | pub fn get_immediate_dominator(&self, i: Index) -> Option<Index> {
  function get_dominator_successors (line 49) | pub fn get_dominator_successors(&self, i: Index) -> HashSet<Index> {
  function get_dominance_frontier (line 53) | pub fn get_dominance_frontier(&self, i: Index) -> HashSet<Index> {
  function compute_dominators (line 59) | fn compute_dominators<T: DirectedGraphNode>(basic_blocks: &[T]) -> Domin...
  function compute_immediate_dominators (line 87) | fn compute_immediate_dominators<T: DirectedGraphNode>(
  function compute_dominance_frontier (line 138) | fn compute_dominance_frontier<T: DirectedGraphNode>(

FILE: program_structure/src/static_single_assignment/errors.rs
  type SSAError (line 7) | pub enum SSAError {
    method into_report (line 15) | pub fn into_report(&self) -> Report {
  type SSAResult (line 12) | pub type SSAResult<T> = Result<T, SSAError>;
  method from (line 37) | fn from(error: SSAError) -> Report {

FILE: program_structure/src/static_single_assignment/mod.rs
  function insert_phi_statements (line 15) | pub fn insert_phi_statements<Cfg: SSAConfig>(
  function insert_ssa_variables (line 55) | pub fn insert_ssa_variables<Cfg: SSAConfig>(
  function insert_ssa_variables_impl (line 64) | fn insert_ssa_variables_impl<Cfg: SSAConfig>(

FILE: program_structure/src/static_single_assignment/traits.rs
  type SSAConfig (line 7) | pub trait SSAConfig: Sized {
  type SSAEnvironment (line 25) | pub trait SSAEnvironment {
    method add_variable_scope (line 27) | fn add_variable_scope(&mut self);
    method remove_variable_scope (line 30) | fn remove_variable_scope(&mut self);
  type SSABasicBlock (line 34) | pub trait SSABasicBlock<Cfg: SSAConfig>: DirectedGraphNode {
    method prepend_statement (line 36) | fn prepend_statement(&mut self, stmt: Cfg::Statement);
    method statements (line 43) | fn statements<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Cfg::Statem...
    method statements_mut (line 51) | fn statements_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut...
    method variables_written (line 54) | fn variables_written(&self) -> HashSet<Cfg::Variable> {
    method has_phi_statement (line 63) | fn has_phi_statement(&self, var: &Cfg::Variable) -> bool {
    method insert_phi_statement (line 69) | fn insert_phi_statement(&mut self, var: &Cfg::Variable, env: &Cfg::Env...
    method update_phi_statements (line 75) | fn update_phi_statements(&mut self, env: &Cfg::Environment) {
    method insert_ssa_variables (line 90) | fn insert_ssa_variables(&mut self, env: &mut Cfg::Environment) -> SSAR...
  type SSAStatement (line 100) | pub trait SSAStatement<Cfg: SSAConfig>: Clone {
    method variables_written (line 102) | fn variables_written(&self) -> HashSet<Cfg::Variable>;
    method new_phi_statement (line 105) | fn new_phi_statement(name: &Cfg::Variable, env: &Cfg::Environment) -> ...
    method is_phi_statement (line 108) | fn is_phi_statement(&self) -> bool;
    method is_phi_statement_for (line 111) | fn is_phi_statement_for(&self, var: &Cfg::Variable) -> bool;
    method ensure_phi_argument (line 117) | fn ensure_phi_argument(&mut self, env: &Cfg::Environment);
    method insert_ssa_variables (line 121) | fn insert_ssa_variables(&mut self, env: &mut Cfg::Environment) -> SSAR...
  type Index (line 124) | pub type Index = usize;
  type IndexSet (line 125) | pub type IndexSet = HashSet<Index>;
  type DirectedGraphNode (line 129) | pub trait DirectedGraphNode {
    method index (line 130) | fn index(&self) -> Index;
    method predecessors (line 132) | fn predecessors(&self) -> &IndexSet;
    method successors (line 134) | fn successors(&self) -> &IndexSet;

FILE: program_structure/src/utils/constants.rs
  type Curve (line 7) | pub enum Curve {
    method fmt (line 15) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    method fmt (line 26) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    method prime (line 32) | fn prime(&self) -> BigInt {
  type Err (line 48) | type Err = Error;
  method from_str (line 50) | fn from_str(curve: &str) -> Result<Self, Self::Err> {
  type UsefulConstants (line 61) | pub struct UsefulConstants {
    method new (line 67) | pub fn new(curve: &Curve) -> UsefulConstants {
    method curve (line 72) | pub fn curve(&self) -> &Curve {
    method prime (line 77) | pub fn prime(&self) -> &BigInt {
    method prime_size (line 82) | pub fn prime_size(&self) -> usize {

FILE: program_structure/src/utils/environment.rs
  type VarInfo (line 5) | pub trait VarInfo {}
  type SignalInfo (line 6) | pub trait SignalInfo {}
  type ComponentInfo (line 7) | pub trait ComponentInfo {}
  type OnlyVars (line 10) | pub struct OnlyVars;
  type OnlySignals (line 13) | pub struct OnlySignals;
  type OnlyComponents (line 16) | pub struct OnlyComponents;
  type FullEnvironment (line 19) | pub struct FullEnvironment;
  type VarEnvironment (line 24) | pub type VarEnvironment<VC> = RawEnvironment<OnlyVars, (), (), VC>;
  type SignalEnvironment (line 25) | pub type SignalEnvironment<SC> = RawEnvironment<OnlySignals, (), SC, ()>;
  type ComponentEnvironment (line 26) | pub type ComponentEnvironment<CC> = RawEnvironment<OnlyComponents, CC, (...
  type CircomEnvironment (line 27) | pub type CircomEnvironment<CC, SC, VC> = RawEnvironment<FullEnvironment,...
  type CircomEnvironmentError (line 29) | pub enum CircomEnvironmentError {
  type RawEnvironment (line 34) | pub struct RawEnvironment<T, CC, SC, VC> {
  method default (line 43) | fn default() -> Self {
  function has_symbol (line 59) | pub fn has_symbol(&self, symbol: &str) -> bool {
  function merge (line 64) | pub fn merge(
  function block_with_variable_symbol (line 101) | fn block_with_variable_symbol(&self, symbol: &str) -> Option<&VariableBl...
  function mut_block_with_variable_symbol (line 112) | fn mut_block_with_variable_symbol(&mut self, symbol: &str) -> Option<&mu...
  function new (line 123) | pub fn new() -> RawEnvironment<T, CC, SC, VC> {
  function add_variable_block (line 126) | pub fn add_variable_block(&mut self) {
  function remove_variable_block (line 129) | pub fn remove_variable_block(&mut self) {
  function add_variable (line 133) | pub fn add_variable(&mut self, variable_name: &str, content: VC) {
  function has_variable (line 138) | pub fn has_variable(&self, symbol: &str) -> bool {
  function get_variable (line 142) | pub fn get_variable(&self, symbol: &str) -> Option<&VC> {
  function get_mut_variable (line 150) | pub fn get_mut_variable(&mut self, symbol: &str) -> Option<&mut VC> {
  function get_variable_res (line 158) | pub fn get_variable_res(&self, symbol: &str) -> Result<&VC, CircomEnviro...
  function remove_variable (line 166) | pub fn remove_variable(&mut self, symbol: &str) {
  function get_variable_or_break (line 172) | pub fn get_variable_or_break(&self, symbol: &str, file: &str, line: u32)...
  function get_mut_variable_mut (line 180) | pub fn get_mut_variable_mut(
  function get_mut_variable_or_break (line 191) | pub fn get_mut_variable_or_break(&mut self, symbol: &str, file: &str, li...
  function variable_iter (line 199) | pub fn variable_iter(&self) -> impl Iterator<Item = (&String, &VC)> {
  function add_component (line 208) | pub fn add_component(&mut self, component_name: &str, content: CC) {
  function remove_component (line 211) | pub fn remove_component(&mut self, component_name: &str) {
  function has_component (line 214) | pub fn has_component(&self, symbol: &str) -> bool {
  function get_component (line 217) | pub fn get_component(&self, symbol: &str) -> Option<&CC> {
  function get_mut_component (line 220) | pub fn get_mut_component(&mut self, symbol: &str) -> Option<&mut CC> {
  function get_component_res (line 223) | pub fn get_component_res(&self, symbol: &str) -> Result<&CC, CircomEnvir...
  function get_component_or_break (line 226) | pub fn get_component_or_break(&self, symbol: &str, file: &str, line: u32...
  function get_mut_component_res (line 230) | pub fn get_mut_component_res(
  function get_mut_component_or_break (line 236) | pub fn get_mut_component_or_break(&mut self, symbol: &str, file: &str, l...
  function add_input (line 246) | pub fn add_input(&mut self, input_name: &str, content: SC) {
  function remove_input (line 249) | pub fn remove_input(&mut self, input_name: &str) {
  function add_output (line 252) | pub fn add_output(&mut self, output_name: &str, content: SC) {
  function remove_output (line 255) | pub fn remove_output(&mut self, output_name: &str) {
  function add_intermediate (line 258) | pub fn add_intermediate(&mut self, intermediate_name: &str, content: SC) {
  function remove_intermediate (line 261) | pub fn remove_intermediate(&mut self, intermediate_name: &str) {
  function has_input (line 264) | pub fn has_input(&self, symbol: &str) -> bool {
  function has_output (line 267) | pub fn has_output(&self, symbol: &str) -> bool {
  function has_intermediate (line 270) | pub fn has_intermediate(&self, symbol: &str) -> bool {
  function has_signal (line 273) | pub fn has_signal(&self, symbol: &str) -> bool {
  function get_input (line 276) | pub fn get_input(&self, symbol: &str) -> Option<&SC> {
  function get_mut_input (line 279) | pub fn get_mut_input(&mut self, symbol: &str) -> Option<&mut SC> {
  function get_input_res (line 282) | pub fn get_input_res(&self, symbol: &str) -> Result<&SC, CircomEnvironme...
  function get_input_or_break (line 285) | pub fn get_input_or_break(&self, symbol: &str, file: &str, line: u32) ->...
  function get_mut_input_res (line 289) | pub fn get_mut_input_res(&mut self, symbol: &str) -> Result<&mut SC, Cir...
  function get_mut_input_or_break (line 292) | pub fn get_mut_input_or_break(&mut self, symbol: &str, file: &str, line:...
  function get_output (line 297) | pub fn get_output(&self, symbol: &str) -> Option<&SC> {
  function get_mut_output (line 300) | pub fn get_mut_output(&mut self, symbol: &str) -> Option<&mut SC> {
  function get_output_res (line 303) | pub fn get_output_res(&self, symbol: &str) -> Result<&SC, CircomEnvironm...
  function get_output_or_break (line 306) | pub fn get_output_or_break(&self, symbol: &str, file: &str, line: u32) -...
  function get_mut_output_res (line 310) | pub fn get_mut_output_res(&mut self, symbol: &str) -> Result<&mut SC, Ci...
  function get_mut_output_or_break (line 313) | pub fn get_mut_output_or_break(&mut self, symbol: &str, file: &str, line...
  function get_intermediate (line 318) | pub fn get_intermediate(&self, symbol: &str) -> Option<&SC> {
  function get_mut_intermediate (line 321) | pub fn get_mut_intermediate(&mut self, symbol: &str) -> Option<&mut SC> {
  function get_intermediate_res (line 324) | pub fn get_intermediate_res(&self, symbol: &str) -> Result<&SC, CircomEn...
  function get_intermediate_or_break (line 327) | pub fn get_intermediate_or_break(&self, symbol: &str, file: &str, line: ...
  function get_mut_intermediate_res (line 331) | pub fn get_mut_intermediate_res(
  function get_mut_intermediate_or_break (line 337) | pub fn get_mut_intermediate_or_break(
  function get_signal (line 347) | pub fn get_signal(&self, symbol: &str) -> Option<&SC> {
  function get_mut_signal (line 358) | pub fn get_mut_signal(&mut self, symbol: &str) -> Option<&mut SC> {
  function get_signal_res (line 369) | pub fn get_signal_res(&self, symbol: &str) -> Result<&SC, CircomEnvironm...
  function get_signal_or_break (line 380) | pub fn get_signal_or_break(&self, symbol: &str, file: &str, line: u32) -...
  function get_mut_signal_res (line 388) | pub fn get_mut_signal_res(&mut self, symbol: &str) -> Result<&mut SC, Ci...
  function get_mut_signal_or_break (line 399) | pub fn get_mut_signal_or_break(&mut self, symbol: &str, file: &str, line...
  type VariableBlock (line 410) | struct VariableBlock<VC> {
  method default (line 414) | fn default() -> Self {
  function new (line 419) | pub fn new() -> VariableBlock<VC> {
  function add_variable (line 422) | pub fn add_variable(&mut self, symbol: &str, content: VC) {
  function remove_variable (line 425) | pub fn remove_variable(&mut self, symbol: &str) {
  function contains_variable (line 428) | pub fn contains_variable(&self, symbol: &str) -> bool {
  function get_variable (line 431) | pub fn get_variable(&self, symbol: &str) -> &VC {
  function get_mut_variable (line 435) | pub fn get_mut_variable(&mut self, symbol: &str) -> &mut VC {
  function iter (line 439) | pub fn iter(&self) -> impl Iterator<Item = (&String, &VC)> {
  function merge (line 442) | pub fn merge(
  function hashmap_union (line 454) | fn hashmap_union<K, V>(

FILE: program_structure/src/utils/nonempty_vec.rs
  type NonEmptyVec (line 32) | pub struct NonEmptyVec<T> {
  function new (line 38) | pub fn new(x: T) -> NonEmptyVec<T> {
  function first (line 42) | pub fn first(&self) -> &T {
  function first_mut (line 46) | pub fn first_mut(&mut self) -> &mut T {
  function last (line 51) | pub fn last(&self) -> &T {
  function last_mut (line 59) | pub fn last_mut(&mut self) -> &mut T {
  function push (line 67) | pub fn push(&mut self, x: T) {
  function pop (line 84) | pub fn pop(&mut self) -> Option<T> {
  function len (line 98) | pub fn len(&self) -> usize {
  function is_empty (line 103) | pub fn is_empty(&self) -> bool {
  function iter (line 108) | pub fn iter(&self) -> NonEmptyIter<'_, T> {
  type Item (line 115) | type Item = &'a T;
  type IntoIter (line 116) | type IntoIter = NonEmptyIter<'a, T>;
  method into_iter (line 118) | fn into_iter(self) -> Self::IntoIter {
  type NonEmptyIter (line 136) | pub struct NonEmptyIter<'a, T> {
  function new (line 142) | fn new(vec: &'a NonEmptyVec<T>) -> NonEmptyIter<'a, T> {
  type Item (line 148) | type Item = &'a T;
  method next (line 150) | fn next(&mut self) -> Option<Self::Item> {
  type Output (line 163) | type Output = T;
  function index (line 165) | fn index(&self, index: usize) -> &Self::Output {
  type Output (line 174) | type Output = T;
  function index (line 176) | fn index(&self, index: &usize) -> &Self::Output {
  function index_mut (line 185) | fn index_mut(&mut self, index: usize) -> &mut Self::Output {
  function index_mut (line 194) | fn index_mut(&mut self, index: &usize) -> &mut Self::Output {
  function from (line 203) | fn from(xs: NonEmptyVec<T>) -> Vec<T> {
  function from (line 212) | fn from(xs: &NonEmptyVec<T>) -> Vec<T> {
  type Error (line 218) | type Error = Error;
  function try_from (line 220) | fn try_from(mut xs: Vec<T>) -> Result<NonEmptyVec<T>, Error> {
  type Error (line 230) | type Error = Error;
  function try_from (line 232) | fn try_from(xs: &Vec<T>) -> Result<NonEmptyVec<T>, Error> {
  type Error (line 242) | type Error = Error;
  function try_from (line 244) | fn try_from(xs: &[T]) -> Result<NonEmptyVec<T>, Error> {
  type Error (line 254) | type Error = Error;
  function try_from (line 256) | fn try_from(xs: &[T; N]) -> Result<NonEmptyVec<T>, Error> {

FILE: program_structure/src/utils/sarif_conversion.rs
  constant SARIF_VERSION (line 14) | const SARIF_VERSION: &str = "2.1.0";
  constant DRIVER_NAME (line 15) | const DRIVER_NAME: &str = "Circomspect";
  constant ORGANIZATION (line 16) | const ORGANIZATION: &str = "Trail of Bits";
  constant DOWNLOAD_URI (line 17) | const DOWNLOAD_URI: &str = "https://github.com/trailofbits/circomspect";
  type ToSarif (line 20) | pub trait ToSarif {
    method to_sarif (line 25) | fn to_sarif(&self, files: &FileLibrary) -> Result<Self::Sarif, Self::E...
    type Sarif (line 29) | type Sarif = sarif::Sarif;
    type Error (line 30) | type Error = SarifError;
    method to_sarif (line 32) | fn to_sarif(&self, files: &FileLibrary) -> Result<Self::Sarif, Self::E...
    type Sarif (line 70) | type Sarif = sarif::Result;
    type Error (line 71) | type Error = SarifError;
    method to_sarif (line 73) | fn to_sarif(&self, files: &FileLibrary) -> SarifResult<sarif::Result> {
    type Sarif (line 111) | type Sarif = sarif::Location;
    type Error (line 112) | type Error = SarifError;
    method to_sarif (line 114) | fn to_sarif(&self, files: &FileLibrary) -> SarifResult<sarif::Location> {
  type ToUri (line 156) | trait ToUri {
    method to_uri (line 158) | fn to_uri(&self, files: &FileLibrary) -> Result<String, Self::Error>;
    type Error (line 162) | type Error = SarifError;
    method to_uri (line 164) | fn to_uri(&self, files: &FileLibrary) -> Result<String, SarifError> {
  type SarifError (line 178) | pub enum SarifError {
    method fmt (line 199) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  type SarifResult (line 196) | type SarifResult<T> = Result<T, SarifError>;

FILE: program_structure/src/utils/writers.rs
  type ReportFilter (line 17) | pub trait ReportFilter {
    method filter (line 19) | fn filter(&self, report: &Report) -> bool;
    method filter (line 23) | fn filter(&self, report: &Report) -> bool {
  type ReportWriter (line 28) | pub trait ReportWriter {
    method write_reports (line 30) | fn write_reports(&mut self, reports: &[Report], file_library: &FileLib...
    method write_report (line 33) | fn write_report(&mut self, report: Report, file_library: &FileLibrary)...
    method reports_written (line 39) | fn reports_written(&self) -> usize;
    method write_reports (line 100) | fn write_reports(&mut self, reports: &[Report], file_library: &FileLib...
    method reports_written (line 122) | fn reports_written(&self) -> usize {
    method write_reports (line 155) | fn write_reports(&mut self, reports: &[Report], file_library: &FileLib...
    method reports_written (line 160) | fn reports_written(&self) -> usize {
    method write_reports (line 206) | fn write_reports(&mut self, reports: &[Report], file_library: &FileLib...
    method reports_written (line 221) | fn reports_written(&self) -> usize {
  type LogWriter (line 42) | pub trait LogWriter {
    method write_messages (line 43) | fn write_messages<D: Display>(&mut self, messages: &[D]);
    method write_message (line 45) | fn write_message<D: Display>(&mut self, message: D) {
    method write_messages (line 82) | fn write_messages<D: Display>(&mut self, messages: &[D]) {
    method write_messages (line 149) | fn write_messages<D: Display>(&mut self, messages: &[D]) {
  type StdoutWriter (line 50) | pub struct StdoutWriter {
    method new (line 58) | pub fn new(verbose: bool) -> StdoutWriter {
    method add_filter (line 67) | pub fn add_filter(mut self, filter: impl ReportFilter + 'static) -> St...
    method filter (line 72) | fn filter(&self, reports: &[Report]) -> ReportCollection {
  type CachedStdoutWriter (line 128) | pub struct CachedStdoutWriter {
    method new (line 134) | pub fn new(verbose: bool) -> CachedStdoutWriter {
    method reports (line 138) | pub fn reports(&self) -> &ReportCollection {
    method add_filter (line 142) | pub fn add_filter(mut self, filter: impl ReportFilter + 'static) -> Ca...
  type SarifWriter (line 166) | pub struct SarifWriter {
    method new (line 173) | pub fn new(sarif_file: &Path) -> SarifWriter {
    method add_filter (line 177) | pub fn add_filter(mut self, filter: impl ReportFilter + 'static) -> Sa...
    method filter (line 182) | fn filter(&self, reports: &[Report]) -> ReportCollection {
    method serialize_reports (line 190) | fn serialize_reports(

FILE: program_structure_tests/src/control_flow_graph.rs
  function test_cfg_from_if (line 10) | fn test_cfg_from_if() {
  function test_cfg_from_if_then_else (line 31) | fn test_cfg_from_if_then_else() {
  function test_cfg_from_while (line 55) | fn test_cfg_from_while() {
  function test_cfg_from_nested_if (line 89) | fn test_cfg_from_nested_if() {
  function test_cfg_from_nested_while (line 128) | fn test_cfg_from_nested_while() {
  function test_cfg_with_non_unique_variables (line 171) | fn test_cfg_with_non_unique_variables() {
  function test_dominance_from_nested_if (line 229) | fn test_dominance_from_nested_if() {
  function test_dominance_from_nested_if_then_else (line 273) | fn test_dominance_from_nested_if_then_else() {
  function test_branches_from_nested_if_then_else (line 322) | fn test_branches_from_nested_if_then_else() {
  function test_branches_from_nested_if (line 366) | fn test_branches_from_nested_if() {
  function validate_cfg (line 405) | fn validate_cfg(
  function validate_dominance (line 468) | fn validate_dominance(
  function validate_branches (line 498) | fn validate_branches(
  function lift (line 527) | fn lift(name: &str) -> VariableName {

FILE: program_structure_tests/src/static_single_assignment.rs
  function test_ssa_with_array (line 12) | fn test_ssa_with_array() {
  function test_ssa_with_components_and_signals (line 29) | fn test_ssa_with_components_and_signals() {
  function test_ssa_from_if (line 46) | fn test_ssa_from_if() {
  function test_ssa_from_if_then_else (line 61) | fn test_ssa_from_if_then_else() {
  function test_ssa_from_while (line 79) | fn test_ssa_from_while() {
  function test_ssa_from_nested_if (line 93) | fn test_ssa_from_nested_if() {
  function test_ssa_from_nested_while (line 110) | fn test_ssa_from_nested_while() {
  function test_ssa_with_non_unique_variables (line 127) | fn test_ssa_with_non_unique_variables() {
  function validate_ssa (line 151) | fn validate_ssa(src: &str, variables: &[&str]) {
  function validate_reads (line 189) | fn validate_reads(current_block: &BasicBlock, cfg: &Cfg, env: &mut HashS...
Condensed preview — 99 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (740K chars).
[
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1519,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  check:\n    name: Ch"
  },
  {
    "path": ".gitignore",
    "chars": 26,
    "preview": ".vscode\n/target\n/examples\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 259,
    "preview": "repos:\n  - repo: local\n    hooks:\n      - id: rustfmt\n        name: rustfmt\n        description: Check Rust source code "
  },
  {
    "path": ".rustfmt.toml",
    "chars": 119,
    "preview": "fn_params_layout = \"Tall\"\nuse_small_heuristics = \"Max\"\nmax_width = 100\nreorder_modules = false\nreorder_imports = false\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2856,
    "preview": "# Release Notes\n\n## v0.8.1 (2023-03-21)\n\n-   Updated dependencies flagged by cargo-audit.\n\n## v0.8.0 (2023-03-21)\n\n### F"
  },
  {
    "path": "CODEOWNERS",
    "chars": 9,
    "preview": "* @fegge\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 137,
    "preview": "[workspace]\nresolver = \"1\"\n\nmembers = [\n  \"cli\",\n  \"parser\",\n  \"program_analysis\",\n  \"program_structure\",\n  \"program_str"
  },
  {
    "path": "LICENSE",
    "chars": 35130,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2021 0Kims"
  },
  {
    "path": "README.md",
    "chars": 2339,
    "preview": "# Circomspect 🔎\n\n![Crates.io badge](https://img.shields.io/crates/v/circomspect.svg) ![GitHub badge](https://github.com/"
  },
  {
    "path": "TODO.md",
    "chars": 1905,
    "preview": "# TODO\n\n  - [x] Implement a basic block type, and functionality allowing us to lift the\n        AST to a CFG.\n  - [x] Im"
  },
  {
    "path": "circom_algebra/Cargo.toml",
    "chars": 368,
    "preview": "[package]\nname = \"circomspect-circom-algebra\"\nversion = \"2.0.2\"\nedition = \"2021\"\nrust-version = \"1.65\"\nlicense = \"LGPL-3"
  },
  {
    "path": "circom_algebra/src/lib.rs",
    "chars": 105,
    "preview": "pub extern crate num_bigint_dig as num_bigint;\npub extern crate num_traits;\n\npub mod modular_arithmetic;\n"
  },
  {
    "path": "circom_algebra/src/modular_arithmetic.rs",
    "chars": 8028,
    "preview": "use num_bigint::{BigInt, ModInverse, Sign};\nuse num_traits::ToPrimitive;\n\npub enum ArithmeticError {\n    DivisionByZero,"
  },
  {
    "path": "cli/Cargo.toml",
    "chars": 859,
    "preview": "[package]\nname = \"circomspect\"\nversion = \"0.9.0\"\nedition = \"2021\"\nrust-version = \"1.65\"\nlicense = \"LGPL-3.0-only\"\nauthor"
  },
  {
    "path": "cli/src/main.rs",
    "chars": 5005,
    "preview": "use std::collections::HashSet;\nuse std::path::PathBuf;\nuse std::process::ExitCode;\nuse clap::{CommandFactory, Parser};\n\n"
  },
  {
    "path": "doc/analysis_passes.md",
    "chars": 12766,
    "preview": "# Analysis Passes\n\n### Side-effect free assignment\n\nAn assigned value which does not contribute either directly or indir"
  },
  {
    "path": "doc/demo.cast",
    "chars": 10825,
    "preview": "{\"version\": 2, \"width\": 114, \"height\": 35, \"timestamp\": 1656748733, \"env\": {\"SHELL\": \"/bin/zsh\", \"TERM\": \"xterm-256color"
  },
  {
    "path": "parser/Cargo.toml",
    "chars": 970,
    "preview": "[package]\nname = \"circomspect-parser\"\nversion = \"2.2.0\"\nedition = \"2021\"\nrust-version = \"1.65\"\nbuild = \"build.rs\"\nlicens"
  },
  {
    "path": "parser/build.rs",
    "chars": 75,
    "preview": "extern crate lalrpop;\n\nfn main() {\n    lalrpop::process_root().unwrap();\n}\n"
  },
  {
    "path": "parser/src/errors.rs",
    "chars": 4927,
    "preview": "use program_structure::ast::{Meta, Version};\nuse program_structure::report_code::ReportCode;\nuse program_structure::repo"
  },
  {
    "path": "parser/src/include_logic.rs",
    "chars": 6003,
    "preview": "use crate::errors::FileOsError;\n\nuse log::debug;\n\nuse super::errors::IncludeError;\nuse program_structure::ast::Include;\n"
  },
  {
    "path": "parser/src/lang.lalrpop",
    "chars": 22508,
    "preview": "use num_bigint::BigInt;\nuse program_structure::statement_builders::*;\nuse program_structure::expression_builders::*;\nuse"
  },
  {
    "path": "parser/src/lib.rs",
    "chars": 8537,
    "preview": "extern crate num_bigint_dig as num_bigint;\nextern crate num_traits;\nextern crate serde;\nextern crate serde_derive;\n\n#[ma"
  },
  {
    "path": "parser/src/parser_logic.rs",
    "chars": 5687,
    "preview": "use super::errors::{ParsingError, UnclosedCommentError};\nuse super::lang;\n\nuse program_structure::ast::AST;\nuse program_"
  },
  {
    "path": "parser/src/syntax_sugar_remover.rs",
    "chars": 41719,
    "preview": "use program_structure::ast::*;\nuse program_structure::statement_builders::{build_block, build_substitution};\nuse program"
  },
  {
    "path": "parser/src/syntax_sugar_traits.rs",
    "chars": 7949,
    "preview": "use program_structure::ast::*;\nuse program_structure::report::ReportCollection;\n\nuse crate::errors::TupleError;\n\npub(cra"
  },
  {
    "path": "program_analysis/Cargo.toml",
    "chars": 804,
    "preview": "[package]\nname = \"circomspect-program-analysis\"\nversion = \"0.8.2\"\nedition = \"2021\"\nrust-version = \"1.65\"\nlicense = \"LGPL"
  },
  {
    "path": "program_analysis/src/analysis_context.rs",
    "chars": 2309,
    "preview": "use thiserror::Error;\n\nuse program_structure::{\n    file_definition::{FileID, FileLocation},\n    cfg::Cfg,\n};\n\n/// Error"
  },
  {
    "path": "program_analysis/src/analysis_runner.rs",
    "chars": 16659,
    "preview": "use log::{debug, trace};\nuse std::path::PathBuf;\nuse std::collections::HashMap;\n\nuse parser::ParseResult;\n\nuse program_s"
  },
  {
    "path": "program_analysis/src/bitwise_complement.rs",
    "chars": 5124,
    "preview": "use log::debug;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::report_code::ReportCode;\nuse program_structure:"
  },
  {
    "path": "program_analysis/src/bn254_specific_circuit.rs",
    "chars": 7123,
    "preview": "use std::collections::HashSet;\n\nuse log::debug;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::constants::Curv"
  },
  {
    "path": "program_analysis/src/config.rs",
    "chars": 171,
    "preview": "use program_structure::ast::Version;\n\npub const COMPILER_VERSION: Version = (2, 1, 4);\npub const DEFAULT_LEVEL: &str = \""
  },
  {
    "path": "program_analysis/src/constant_conditional.rs",
    "chars": 3491,
    "preview": "use log::debug;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::report_code::ReportCode;\nuse program_structure:"
  },
  {
    "path": "program_analysis/src/constraint_analysis.rs",
    "chars": 7377,
    "preview": "use log::{debug, trace};\nuse std::collections::{HashMap, HashSet};\n\nuse program_structure::cfg::Cfg;\nuse program_structu"
  },
  {
    "path": "program_analysis/src/definition_complexity.rs",
    "chars": 3925,
    "preview": "use program_structure::cfg::{Cfg, DefinitionType};\nuse program_structure::report_code::ReportCode;\nuse program_structure"
  },
  {
    "path": "program_analysis/src/field_arithmetic.rs",
    "chars": 5469,
    "preview": "use log::debug;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::report_code::ReportCode;\nuse program_structure:"
  },
  {
    "path": "program_analysis/src/field_comparisons.rs",
    "chars": 5662,
    "preview": "use log::debug;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::report_code::ReportCode;\nuse program_structure:"
  },
  {
    "path": "program_analysis/src/lib.rs",
    "chars": 2137,
    "preview": "use analysis_context::AnalysisContext;\n\nuse program_structure::cfg::Cfg;\nuse program_structure::report::ReportCollection"
  },
  {
    "path": "program_analysis/src/nonstrict_binary_conversion.rs",
    "chars": 7303,
    "preview": "use log::debug;\nuse num_bigint::BigInt;\n\nuse program_structure::cfg::{Cfg, DefinitionType};\nuse program_structure::const"
  },
  {
    "path": "program_analysis/src/side_effect_analysis.rs",
    "chars": 19409,
    "preview": "use log::debug;\nuse std::fmt::Write;\nuse std::collections::{HashMap, HashSet};\n\nuse program_structure::cfg::{Cfg, Defini"
  },
  {
    "path": "program_analysis/src/signal_assignments.rs",
    "chars": 11639,
    "preview": "use log::{debug, trace};\nuse program_structure::intermediate_representation::degree_meta::{DegreeRange, DegreeMeta};\nuse"
  },
  {
    "path": "program_analysis/src/taint_analysis.rs",
    "chars": 9938,
    "preview": "use log::{debug, trace};\nuse program_structure::cfg::parameters::Parameters;\nuse program_structure::intermediate_represe"
  },
  {
    "path": "program_analysis/src/unconstrained_division.rs",
    "chars": 12548,
    "preview": "use std::collections::HashMap;\nuse std::fmt;\n\nuse log::{debug, trace};\n\nuse num_traits::Zero;\nuse program_structure::cfg"
  },
  {
    "path": "program_analysis/src/unconstrained_less_than.rs",
    "chars": 12965,
    "preview": "use std::collections::HashMap;\nuse std::fmt;\n\nuse log::{debug, trace};\n\nuse num_bigint::BigInt;\nuse program_structure::c"
  },
  {
    "path": "program_analysis/src/under_constrained_signals.rs",
    "chars": 8346,
    "preview": "use std::collections::{HashMap, HashSet};\n\nuse log::debug;\nuse program_structure::cfg::Cfg;\nuse program_structure::file_"
  },
  {
    "path": "program_analysis/src/unused_output_signal.rs",
    "chars": 20000,
    "preview": "use log::debug;\nuse std::collections::HashSet;\n\nuse program_structure::{\n    ir::*,\n    ir::value_meta::ValueMeta,\n    r"
  },
  {
    "path": "program_structure/Cargo.toml",
    "chars": 914,
    "preview": "[package]\nname = \"circomspect-program-structure\"\nversion = \"2.1.4\"\nedition = \"2021\"\nrust-version = \"1.65\"\nlicense = \"LGP"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/assign_op_impl.rs",
    "chars": 184,
    "preview": "use super::ast::AssignOp;\n\nimpl AssignOp {\n    pub fn is_signal_operator(self) -> bool {\n        use AssignOp::*;\n      "
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/ast.rs",
    "chars": 10858,
    "preview": "use crate::file_definition::FileLocation;\nuse num_bigint::BigInt;\nuse serde_derive::{Deserialize, Serialize};\n\npub trait"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/ast_impl.rs",
    "chars": 519,
    "preview": "use super::ast::*;\n\nimpl AST {\n    pub fn get_includes(&self) -> &Vec<Include> {\n        &self.includes\n    }\n\n    pub f"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/ast_shortcuts.rs",
    "chars": 4932,
    "preview": "use super::ast::*;\nuse super::expression_builders::*;\nuse super::statement_builders::*;\nuse crate::ast::{Access, Express"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/expression_builders.rs",
    "chars": 1944,
    "preview": "use super::ast::*;\nuse num_bigint::BigInt;\nuse Expression::*;\n\npub fn build_infix(\n    meta: Meta,\n    lhe: Expression,\n"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/expression_impl.rs",
    "chars": 10320,
    "preview": "use std::fmt::{Debug, Display, Error, Formatter};\n\nuse super::ast::*;\nuse super::expression_builders::build_anonymous_co"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/mod.rs",
    "chars": 168,
    "preview": "mod assign_op_impl;\npub mod ast;\nmod ast_impl;\npub mod ast_shortcuts;\npub mod expression_builders;\nmod expression_impl;\n"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/statement_builders.rs",
    "chars": 2648,
    "preview": "use super::ast::*;\nuse Statement::*;\n\npub fn build_conditional_block(\n    meta: Meta,\n    cond: Expression,\n    if_case:"
  },
  {
    "path": "program_structure/src/abstract_syntax_tree/statement_impl.rs",
    "chars": 10736,
    "preview": "use std::fmt::{Debug, Display, Error, Formatter};\n\nuse super::ast::*;\n\nimpl Statement {\n    pub fn get_meta(&self) -> &M"
  },
  {
    "path": "program_structure/src/control_flow_graph/basic_block.rs",
    "chars": 6945,
    "preview": "use log::trace;\nuse std::collections::HashSet;\nuse std::fmt;\n\nuse crate::ir::declarations::Declarations;\nuse crate::ir::"
  },
  {
    "path": "program_structure/src/control_flow_graph/cfg.rs",
    "chars": 19115,
    "preview": "use log::debug;\nuse std::collections::HashSet;\nuse std::fmt;\nuse std::time::{Instant, Duration};\n\nuse crate::constants::"
  },
  {
    "path": "program_structure/src/control_flow_graph/errors.rs",
    "chars": 4947,
    "preview": "use thiserror::Error;\n\nuse crate::report_code::ReportCode;\nuse crate::report::Report;\nuse crate::file_definition::{FileI"
  },
  {
    "path": "program_structure/src/control_flow_graph/lifting.rs",
    "chars": 15966,
    "preview": "use log::{debug, trace};\nuse std::collections::HashSet;\n\nuse crate::ast;\nuse crate::ast::Definition;\n\nuse crate::constan"
  },
  {
    "path": "program_structure/src/control_flow_graph/mod.rs",
    "chars": 214,
    "preview": "pub mod basic_block;\npub mod errors;\npub mod parameters;\n\nmod cfg;\nmod lifting;\nmod ssa_impl;\nmod unique_vars;\n\npub use "
  },
  {
    "path": "program_structure/src/control_flow_graph/parameters.rs",
    "chars": 2267,
    "preview": "use crate::ast::Definition;\nuse crate::file_definition::{FileID, FileLocation};\nuse crate::function_data::FunctionData;\n"
  },
  {
    "path": "program_structure/src/control_flow_graph/ssa_impl.rs",
    "chars": 17394,
    "preview": "use log::{debug, trace, warn};\nuse std::collections::HashSet;\nuse std::convert::TryInto;\nuse std::ops::Range;\n\nuse crate"
  },
  {
    "path": "program_structure/src/control_flow_graph/unique_vars.rs",
    "chars": 12810,
    "preview": "use log::trace;\nuse std::convert::{TryFrom, TryInto};\n\nuse super::errors::{CFGError, CFGResult};\nuse super::parameters::"
  },
  {
    "path": "program_structure/src/intermediate_representation/declarations.rs",
    "chars": 2542,
    "preview": "use std::collections::HashMap;\n\nuse crate::file_definition::{FileID, FileLocation};\nuse crate::ir::*;\n\n/// A structure u"
  },
  {
    "path": "program_structure/src/intermediate_representation/degree_meta.rs",
    "chars": 17384,
    "preview": "use log::trace;\nuse std::cmp::{Ordering, min, max};\nuse std::collections::HashMap;\nuse std::fmt;\n\nuse super::{VariableNa"
  },
  {
    "path": "program_structure/src/intermediate_representation/errors.rs",
    "chars": 2050,
    "preview": "use thiserror::Error;\n\nuse crate::report_code::ReportCode;\nuse crate::report::Report;\nuse crate::file_definition::{FileI"
  },
  {
    "path": "program_structure/src/intermediate_representation/expression_impl.rs",
    "chars": 43004,
    "preview": "use log::trace;\nuse num_traits::Zero;\nuse std::collections::HashSet;\nuse std::fmt;\nuse std::hash::{Hash, Hasher};\n\nuse c"
  },
  {
    "path": "program_structure/src/intermediate_representation/ir.rs",
    "chars": 11190,
    "preview": "use num_bigint::BigInt;\nuse std::fmt;\n\nuse crate::file_definition::{FileID, FileLocation};\nuse crate::nonempty_vec::NonE"
  },
  {
    "path": "program_structure/src/intermediate_representation/lifting.rs",
    "chars": 16539,
    "preview": "use crate::ast::{self, LogArgument};\nuse crate::report::ReportCollection;\n\nuse crate::ir;\nuse crate::ir::declarations::{"
  },
  {
    "path": "program_structure/src/intermediate_representation/mod.rs",
    "chars": 204,
    "preview": "pub mod declarations;\npub mod degree_meta;\npub mod errors;\npub mod type_meta;\npub mod value_meta;\npub mod variable_meta;"
  },
  {
    "path": "program_structure/src/intermediate_representation/statement_impl.rs",
    "chars": 15943,
    "preview": "use log::trace;\nuse std::fmt;\n\nuse super::declarations::Declarations;\nuse super::ir::*;\nuse super::degree_meta::{Degree,"
  },
  {
    "path": "program_structure/src/intermediate_representation/type_meta.rs",
    "chars": 1935,
    "preview": "use super::declarations::Declarations;\nuse super::ir::VariableType;\n\npub trait TypeMeta {\n    /// Propagate variable typ"
  },
  {
    "path": "program_structure/src/intermediate_representation/value_meta.rs",
    "chars": 4542,
    "preview": "use num_bigint::BigInt;\nuse std::collections::HashMap;\nuse std::fmt;\n\nuse crate::constants::UsefulConstants;\n\nuse super:"
  },
  {
    "path": "program_structure/src/intermediate_representation/variable_meta.rs",
    "chars": 7378,
    "preview": "use std::fmt;\nuse std::collections::HashSet;\n\nuse super::ir::{AccessType, Meta, VariableName};\n\n/// A variable use (a va"
  },
  {
    "path": "program_structure/src/lib.rs",
    "chars": 458,
    "preview": "extern crate num_bigint_dig as num_bigint;\nextern crate num_traits;\n\npub mod abstract_syntax_tree;\npub mod control_flow_"
  },
  {
    "path": "program_structure/src/program_library/file_definition.rs",
    "chars": 1620,
    "preview": "use codespan_reporting::files::{Files, SimpleFiles};\nuse std::{ops::Range, collections::HashSet};\n\npub type FileSource ="
  },
  {
    "path": "program_structure/src/program_library/function_data.rs",
    "chars": 1918,
    "preview": "use super::ast::{FillMeta, Statement};\nuse super::file_definition::FileID;\nuse crate::file_definition::FileLocation;\nuse"
  },
  {
    "path": "program_structure/src/program_library/mod.rs",
    "chars": 199,
    "preview": "use super::ast;\npub mod report_code;\npub mod report;\npub mod file_definition;\npub mod function_data;\npub mod program_arc"
  },
  {
    "path": "program_structure/src/program_library/program_archive.rs",
    "chars": 4677,
    "preview": "use super::ast::{Definition, Expression, MainComponent};\nuse super::file_definition::{FileID, FileLibrary};\nuse super::f"
  },
  {
    "path": "program_structure/src/program_library/program_merger.rs",
    "chars": 4016,
    "preview": "use super::ast::Definition;\nuse super::report_code::ReportCode;\nuse super::report::Report;\nuse super::file_definition::F"
  },
  {
    "path": "program_structure/src/program_library/report.rs",
    "chars": 6216,
    "preview": "use anyhow::anyhow;\nuse std::cmp::Ordering;\nuse std::fmt::Display;\nuse std::str::FromStr;\n\nuse codespan_reporting::diagn"
  },
  {
    "path": "program_structure/src/program_library/report_code.rs",
    "chars": 13805,
    "preview": "const DOC_URL: &str = \"https://github.com/trailofbits/circomspect/blob/main/doc/analysis_passes.md\";\n\n#[derive(Copy, Clo"
  },
  {
    "path": "program_structure/src/program_library/template_data.rs",
    "chars": 6704,
    "preview": "use super::ast;\nuse super::ast::{FillMeta, Statement};\nuse super::file_definition::FileID;\nuse crate::file_definition::F"
  },
  {
    "path": "program_structure/src/program_library/template_library.rs",
    "chars": 3889,
    "preview": "use std::collections::HashMap;\n\nuse crate::ast::Definition;\nuse crate::file_definition::{FileID, FileLibrary};\nuse crate"
  },
  {
    "path": "program_structure/src/static_single_assignment/dominator_tree.rs",
    "chars": 5958,
    "preview": "use log::trace;\nuse std::collections::HashSet;\nuse std::marker::PhantomData;\n\nuse super::traits::DirectedGraphNode;\n\ntyp"
  },
  {
    "path": "program_structure/src/static_single_assignment/errors.rs",
    "chars": 1236,
    "preview": "use crate::report_code::ReportCode;\nuse crate::report::Report;\nuse crate::file_definition::{FileID, FileLocation};\n\n/// "
  },
  {
    "path": "program_structure/src/static_single_assignment/mod.rs",
    "chars": 3535,
    "preview": "//! This module implements a generic conversion into single-static assignment\n//! form.\npub mod dominator_tree;\npub mod "
  },
  {
    "path": "program_structure/src/static_single_assignment/traits.rs",
    "chars": 5081,
    "preview": "use log::trace;\nuse std::collections::HashSet;\nuse std::hash::Hash;\n\nuse super::errors::SSAResult;\n\npub trait SSAConfig:"
  },
  {
    "path": "program_structure/src/utils/constants.rs",
    "chars": 2097,
    "preview": "use anyhow::{anyhow, Error};\nuse num_bigint::BigInt;\nuse std::fmt;\nuse std::str::FromStr;\n\n#[derive(Default, Clone, Part"
  },
  {
    "path": "program_structure/src/utils/environment.rs",
    "chars": 17168,
    "preview": "use std::collections::HashMap;\nuse std::hash::Hash;\nuse std::marker::PhantomData;\n\npub trait VarInfo {}\npub trait Signal"
  },
  {
    "path": "program_structure/src/utils/mod.rs",
    "chars": 105,
    "preview": "pub mod constants;\npub mod environment;\npub mod nonempty_vec;\npub mod writers;\npub mod sarif_conversion;\n"
  },
  {
    "path": "program_structure/src/utils/nonempty_vec.rs",
    "chars": 6817,
    "preview": "use anyhow::{anyhow, Error};\nuse std::convert::TryFrom;\nuse std::ops::{Index, IndexMut};\n\n/// A vector type which is gua"
  },
  {
    "path": "program_structure/src/utils/sarif_conversion.rs",
    "chars": 7473,
    "preview": "use codespan_reporting::files::Files;\nuse log::{debug, trace};\nuse serde_sarif::sarif;\nuse std::collections::HashSet;\nus"
  },
  {
    "path": "program_structure/src/utils/writers.rs",
    "chars": 6720,
    "preview": "use anyhow;\nuse anyhow::Context;\nuse log::{info, warn};\nuse std::fmt::Display;\nuse std::fs::File;\nuse std::io::Write;\nus"
  },
  {
    "path": "program_structure_tests/Cargo.toml",
    "chars": 546,
    "preview": "[package]\nname = \"circomspect-program-structure-tests\"\nversion = \"0.8.0\"\nedition = \"2021\"\nrust-version = \"1.65\"\n\n[depend"
  },
  {
    "path": "program_structure_tests/src/control_flow_graph.rs",
    "chars": 14007,
    "preview": "use std::collections::{HashMap, HashSet};\n\nuse parser::parse_definition;\nuse program_structure::cfg::*;\nuse program_stru"
  },
  {
    "path": "program_structure_tests/src/lib.rs",
    "chars": 81,
    "preview": "#[cfg(test)]\nmod control_flow_graph;\n\n#[cfg(test)]\nmod static_single_assignment;\n"
  },
  {
    "path": "program_structure_tests/src/static_single_assignment.rs",
    "chars": 5786,
    "preview": "use std::collections::HashSet;\n\nuse parser::parse_definition;\nuse program_structure::cfg::{BasicBlock, Cfg, IntoCfg};\nus"
  }
]

About this extraction

This page contains the full source code of the trailofbits/circomspect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 99 files (692.1 KB), approximately 163.0k tokens, and a symbol index with 1364 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!