master df63525a226c cached
160 files
406.4 KB
101.2k tokens
380 symbols
1 requests
Download .txt
Showing preview only (459K chars total). Download the full file or copy to clipboard to get everything.
Repository: JetBrains/ruby-type-inference
Branch: master
Commit: df63525a226c
Files: 160
Total size: 406.4 KB

Directory structure:
gitextract_m6imoy85/

├── .gitignore
├── .travis.yml
├── FEATURES.md
├── LICENSE
├── README.md
├── arg_scanner/
│   ├── .gitignore
│   ├── Gemfile
│   ├── LICENSE.txt
│   ├── README.md
│   ├── Rakefile
│   ├── arg_scanner.gemspec
│   ├── bin/
│   │   ├── arg-scanner
│   │   ├── console
│   │   ├── rubymine-type-tracker
│   │   └── setup
│   ├── ext/
│   │   └── arg_scanner/
│   │       ├── arg_scanner.c
│   │       ├── arg_scanner.h
│   │       └── extconf.rb
│   ├── lib/
│   │   ├── arg_scanner/
│   │   │   ├── options.rb
│   │   │   ├── require_all.rb
│   │   │   ├── starter.rb
│   │   │   ├── state_tracker.rb
│   │   │   ├── type_tracker.rb
│   │   │   ├── version.rb
│   │   │   └── workspace.rb
│   │   └── arg_scanner.rb
│   ├── test/
│   │   ├── helper.rb
│   │   ├── test_args_info.rb
│   │   ├── test_call_info.rb
│   │   └── test_state_tracker.rb
│   └── util/
│       └── state_filter.rb
├── build.gradle
├── common/
│   ├── build.gradle
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── jetbrains/
│                       └── ruby/
│                           └── codeInsight/
│                               ├── Injector.kt
│                               ├── Logger.kt
│                               └── PrintToStdoutLogger.kt
├── contract-creator/
│   ├── build.gradle
│   └── src/
│       └── org/
│           └── jetbrains/
│               └── ruby/
│                   └── runtime/
│                       └── signature/
│                           └── server/
│                               ├── SignatureServer.kt
│                               ├── SignatureServerInjector.kt
│                               └── serialisation/
│                                   └── ServerResponseBean.kt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── ide-plugin/
│   ├── CHANGELOG.md
│   ├── build.gradle
│   ├── resources/
│   │   └── META-INF/
│   │       └── plugin.xml
│   └── src/
│       ├── com/
│       │   └── intellij/
│       │       └── execution/
│       │           └── executors/
│       │               ├── CollectStateExecutor.kt
│       │               └── RunWithTypeTrackerExecutor.java
│       ├── org/
│       │   └── jetbrains/
│       │       └── plugins/
│       │           └── ruby/
│       │               ├── IdePluginLogger.kt
│       │               ├── PluginResourceUtil.java
│       │               ├── RubyDynamicCodeInsightPluginInjector.kt
│       │               ├── ancestorsextractor/
│       │               │   ├── AncestorsExtractor.kt
│       │               │   └── RailsConsoleRunner.kt
│       │               ├── ruby/
│       │               │   ├── actions/
│       │               │   │   ├── ExportAncestorsActions.kt
│       │               │   │   ├── ExportAncesttorsDiffAction.kt
│       │               │   │   ├── ExportFileActionBase.kt
│       │               │   │   └── ImportExportContractsAction.kt
│       │               │   ├── codeInsight/
│       │               │   │   ├── ProjectLifecycleListenerImpl.kt
│       │               │   │   ├── RubyDynamicCodeInsightPluginAppLifecyctlListener.kt
│       │               │   │   ├── TrackerDataLoader.kt
│       │               │   │   ├── stateTracker/
│       │               │   │   │   ├── ClassHierarchySymbolProvider.kt
│       │               │   │   │   └── RubyClassHierarchyWithCaching.kt
│       │               │   │   ├── symbols/
│       │               │   │   │   └── structure/
│       │               │   │   │       └── RMethodSyntheticSymbol.java
│       │               │   │   └── types/
│       │               │   │       ├── RubyCollectStateRunner.kt
│       │               │   │       ├── RubyRunWithTypeTrackerRunner.kt
│       │               │   │       └── RubyTypeProvider.kt
│       │               │   ├── intentions/
│       │               │   │   ├── AddContractAnnotationIntention.java
│       │               │   │   ├── BaseRubyMethodIntentionAction.kt
│       │               │   │   └── RemoveCollectedInfoIntention.kt
│       │               │   ├── persistent/
│       │               │   │   └── TypeInferenceDirectory.kt
│       │               │   └── run/
│       │               │       └── configuration/
│       │               │           ├── CollectExecSettings.java
│       │               │           └── RunWithTypeTrackerRunConfigurationExtension.java
│       │               ├── settings/
│       │               │   ├── RubyTypeContractsConfigurable.kt
│       │               │   ├── RubyTypeContractsConfigurableUI.kt
│       │               │   └── RubyTypeContractsSettings.kt
│       │               └── util/
│       │                   └── SignatureServerUtil.kt
│       └── test/
│           ├── java/
│           │   ├── CallStatCompletionTest.kt
│           │   └── org/
│           │       └── jetbrains/
│           │           └── plugins/
│           │               └── ruby/
│           │                   └── ruby/
│           │                       └── actions/
│           │                           └── ImportExportTests.kt
│           └── testData/
│               ├── anonymous_module_method_call_test.rb
│               ├── call_info_of_nested_class_test.rb
│               ├── duplicates_in_callinfo_table_test.rb
│               ├── forget_call_info_when_arguments_number_changed_test_part_1.rb
│               ├── forget_call_info_when_arguments_number_changed_test_part_2.rb
│               ├── in_project_root_test/
│               │   ├── gem_like.rb
│               │   └── in_project_root_test.rb
│               ├── merge_test1.rb
│               ├── merge_test1_to_run.rb
│               ├── merge_test2.rb
│               ├── merge_test2_to_run.rb
│               ├── method_without_parameters_test.rb
│               ├── multiple_execution_test1.rb
│               ├── multiple_execution_test2.rb
│               ├── multiple_execution_test2_to_run.rb
│               ├── ref_links_test.rb
│               ├── ref_links_test_to_run.rb
│               ├── ruby_exec_part_2.rb
│               ├── ruby_exec_test.rb
│               ├── sample_kw_test.rb
│               ├── sample_kw_test_to_run.rb
│               ├── sample_test.rb
│               ├── sample_test_to_run.rb
│               ├── save_types_between_launches_test_part_1.rb
│               ├── save_types_between_launches_test_part_2.rb
│               ├── simple_call_info_collection_test.rb
│               ├── simple_call_info_collection_test_multiple_functions_test.rb
│               ├── simple_call_info_collection_with_multiple_arguments_test.rb
│               └── top_level_methods_call_info_collection_test.rb
├── ruby-call-signature/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── org/
│       │           └── jetbrains/
│       │               └── ruby/
│       │                   └── codeInsight/
│       │                       └── types/
│       │                           └── signature/
│       │                               ├── CallInfo.kt
│       │                               ├── ClassInfo.kt
│       │                               ├── GemInfo.kt
│       │                               ├── MethodInfo.kt
│       │                               ├── ParameterInfo.java
│       │                               ├── RSignatureContract.java
│       │                               ├── RSignatureContractContainer.kt
│       │                               ├── RSignatureContractNode.java
│       │                               ├── RTuple.java
│       │                               ├── SignatureContract.kt
│       │                               ├── SignatureInfo.kt
│       │                               ├── contractTransition/
│       │                               │   ├── ContractTransition.java
│       │                               │   ├── ReferenceContractTransition.java
│       │                               │   ├── TransitionHelper.java
│       │                               │   └── TypedContractTransition.java
│       │                               └── serialization/
│       │                                   ├── MethodInfoSerialization.kt
│       │                                   ├── RmcDirectory.kt
│       │                                   ├── SignatureContractSerialization.kt
│       │                                   └── TestSerialization.kt
│       └── test/
│           └── java/
│               └── org/
│                   └── jetbrains/
│                       └── ruby/
│                           └── codeInsight/
│                               └── types/
│                                   └── signature/
│                                       ├── GemInfoFromPathTest.kt
│                                       ├── SignatureContractMergeTest.kt
│                                       ├── SignatureContractSerializationTest.kt
│                                       └── SignatureContractTestBase.kt
├── settings.gradle
├── signature-viewer/
│   ├── build.gradle
│   └── src/
│       └── org/
│           └── jetbrains/
│               └── ruby/
│                   └── runtime/
│                       └── signature/
│                           ├── DBViewer.kt
│                           ├── EraseLocation.kt
│                           ├── SignatureExport.kt
│                           ├── SignatureImport.kt
│                           ├── SignatureViewer.kt
│                           └── SplitDB.kt
├── state-tracker/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── org/
│       │           └── jetbrains/
│       │               └── ruby/
│       │                   └── stateTracker/
│       │                       ├── RubyClassHierarchy.kt
│       │                       └── RubyClassHierarchyLoader.kt
│       └── test/
│           └── java/
│               ├── org/
│               │   └── jetbrains/
│               │       └── ruby/
│               │           └── stateTracker/
│               │               ├── RubyClassHierarchyLoaderNonStandardModuleTypeTest.kt
│               │               └── RubyClassHierarchyLoaderTest.kt
│               └── testData/
│                   ├── classes.json
│                   └── non-standard-module-type.json
└── storage-server-api/
    ├── build.gradle
    └── src/
        ├── main/
        │   └── java/
        │       └── org/
        │           └── jetbrains/
        │               └── ruby/
        │                   └── codeInsight/
        │                       └── types/
        │                           ├── signature/
        │                           │   └── serialization/
        │                           │       └── BlobSerialization.kt
        │                           └── storage/
        │                               └── server/
        │                                   ├── DatabaseProvider.kt
        │                                   ├── RSignatureProvider.java
        │                                   ├── RSignatureStorage.java
        │                                   ├── StorageException.java
        │                                   ├── impl/
        │                                   │   ├── IntIdTableWithPossibleDependency.kt
        │                                   │   ├── RSignatureProviderImpl.kt
        │                                   │   ├── RowConversions.kt
        │                                   │   └── Schema.kt
        │                                   └── testutil/
        │                                       └── DatabaseTestUtils.kt
        └── test/
            └── java/
                └── org/
                    └── jetbrains/
                        └── ruby/
                            └── codeInsight/
                                └── types/
                                    └── storage/
                                        └── server/
                                            └── impl/
                                                └── RSignatureProviderTest.kt

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

================================================
FILE: .gitignore
================================================
/build/
out/
*/build/
.gradle

.idea/

**/*.iml
**/.rakeTasks
arg_scanner/arg_scanner.iml


================================================
FILE: .travis.yml
================================================
language: ruby
dist: trusty
os:
  - linux
#  - osx

rvm:
  - 2.3.3
  - 2.4.2
  - ruby-head

matrix:
  fast_finish: true
  allow_failures:
    - rvm: ruby-head

services:
  - mysql

cache:
  directories:
    - $HOME/.gradle/caches/
    - $HOME/.gradle/wrapper/

before_install:
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update       ; fi
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install mysql; fi
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mysql.server start; fi
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mysql -u root -e "CREATE USER 'travis'@'127.0.0.1' IDENTIFIED BY '';"; fi
  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mysql -u root -e "FLUSH PRIVILEGES;"; fi
  - mysql -u root -e 'CREATE DATABASE ruby_type_contracts;'
  - mysql -u root -e 'GRANT ALL ON ruby_type_contracts.* TO 'travis'@'127.0.0.1';'
  - cd arg_scanner

script:
  - gem install rake
  - rake test
  - rake install
  - cd ..
  - travis_wait 40 ./gradlew tasks
  - ./gradlew -Dmysql.user.name=travis -Dmysql.user.password=""  test

================================================
FILE: FEATURES.md
================================================
# ruby-type-inference features

This doc contains `ruby-type-inference` features which can be useful 
for you after running your ruby program under type tracker:

![Run with type tracker](screenshots/run_with_type_tracker.png)

## Type providing for method parameters

![Parameter type providing](screenshots/parameter_type_providing.png)


## Type providing for return value

![Return type providing](screenshots/return_type_providing.png)

## Side notes

As now RubyMine has more information about types it can provide 
more reliable code completion, code analysis and other code insight features


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2016-2017 JetBrains s.r.o.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
Automated Type Contracts Generation [![JetBrains incubator project](http://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![Build Status](https://travis-ci.org/JetBrains/ruby-type-inference.svg?branch=master)](https://travis-ci.org/JetBrains/ruby-type-inference)
===================================

`ruby-type-inference` project is a completely new approach to
tackle the problems of Ruby dynamic nature and provide more reliable
symbol resolution and type inference. It collects some run time data
to build type contracts for the methods.

Every time a method is being called, some arguments of
particular types are being passed to it. Type Tracker collects
all such argument combinations and then builds a special contract
which satisfies all encountered argument type tuples. 

The approach has its own pros and cons:
* The obtained contracts utilize real-world usages of code of
  any complexity so it provides true results even if a method
  utilizes dynamic Ruby features heavily.
* The completeness of the contracts obtained for a method highly
  depends on the coverage of that method, including its callees.
  That implies the need to merge the data obtained from the
  different sources (e.g. different projects using the same gem).
  
This implementation addresses the stated coverage problem by providing
the possibility to merge any type contracts at any time.
     
## Usage

For simple usage you need to install the [Ruby Dynamic Code Insight](https://plugins.jetbrains.com/plugin/10227-ruby-dynamic-code-insight) 
plugin for RubyMine. Then this plugin will require the [arg_scanner](https://rubygems.org/gems/arg_scanner) gem to be installed.
See [arg_scanner installation instruction](arg_scanner/README.md#installation) if you have problems while installation. 

After that, you will have the possibility to run your programs under type tracker:

![Run with type tracker](screenshots/run_with_type_tracker.png)

Or you can run your programs in terminal via the `rubymine-type-tracker` binary (But you have to keep your project opened 
in RubyMine). E.g.:
```
rubymine-type-tracker bin/rails server
```

The `rubymine-type-tracker` binary is included into the [arg_scanner](https://rubygems.org/gems/arg_scanner) gem.

See [FEATURES.md](FEATURES.md) for understanding what benefits you will have after running your program under type tracker.
     
## Architecture
 
* **arg_scanner** is a gem with a native extension to attach to 
  ruby processes and trace and intercept all method calls to log 
  type-wise data flow in runtime.
  
  See [`arg_scanner`] documentation for details on usage.

* The [**type contract processor**](contract-creator) server listens for
  incoming type data (from `arg_scanner`) and processes it to a compact format.
  
  The data stored may be used later for better code analysis and also
  can be shared with other users.

* Code analysis clients (a RubyMine/IJ+Ruby [plugin](ide-plugin)) use the contract data
  to provide features for the users such as code completion, better resolution, etc.

* (_todo_) Signature server receives contracts anonymously from the users and provides
  a compiled contract collections for popular gems.

## Running project from sources

#### Prerequisites

The [`arg_scanner`] gem is used for collecting type information. It can be installed manually 
to the target SDK and requires MRI Ruby at least 2.3.

#### Running type tracker

There are two possibilities to use the type tracker:
_(I)_ using IJ/RubyMine plugin or _(II)_ requiring it from Ruby code.

##### Using RubyMine plugin

The easiest way to run the plugin (and the most convenient for its development) is
running it with special gradle task against IJ Ultimate snapshot:
 
```
./gradlew ide-plugin:runIde
```

The task will compile the plugin, run IJ Ultimate with plugin "installed" in it.
There is no need in running anything manually in that case.

If you want to try it with existing RubyMine instance,
you should:

1. Build it via `./gradlew ide-plugin:buildPlugin`
2. Install plugin in the IDE
    * Navigate to `File | Settings | Plugins | Install plugin from disk...`
    * Locate plugin in `ide-plugin/build/distributions` and select.
    * Restart IDE.

Note that due to API changes the plugin may be incompatible with older RM instances.

##### Using command line

1. In order to collect the data for the script needs a contract server to be up and running;
   it could be run by running
   ```sh
   ./gradlew contract-creator:runServer --args path-to-db.mv.db
   ```
   where `path-to-db.mv.db` is path where type contracts will be stored (H2 database file).

1. Run the ruby script to be processed via [`arg-scanner`](arg_scanner/bin/arg-scanner)
   binary.

1. Use the data collected by the contract server.

## Contributions

Any kind of ideas, use cases, contributions and questions are very welcome
as the project is just incubating.
Please feel free to create issues for any sensible request.

[`arg_scanner`]: arg_scanner/README.md

================================================
FILE: arg_scanner/.gitignore
================================================
*.iml

.bundle/
.yardoc
Gemfile.lock
_yardoc/
coverage/
doc/
pkg/
spec/reports/
tmp/
*.bundle
*.so
*.o
*.a
mkmf.log


================================================
FILE: arg_scanner/Gemfile
================================================
source 'https://rubygems.org'

# Specify your gem's dependencies in arg_scanner.gemspec
gemspec

group :test do
  gem 'test-unit'
end


================================================
FILE: arg_scanner/LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2017 JetBrains

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: arg_scanner/README.md
================================================
# ArgScanner [![Gem Version](https://badge.fury.io/rb/arg_scanner.svg)](https://badge.fury.io/rb/arg_scanner)

`arg_scanner` is a gem with the purpose to track all method calls and
deliver the following information:

* Method signature (arguments, their names and kinds) and declaration place
* The types of argument variables given to each method call done

This information can be used then to calculate and use type contracts
for the analysed methods.

`arg_scanner` is meant to be used as a binary to run any other ruby executable
manually so including it in the `Gemfile` is not necessary.

## Installation

The recommended way to install it is to execute command:

```
gem install arg_scanner
```
**You will possibly need to install [native dependencies](#dependencies)**
    
## Building from sources

If you want to compile the gem from sources, just run the following commands:

```    
bundle install
bundle exec rake install
```
    
If you have problems with native extension compilation, make sure you have
actual version of [ruby-core-source gem](https://github.com/os97673/debase-ruby_core_source) and 
have [native dependencies](#dependencies) installed. 

## Dependencies

##### [Glib](https://developer.gnome.org/glib/)

macOS: `brew install glib`   
Debian/Ubuntu: `sudo apt install libglib2.0-dev`  
Arch Linux: `sudo pacman -S glib2`  

## Usage

`arg_scanner` provides the `arg-scanner` binary which receives any number of
arguments and executes the given command in type tracking mode,
for example:

```
arg-scanner --type-tracker --pipe-file-path=[pipe_file_path] bundle exec rake spec
```
`pipe_file_path` here is path to pipe file which is printed by server's stdout

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/JetBrains/ruby-type-inference

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).



================================================
FILE: arg_scanner/Rakefile
================================================
require "bundler/gem_tasks"
require "rake/extensiontask"
require 'rake/testtask'

BASE_TEST_FILE_LIST = Dir['test/**/test_*.rb']

task :build => :compile

Rake::ExtensionTask.new("arg_scanner") do |ext|
  ext.lib_dir = "lib/arg_scanner"
end

desc "Test arg_scanner."
Rake::TestTask.new(:test => [:clean, :compile]) do |t|
  t.libs += %w(./ext ./lib)
  t.test_files = FileList[BASE_TEST_FILE_LIST]
  t.verbose = true
end

task :test => :lib

task :default => [:clobber, :compile, :test]


================================================
FILE: arg_scanner/arg_scanner.gemspec
================================================
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'arg_scanner/version'

Gem::Specification.new do |spec|
  spec.name = "arg_scanner"
  spec.version = ArgScanner::VERSION
  spec.authors = ["Nickolay Viuginov", "Valentin Fondaratov", "Vladimir Koshelev"]
  spec.email = ["viuginov.nickolay@gmail.com", "fondarat@gmail.com", "vkkoshelev@gmail.com"]

  spec.summary = %q{Program execution tracker to retrieve data types information}
  spec.homepage = "https://github.com/jetbrains/ruby-type-inference"
  spec.license = "MIT"

  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
  # to allow pushing to a single host or delete this section to allow pushing to any host.
  # if spec.respond_to?(:metadata)
  #   spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
  # else
  #   raise "RubyGems 2.0 or newer is required to protect against " \
  #     "public gem pushes."
  # end

  spec.files = `git ls-files -z`.split("\x0").reject do |f|
    f.match(%r{^(test|spec|features)/})
  end
  spec.bindir = "bin"
  spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f)}
  spec.require_paths = ["lib"]
  spec.extensions = ["ext/arg_scanner/extconf.rb"]

  spec.add_development_dependency "bundler", ">= 1.13"
  spec.add_development_dependency "rake", ">= 12.0"
  spec.add_development_dependency "rake-compiler"
  spec.add_dependency "debase-ruby_core_source", ">= 0.10.4"
  spec.add_dependency "native-package-installer", ">= 1.0.0"
end


================================================
FILE: arg_scanner/bin/arg-scanner
================================================
#!/usr/bin/env ruby

require 'optparse'
require 'arg_scanner/options'
require 'arg_scanner/version'

options = ArgScanner::OPTIONS
option_parser = OptionParser.new do |opts|
  opts.banner = "arg-scanner #{ArgScanner::VERSION}" + <<~EOB
  
    Usage: arg-scanner [OPTIONS] <ruby cmdline> 
    arg-scanner is a ruby script mediator supposed to be run from the command line or IDE.
        The data will be sent to a signature server so it must be running during arg-scanner execution.
  EOB

  opts.separator "Options:"
  opts.on("--type-tracker", "enable type tracker") do
    options.enable_type_tracker = true
  end
  opts.on("--state-tracker", "enable state tracker") do
    options.enable_state_tracker = true
  end

  opts.on("--no-type-tracker", "disable type tracker") do
    options.enable_type_tracker = false
  end
  opts.on("--no-state-tracker", "disable state tracker") do
    options.enable_state_tracker = false
  end

  opts.on("--output-dir=[Dir]", String, "specify output directory (ignored by type tracker)") do |dir|
    options.output_dir = dir
  end

  opts.on("--catch-only-every-N-call=[N]", Integer, "randomly catches only 1/N of all calls to speed up performance (by default N = 1)") do |n|
    options.catch_only_every_n_call = n
  end
  opts.on("--project-root=[PATH]", String, "Specify project's root directory to catch every call from this directory. "\
      "Calls from other directories aren't guaranteed to be caught") do |path|
    options.project_root = path
  end

  opts.on("--pipe-file-path=[PATH]", String, "Specify pipe file path to connect to server") do |path|
    options.pipe_file_path = path
  end

  opts.on("--buffering", "enable buffering between arg-scanner and server. It speeds up arg-scanner but doesn't allow "\
      "to use arg-scanner \"interactively\". Disabled by default") do |buffering|
    options.buffering = buffering
  end
end

begin
  option_parser.parse! ARGV
rescue StandardError => e
  puts option_parser
  puts
  puts e.message
  exit 1
end

if ARGV.size < 1
  puts option_parser
  puts
  puts "Ruby program to trace must be specified."
  exit 1
end

options.set_env

old_opts = ENV['RUBYOPT'] || ''
starter = "-r #{File.expand_path(File.dirname(__FILE__))}/../lib/arg_scanner/starter"
unless old_opts.include? starter
  ENV['RUBYOPT'] = starter
  ENV['RUBYOPT'] += " #{old_opts}" if old_opts != ''
end

$0 = ARGV[0]
Kernel.exec *ARGV


================================================
FILE: arg_scanner/bin/console
================================================
#!/usr/bin/env ruby

require "bundler/setup"
require "arg_scanner"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

require "irb"
IRB.start


================================================
FILE: arg_scanner/bin/rubymine-type-tracker
================================================
#!/usr/bin/env ruby
# This is small script for launching type tracker under RubyMine's provided server. Acts like arg-scanner wrapper
require 'optparse'
require 'arg_scanner/version'
require 'tmpdir'
require 'json'

option_parser = OptionParser.new do |opts|
  opts.banner = <<~EOB
    rubymine-type-tracker #{ArgScanner::VERSION}
    
    Usage: rubymine-type-tracker <ruby script to execute>
        rubymine-type-tracker is a ruby script for easy launching some command under
        RubyMine's type tracker. The data will be sent to a server run by RubyMine. 
        So before launching this script be sure project is opened in RubyMine with 
        "Ruby Dynamic Code Insight" plugin installed.
  EOB
end

begin
  option_parser.parse! ARGV
  if ARGV.size == 0
    raise StandardError.new("")
  end
rescue StandardError => e
  puts option_parser
  exit 1
end

dot_ruby_type_inference_dir = File.join(Dir.tmpdir, ".ruby-type-inference")
if File.directory?(dot_ruby_type_inference_dir)
  match_jsons = Dir.foreach(dot_ruby_type_inference_dir).map do |file_name|
    if file_name == '.' || file_name == '..'
      next nil
    end
    json = JSON.parse(IO.read(File.join(dot_ruby_type_inference_dir, file_name)))
    if json["projectPath"] != Dir.pwd
      next nil
    end
    next json
  end.select { |x| x != nil }
else
  match_jsons = []
end

if match_jsons.count == 1
  json = match_jsons[0]
elsif match_jsons.count > 1
  STDERR.puts <<~EOB
      Critical error! You may try to:\n
      1. Close RubyMine
      2. Clean #{dot_ruby_type_inference_dir}
      3. Open RubyMine
  EOB
  exit 1
elsif match_jsons.count == 0
  STDERR.puts <<~EOB
      Error! You are possibly...
      * launching this script under directory different from project 
        opened in RubyMine (please `cd` to dir firstly)
      * haven't opened project in RubyMine 
      * haven't installed "Ruby Dynamic Code Insight" plugin in RubyMine
  EOB
  exit 1
end

to_exec = ["arg-scanner",
           "--type-tracker",
           "--project-root=#{json["projectPath"]}",
           "--pipe-file-path=#{json["pipeFilePath"]}",
           *ARGV]

Kernel.exec(*to_exec)


================================================
FILE: arg_scanner/bin/setup
================================================
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here


================================================
FILE: arg_scanner/ext/arg_scanner/arg_scanner.c
================================================
#include "arg_scanner.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <netinet/in.h>
#include <glib.h>

//#define DEBUG_ARG_SCANNER 1

#if RUBY_API_VERSION_CODE >= 20500
  #if (RUBY_RELEASE_YEAR == 2017 && RUBY_RELEASE_MONTH == 10 && RUBY_RELEASE_DAY == 10) //workaround for 2.5.0-preview1
    #define TH_CFP(thread) ((rb_control_frame_t *)(thread)->ec.cfp)
  #else
    #define TH_CFP(thread) ((rb_control_frame_t *)(thread)->ec->cfp)
  #endif
#else
  #define TH_CFP(thread) ((rb_control_frame_t *)(thread)->cfp)
#endif

#ifdef DEBUG_ARG_SCANNER
    #define LOG(f, args...) { fprintf(stderr, "DEBUG: '%s'=", #args); fprintf(stderr, f, ##args); fflush(stderr); }
#else
    #define LOG(...) {}
#endif

#define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
typedef struct rb_trace_arg_struct rb_trace_arg_t;

VALUE mArgScanner = Qnil;
int types_ids[20];

static VALUE c_signature;

/**
 * Contains info related to explicitly passed args
 * For example:
 * def foo(a, b = 1); end
 *
 * `b` passed here implicitly:
 * foo(1)
 *
 * But here explicitly:
 * foo(1, 10)
 */
typedef struct
{
    ssize_t call_info_explicit_argc;   // Number of arguments that was explicitly passed by user
    char **call_info_kw_explicit_args; // kw arguments names that was explicitly passed by user (null terminating array)
} call_info_t;

typedef struct
{
    char *receiver_name;
    char *method_name;
    char *args_info;
    char *path;
    char *return_type_name;
    ssize_t explicit_argc; // Number of arguments that was explicitly passed by user
    int lineno;
    int is_in_project_root; // Can be 0, 1 or -1 when project_root is not specified
} signature_t;

void Init_arg_scanner();

static const char *ARG_SCANNER_EXIT_COMMAND = "EXIT";
static const char *EMPTY_VALUE = "";
static const int MAX_NUMBER_OF_MISSED_CALLS = 10;
/**
 * There we keep information about signatures that have already been sent to server in order to not sent them again
 */
static GTree *sent_to_server_tree;
/**
 * Here we store map with key: signature_t and value: int number (how many times method was called with the same args)
 * If we got that any method is called with the same args more than MAX_NUMBER_OF_MISSED_CALLS times in a row then
 * we will ignore it.
 */
static GTree *number_missed_calls_tree;
static GSList *call_stack = NULL;
static char *get_args_info(const char *const *explicit_kw_args);
static VALUE handle_call(VALUE self, VALUE tp);
static VALUE handle_return(VALUE self, VALUE tp);
static VALUE destructor(VALUE self);
static const char *calc_sane_class_name(VALUE ptr);

// returns Qnil if ready; or string containing error message otherwise 
static VALUE check_if_arg_scanner_ready(VALUE self);

// For testing
static VALUE get_args_info_rb(VALUE self);
static VALUE get_call_info_rb(VALUE self);

static call_info_t get_call_info();
static bool is_call_info_needed();

static void call_info_t_free(call_info_t s)
{
    free(s.call_info_kw_explicit_args);
}

static void signature_t_free(signature_t *s)
{
    free(s->receiver_name);
    free(s->method_name);
    free(s->args_info);
    free(s->path);
    free(s->return_type_name);
    free(s);
}

// Free signature_t partially leaving parts that are used in sent_to_server_tree_comparator
// @see_also sent_to_server_tree_comparator
static void signature_t_free_partially(signature_t *s)
{
    free(s->receiver_name);
    s->receiver_name = NULL;

    free(s->method_name);
    s->method_name = NULL;
}

// Comparator for number_missed_calls_tree.
static gint
number_missed_calls_tree_comparator(gconstpointer x, gconstpointer y, gpointer user_data_ignored) {
    const signature_t *a = x;
    const signature_t *b = y;
    int ret;

    // Comparison using lineno and path theoretically should guarantees us unique.
    // And compare lineno firstly because it's faster O(1) than comparing path which is O(path_len)
    ret = a->lineno - b->lineno;
    if (ret != 0) return ret;

    ret = strcmp(a->path, b->path);
    if (ret != 0) return ret;

    return 0;
}

// Comparator for sent_to_server_tree.
// If you want to change the way it compare then don't forget to
// change signature_t_free_partially accordingly
// @see_also signature_t_free_partially
static gint
sent_to_server_tree_comparator(gconstpointer x, gconstpointer y, gpointer user_data_ignored) {
    const signature_t *a = x;
    const signature_t *b = y;
    int ret;

    ret = number_missed_calls_tree_comparator(x, y, user_data_ignored);
    if (ret != 0) return ret;

    if (a->args_info != NULL && b->args_info != NULL) {
        ret = strcmp(a->args_info, b->args_info);
        if (ret != 0) return ret;
    }

    ret = strcmp(a->return_type_name, b->return_type_name);
    if (ret != 0) return ret;

    return 0;
}

inline int start_with(const char *str, const char *prefix) {
    if (str == NULL || prefix == NULL) {
        return -1;
    }
    while (*str != '\0' && *prefix != '\0') {
        if (*str != *prefix) {
            return 0;
        }
        str++;
        prefix++;
    }
    return 1;
}

FILE *pipe_file = NULL;
static char *project_root = NULL;
static int catch_only_every_n_call = 1;

static int file_exists(const char *file_path) {
    return access(file_path, F_OK) != -1;
}

static VALUE init(VALUE self, VALUE pipe_file_path, VALUE buffering,
                  VALUE project_root_local, VALUE catch_only_every_n_call_local) {
    if (pipe_file_path != Qnil) {
        pipe_file_path = rb_file_s_expand_path(1, &pipe_file_path); // https://ruby-doc.org/core-2.2.0/File.html#method-c-expand_path
        const char *pipe_file_path_c = StringValueCStr(pipe_file_path);
        if (!file_exists(pipe_file_path_c)) {
            fprintf(stderr, "Specified pipe file: %s doesn't exists\n", pipe_file_path_c);
            exit(1);
        }
        pipe_file = fopen(pipe_file_path_c, "w");
        if (pipe_file == NULL) {
            fprintf(stderr, "Cannot open pipe file \"%s\" with write access\n", pipe_file_path_c);
            exit(1);
        }

        int buffering_disabled = buffering == Qnil;
        if (buffering_disabled) {
            setbuf(pipe_file, NULL);
        }
    }
    if (project_root_local != Qnil) {
        project_root = strdup(StringValueCStr(project_root_local));
    }
    if (catch_only_every_n_call_local != Qnil) {
        if (sscanf(StringValueCStr(catch_only_every_n_call_local), "%d", &catch_only_every_n_call) != 1) {
            fprintf(stderr, "Please specify number in --catch-only-every-N-call arg\n");
            exit(1);
        }
        srand(time(0));
    }
    return Qnil;
}

void Init_arg_scanner() {
    mArgScanner = rb_define_module("ArgScanner");
    rb_define_module_function(mArgScanner, "handle_call", handle_call, 1);
    rb_define_module_function(mArgScanner, "handle_return", handle_return, 1);
    rb_define_module_function(mArgScanner, "get_args_info", get_args_info_rb, 0);
    rb_define_module_function(mArgScanner, "get_call_info", get_call_info_rb, 0);
    rb_define_module_function(mArgScanner, "destructor", destructor, 0);
    rb_define_module_function(mArgScanner, "check_if_arg_scanner_ready", check_if_arg_scanner_ready, 0);
    rb_define_module_function(mArgScanner, "init", init, 4);

    sent_to_server_tree = g_tree_new_full(/*key_compare_func =*/sent_to_server_tree_comparator,
                                          /*key_compare_data =*/NULL,
                                          /*key_destroy_func =*/(GDestroyNotify)signature_t_free,
                                          /*value_destroy_func =*/NULL);

    // key_destroy_func is NULL because we will use the same keys for number_missed_calls_tree
    // and sent_to_server_tree. And all memory management is done by sent_to_server_tree
    number_missed_calls_tree = g_tree_new_full(/*key_compare_func =*/number_missed_calls_tree_comparator,
                                               /*key_compare_data =*/NULL,
                                               /*key_destroy_func =*/NULL,
                                               /*value_destroy_func =*/NULL);
}

inline void push_to_call_stack(signature_t *signature) {
    call_stack = g_slist_prepend(call_stack, (gpointer) signature);
}

inline signature_t *pop_from_call_stack() {
    if (call_stack == NULL) {
        return NULL;
    }
    signature_t *ret = (signature_t *) call_stack->data;

    GSList *old_head = call_stack;
    call_stack = g_slist_remove_link(call_stack, old_head);
    g_slist_free_1(old_head);
    return ret;
}

inline int is_call_stack_empty() {
    return call_stack == NULL;
}

/**
 * Looks at the object at the top of this stack without removing it from the stack.
 */
inline signature_t *top_of_call_stack() {
    if (call_stack == NULL) {
        return NULL;
    }
    return (signature_t *) call_stack[0].data;
}

rb_control_frame_t *
my_rb_vm_get_binding_creatable_next_cfp(const rb_thread_t *th, const rb_control_frame_t *cfp)
{
    while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
        if (cfp->iseq) {
            return (rb_control_frame_t *)cfp;
        }
	    cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
    }
    return 0;
}

static VALUE exit_from_handle_call_skipping_call() {
    push_to_call_stack(NULL);
    return Qnil;
}

static VALUE
handle_call(VALUE self, VALUE tp)
{
    signature_t sign_temp;
    memset(&sign_temp, 0, sizeof(sign_temp));
    sign_temp.lineno = FIX2INT(rb_funcall(tp, rb_intern("lineno"), 0)); // Convert Ruby's Fixnum to C language int
    VALUE path = rb_funcall(tp, rb_intern("path"), 0);
    path = rb_file_s_expand_path(1, &path); // https://ruby-doc.org/core-2.2.0/File.html#method-c-expand_path
    sign_temp.path = StringValueCStr(path);

    int is_in_project_root = start_with(sign_temp.path, project_root);

    if (project_root != NULL && !is_in_project_root) {
        signature_t *peek = top_of_call_stack();

        if (!is_call_stack_empty() && (peek == NULL || !(peek->is_in_project_root))) {
            return exit_from_handle_call_skipping_call();
        }
    }

    if (project_root == NULL || !is_in_project_root) {
        int number_of_missed_calls = (int)g_tree_lookup(number_missed_calls_tree, &sign_temp);
        if (number_of_missed_calls > MAX_NUMBER_OF_MISSED_CALLS) {
            return exit_from_handle_call_skipping_call();
        }
    }

    if (catch_only_every_n_call != 1 && rand() % catch_only_every_n_call != 0) {
        return exit_from_handle_call_skipping_call();
    }

    signature_t *sign = (signature_t *) calloc(1, sizeof(*sign));

    sign->is_in_project_root = is_in_project_root;
    sign->lineno = sign_temp.lineno;
    sign->path = strdup(sign_temp.path);
    sign->method_name = strdup(rb_id2name(SYM2ID(rb_funcall(tp, rb_intern("method_id"), 0))));
    sign->explicit_argc = -1;

#ifdef DEBUG_ARG_SCANNER
    LOG("Getting args info for %s %s %d \n", sign->method_name, sign->path, sign->lineno);
#endif
    call_info_t info;
    info.call_info_kw_explicit_args = NULL;
    if (is_call_info_needed()) {
        info = get_call_info();
        sign->explicit_argc = info.call_info_explicit_argc;
    }

    sign->args_info = get_args_info(info.call_info_kw_explicit_args);
    call_info_t_free(info);

    if (sign->args_info != NULL && strlen(sign->args_info) >= 1000) {
        signature_t_free(sign);
        return exit_from_handle_call_skipping_call();
    }

    push_to_call_stack(sign);
    return Qnil;
}

static VALUE
handle_return(VALUE self, VALUE tp)
{
    signature_t *sign = pop_from_call_stack();
    if (sign == NULL) {
        return Qnil;
    }
    VALUE defined_class = rb_funcall(tp, rb_intern("defined_class"), 0);

    VALUE receiver_name = rb_mod_name(defined_class);

    // if defined_class is nil then it means that method is invoked from anonymous module.
    // Then trying to extract name of it's anonymous module. For more details see
    // CallStatCompletionTest#testAnonymousModuleMethodCall
    if (receiver_name == Qnil) {
        VALUE this = rb_funcall(tp, rb_intern("self"), 0);
        receiver_name = rb_funcall(this, rb_intern("to_s"), 0);
    }

    VALUE return_type_name = rb_funcall(tp, rb_intern("return_value"), 0);

    sign->receiver_name = strdup(StringValueCStr(receiver_name));
    sign->return_type_name = strdup(calc_sane_class_name(return_type_name));

    signature_t *sign_in_sent_to_server_tree = g_tree_lookup(sent_to_server_tree, sign);
    if (sign_in_sent_to_server_tree == NULL) {
        // Resets number of missed calls to 0
        g_tree_insert(number_missed_calls_tree, /*key = */sign, /*value = */0);

        // GTree will free memory allocated by sign by itself
        g_tree_insert(sent_to_server_tree, /*key = */sign, /*value = */sign);

        if (pipe_file != NULL) {
            fprintf(pipe_file,
                "{\"method_name\":\"%s\",\"call_info_argc\":\"%d\",\"args_info\":\"%s\",\"visibility\":\"%s\","
                "\"path\":\"%s\",\"lineno\":\"%d\",\"receiver_name\":\"%s\",\"return_type_name\":\"%s\"}\n",
                sign->method_name,
                sign->explicit_argc,
                sign->args_info != NULL ? sign->args_info : "",
                "PUBLIC",
                sign->path,
                sign->lineno,
                sign->receiver_name,
                sign->return_type_name);
        }

        signature_t_free_partially(sign);
    } else if (project_root == NULL || !sign->is_in_project_root) {
        signature_t_free(sign);

        int found = (int) g_tree_lookup(number_missed_calls_tree, sign_in_sent_to_server_tree);
        g_tree_insert(number_missed_calls_tree, /*key = */sign_in_sent_to_server_tree, /*value = */found + 1);
    }
    return Qnil;
}

static call_info_t
get_call_info() {
    rb_thread_t *thread = ruby_current_thread;
    rb_control_frame_t *cfp = TH_CFP(thread);

    call_info_t empty;
    empty.call_info_kw_explicit_args = NULL;
    empty.call_info_explicit_argc = -1;

    cfp += 3;
    cfp = my_rb_vm_get_binding_creatable_next_cfp(thread, cfp);

    if(cfp->iseq == NULL || cfp->pc == NULL || cfp->iseq->body == NULL) {
        return empty;
    }

    const rb_iseq_t *iseq = (const rb_iseq_t *) cfp->iseq;

    ptrdiff_t pc = cfp->pc - cfp->iseq->body->iseq_encoded;

    const VALUE *iseq_original = rb_iseq_original_iseq(iseq);

    int indent;
    for (indent = 1; indent < 6; indent++) {
        VALUE insn = iseq_original[pc - indent];
        int tmp = (int)insn;
        if(0 < tmp && tmp < 256) {
            if(indent < 3) {
                return empty;
            }
            call_info_t info;
            struct rb_call_info *ci = (struct rb_call_info *)iseq_original[pc - indent + 1];
            info.call_info_explicit_argc = ci->orig_argc;
            info.call_info_kw_explicit_args = NULL;

            if (ci->flag & VM_CALL_KWARG) {
                struct rb_call_info_kw_arg *kw_args = ((struct rb_call_info_with_kwarg *)ci)->kw_arg;

                size_t kwArgSize = kw_args->keyword_len;

                VALUE kw_ary = rb_ary_new_from_values(kw_args->keyword_len, kw_args->keywords);

                info.call_info_kw_explicit_args = (char **) malloc((kwArgSize + 1)*sizeof(*(info.call_info_kw_explicit_args)));

                int i;
                for (i = kwArgSize -1 ; i >= 0; --i) {
                    VALUE kw = rb_ary_pop(kw_ary);
                    const char *kw_name = rb_id2name(SYM2ID(kw));

                    info.call_info_kw_explicit_args[i] = kw_name;
                }
                info.call_info_kw_explicit_args[kwArgSize] = NULL;
            } else {
                info.call_info_kw_explicit_args = malloc(sizeof(*info.call_info_kw_explicit_args));
                info.call_info_kw_explicit_args[0] = NULL;
            }
            return info;
        }
    }
    return empty;
}

static const char*
calc_sane_class_name(VALUE ptr)
{
    VALUE klass = rb_obj_class(ptr);

    const char* klass_name;
    // may be false, see `object.c#rb_class_get_superclass`
    if (klass == Qfalse) {
        klass_name = "<err>";
    }
    else
    {
        klass_name = rb_class2name(klass);
    }

    // returned value may be NULL, see `variable.c#rb_class2name`
    if (klass_name == NULL)
    {
        klass_name = "<err>";
    }

    return klass_name;
}

static char *
fast_join_array(char sep, size_t count, const char **strings)
{
    size_t lengths[count + 1];
    size_t i;
    char *result;

    lengths[0] = 0;

    for (i = 0; i < count; i++)
    {
        const char *str = strings[i];
        size_t length;
        if (!str)
            length = 0;
        else
            length = strlen(str) + (i > 0); // 1 for separator before

        lengths[i + 1] = lengths[i] + length;
    }

    result = (char *)malloc(sizeof(*result) * (1 + lengths[count]));

    for (i = 0; i < count; i++)
    {
        const char *str = strings[i];
        if (str)
        {
            int start = lengths[i];
            if (i > 0)
                result[start++] = sep;

            memcpy(result + start, str, sizeof(*result) * (lengths[i + 1] - start));
        }
    }

    result[lengths[count]] = 0;

    return result;
}

static char *
fast_join(char sep, size_t count, ...)
{
    char *strings[count];
    size_t i;
    va_list ap;

    va_start(ap, count);
    for (i = 0; i < count; i++)
    {
        strings[i] = va_arg(ap, char *);
    }
    va_end(ap);

    return fast_join_array(sep, count, strings);
}

/**
 * Checks that `container` contains `element`
 */
static int contains(const char *const *container, const char *element) {
    if (container == NULL || element == NULL) {
        return 0;
    }
    const char *const *iterator = container;
    while (*iterator != NULL) {
        if (strcmp(*iterator, element) == 0) {
            return 1;
        }
        ++iterator;
    }
    return 0;
}

#define JOIN_KW_NAMES_AND_TYPES_BUF_SIZE 2048
static char join_kw_names_and_types_buf[JOIN_KW_NAMES_AND_TYPES_BUF_SIZE];
/**
 * Null terminating array which contains strings of explicitly passed kw args.
 * It's used for join_kw_names_and_types
 */
static const char *const *join_kw_names_and_types_explicit_kw_args = NULL;

/**
 * This function is used for concatenating hash keys and value's types.
 * Be sure that buf is at least JOIN_KW_NAMES_AND_TYPES_BUF_SIZE bytes.
 * If join_kw_names_and_types_buf size = JOIN_KW_NAMES_AND_TYPES_BUF_SIZE
 * isn't enough then this buf will contain invalid information
 */
static int join_kw_names_and_types(VALUE key, VALUE val, VALUE ignored) {
    const char *kw_name = rb_id2name(SYM2ID(key));
    const char *kw_type = calc_sane_class_name(val);

    const char *const *explicit_kw_args_iterator = join_kw_names_and_types_explicit_kw_args;

    // Just such behaviour: when join_kw_names_and_types_explicit_kw_args is
    // not provided then consider every kw arg as explicitly passed by user
    int is_explicit = explicit_kw_args_iterator == NULL;

    if (explicit_kw_args_iterator != NULL) {
        while(*explicit_kw_args_iterator != NULL) {
            if (strcmp(*explicit_kw_args_iterator, kw_name) == 0) {
                is_explicit = 1;
                break;
            }
            ++explicit_kw_args_iterator;
        }
    }

    if (is_explicit) {
        // Check that buf is not empty
        if (join_kw_names_and_types_buf[0] != '\0') {
            strncat(join_kw_names_and_types_buf, ";", JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1);
        }
        strncat(join_kw_names_and_types_buf, "KEYREST,", JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1);
        strncat(join_kw_names_and_types_buf, kw_type, JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1);
        strncat(join_kw_names_and_types_buf, ",", JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1);
        strncat(join_kw_names_and_types_buf, kw_name, JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1);
    }
    return ST_CONTINUE;
}

static char*
get_args_info(const char *const *explicit_kw_args)
{
    rb_thread_t *thread;
    rb_control_frame_t *cfp;

    thread = ruby_current_thread;
    cfp = TH_CFP(thread);

    cfp += 2;

    VALUE *ep = cfp->ep;
    ep -= cfp->iseq->body->local_table_size;

    size_t param_size = cfp->iseq->body->param.size;
    size_t lead_num = cfp->iseq->body->param.lead_num;
    size_t opt_num = cfp->iseq->body->param.opt_num;
    size_t post_num = cfp->iseq->body->param.post_num;

    unsigned int has_rest = cfp->iseq->body->param.flags.has_rest;
    unsigned int has_kw = cfp->iseq->body->param.flags.has_kw;
    unsigned int has_kwrest = cfp->iseq->body->param.flags.has_kwrest;
    unsigned int has_block = cfp->iseq->body->param.flags.has_block;

    LOG("%d\n", param_size);
    LOG("%d\n", lead_num);
    LOG("%d\n", opt_num);
    LOG("%d\n", post_num);

    LOG("%d\n", has_rest);
    LOG("%d\n", has_kw);
    LOG("%d\n", has_kwrest);
    LOG("%d\n", has_block);

    if (param_size == 0) {
        return 0;
    }

    const char **types = (const char **)malloc(param_size * sizeof(*types));
    size_t i, ans_iterator;
    int types_iterator;

    ans_iterator = 0;

    int new_version_flag = strcmp(RUBY_VERSION, "2.4.0") >= 0 ? 1 : 0;
    LOG("%d\n", new_version_flag);

    for(i = param_size - 1 - new_version_flag, types_iterator = 0; (size_t)types_iterator < param_size; i--, types_iterator++)
    {
        types[types_iterator] = calc_sane_class_name(ep[i - 1]);
        types_ids[types_iterator] = i - 1;
        LOG("Type #%d=%s\n", types_iterator, types[types_iterator])
    }

    types_iterator--;

    if(has_kw) {
        param_size--;
    }

    char **ans = (char **)malloc(param_size * sizeof(*ans));

    for(i = 0; i < lead_num; i++, ans_iterator++, types_iterator--)
    {
        const char* name = rb_id2name(cfp->iseq->body->local_table[ans_iterator]);
        ans[ans_iterator] = fast_join(',', 3, "REQ", types[types_iterator], name);
    }

    for(i = 0; i < opt_num; i++, ans_iterator++, types_iterator--)
    {
        const char* name = rb_id2name(cfp->iseq->body->local_table[ans_iterator]);
        ans[ans_iterator] = fast_join(',', 3, "OPT", types[types_iterator], name);
    }

    for(i = 0; i < has_rest; i++, ans_iterator++, types_iterator--)
    {
        const char* name = rb_id2name(cfp->iseq->body->local_table[ans_iterator]);
        ans[ans_iterator] = fast_join(',', 3, "REST", types[types_iterator], name);
    }

    for(i = 0; i < post_num; i++, ans_iterator++, types_iterator--)
    {
        const char* name = rb_id2name(cfp->iseq->body->local_table[ans_iterator]);
        ans[ans_iterator] = fast_join(',', 3, "POST", types[types_iterator], name);
    }


    if(cfp->iseq->body->param.keyword != NULL)
    {
        const ID *keywords = cfp->iseq->body->param.keyword->table;
        size_t kw_num = cfp->iseq->body->param.keyword->num;
        size_t required_num = cfp->iseq->body->param.keyword->required_num;
        size_t rest_start = cfp->iseq->body->param.keyword->rest_start;

        LOG("%d %d\n", kw_num, required_num)

        for(i = 0; i < required_num; i++, ans_iterator++, types_iterator--)
        {
            ID key = keywords[i];
            ans[ans_iterator] = fast_join(',', 3, "KEYREQ", types[types_iterator], rb_id2name(key));
        }
        for(i = required_num; i < kw_num; i++, types_iterator--)
        {
            ID key = keywords[i];
            const char *name = rb_id2name(key);
            if (explicit_kw_args == NULL || contains(explicit_kw_args, name)) {
                ans[ans_iterator++] = fast_join(',', 3, "KEY", types[types_iterator], name);
            }
        }

        if (param_size - has_block > 1 && has_kwrest && TYPE(ep[types_ids[types_iterator]]) == T_FIXNUM) {
            types_iterator--;
        }

        if (has_kwrest)
        {
            char *buf = malloc(JOIN_KW_NAMES_AND_TYPES_BUF_SIZE * sizeof(*buf));
            buf[0] = '\0';
            join_kw_names_and_types_buf[0] = '\0';
            join_kw_names_and_types_explicit_kw_args = explicit_kw_args;

            // This function call will concatenate info into join_kw_names_and_types_buf
            rb_hash_foreach(ep[types_ids[types_iterator]], join_kw_names_and_types, Qnil);

            // Checking that join_kw_names_and_types_buf isn't possibly containing invalid info.
            // See join_kw_names_and_types documentation to understand why it can be invalid
            size_t len = strlen(join_kw_names_and_types_buf);
            if (len > 0 && len < JOIN_KW_NAMES_AND_TYPES_BUF_SIZE - 1) {
                strncpy(buf, join_kw_names_and_types_buf, JOIN_KW_NAMES_AND_TYPES_BUF_SIZE);
                ans[ans_iterator++] = buf;
            }
            join_kw_names_and_types_explicit_kw_args = NULL;
            types_iterator--;
        }
    }

    for(i = 0; i < has_block; i++, ans_iterator++, types_iterator--)
    {
        const char* name = rb_id2name(cfp->iseq->body->local_table[ans_iterator]);
        ans[ans_iterator] = fast_join(',', 3, "BLOCK", types[types_iterator], name);
    }

    LOG("%d\n", ans_iterator)
    char *answer = fast_join_array(';', ans_iterator, ans);

    for(i = 0; i < ans_iterator; i++) {
        LOG("free2 %d %d =%s= \n", ans[i], strlen(ans[i]), ans[i]);
        free(ans[i]);
    }

    LOG("%d %d %d", ans_iterator, param_size, types_iterator);
    assert(types_iterator <= 0);

    free(types);
    free(ans);

    return answer;
}

static VALUE
get_args_info_rb(VALUE self)
{
    call_info_t info;
    info.call_info_kw_explicit_args = NULL;
    if (is_call_info_needed()) {
        info = get_call_info();
    }

    char *args_info = get_args_info(info.call_info_kw_explicit_args);
    call_info_t_free(info);
    VALUE ret = args_info ? rb_str_new_cstr(args_info) : Qnil;
    free(args_info);
    return ret;
}

static VALUE
get_call_info_rb(VALUE self)
{
    if (is_call_info_needed())
    {
        call_info_t info = get_call_info();

        VALUE ans;
        ans = rb_ary_new();
        rb_ary_push(ans, LONG2FIX(info.call_info_explicit_argc));
        if (info.call_info_kw_explicit_args != NULL) {
            const char *const *kwarg = info.call_info_kw_explicit_args;
            int explicit_kw_count = 0;
            while (*kwarg != NULL) {
                ++explicit_kw_count;
                ++kwarg;
            }
            char *answer = fast_join_array(',', explicit_kw_count, info.call_info_kw_explicit_args);
            rb_ary_push(ans, rb_str_new_cstr(answer));
            free(answer);
        }

        call_info_t_free(info);

        return ans;
    }
    else
    {
        return Qnil;
    }
}

static bool
is_call_info_needed()
{
    rb_thread_t *thread;
    rb_control_frame_t *cfp;

    thread = ruby_current_thread;
    cfp = TH_CFP(thread);
    cfp += 2;

    return (cfp->iseq->body->param.flags.has_opt
        || cfp->iseq->body->param.flags.has_kwrest
        || cfp->iseq->body->param.flags.has_rest
        || (cfp->iseq->body->param.keyword != NULL && cfp->iseq->body->param.keyword->required_num == 0));
}

static VALUE 
check_if_arg_scanner_ready(VALUE self) {
    char error_msg[1024];
    if (pipe_file == NULL) {
        snprintf(error_msg, sizeof(error_msg)/sizeof(*error_msg), "Pipe file is not specified");
        return rb_str_new_cstr(error_msg);
    }
    return Qnil;
}

static VALUE
destructor(VALUE self) {
    g_tree_destroy(sent_to_server_tree);
    g_tree_destroy(number_missed_calls_tree);
    fprintf(pipe_file, "%s\n", ARG_SCANNER_EXIT_COMMAND);
    fclose(pipe_file);
    free(project_root);
    return Qnil;
}


================================================
FILE: arg_scanner/ext/arg_scanner/arg_scanner.h
================================================
#ifndef ARG_SCANNER_H
#define ARG_SCANNER_H 1

#include "ruby.h"
#include "vm_core.h"
#include "version.h"
#include "iseq.h"
#include "method.h"

#endif /* ARG_SCANNER_H */


================================================
FILE: arg_scanner/ext/arg_scanner/extconf.rb
================================================
require "mkmf"

RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']

require "debase/ruby_core_source"
require "native-package-installer"

class NilClass
  def empty?; true; end
end

# Just a replacement of have_header because have_header searches not recursively :(
def real_have_header(header_name)
  if (have_header(header_name))
    return true
  end
  yes_msg = "checking for #{header_name}... yes"
  no_msg = "checking for #{header_name}... no"

  include_env = ENV["C_INCLUDE_PATH"]
  if !include_env.empty? && !Dir.glob("#{include_env}/**/#{header_name}").empty?
    puts yes_msg
    return true
  end
  if !Dir.glob("/usr/include/**/#{header_name}").empty?
    puts yes_msg
    return true
  end
  puts no_msg
  return false
end

if !real_have_header('glib.h') &&
    !NativePackageInstaller.install(:alt_linux => "glib2-devel",
                                    :debian => "libglib2.0-dev",
                                    :redhat => "glib2-devel",
                                    :arch_linux => "glib2",
                                    :homebrew => "glib",
                                    :macports => "glib2",
                                    :msys2 => "glib2")
  exit(false)
end

hdrs = proc {
  have_header("vm_core.h") and
  have_header("iseq.h") and
  have_header("version.h") and
      have_header("vm_core.h") and
      have_header("vm_insnhelper.h") and
      have_header("vm_core.h") and
      have_header("method.h")
}

# Allow use customization of compile options. For example, the
# following lines could be put in config_options to to turn off
# optimization:
#   $CFLAGS='-fPIC -fno-strict-aliasing -g3 -ggdb -O2 -fPIC'
config_file = File.join(File.dirname(__FILE__), 'config_options.rb')
load config_file if File.exist?(config_file)

if ENV['debase_debug']
  $CFLAGS+=' -Wall -Werror -g3'
end

$CFLAGS += ' `pkg-config --cflags --libs glib-2.0`'
$DLDFLAGS += ' `pkg-config --cflags --libs glib-2.0`'

dir_config("ruby")
if !Debase::RubyCoreSource.create_makefile_with_core(hdrs, "arg_scanner/arg_scanner")
  STDERR.print("Makefile creation failed\n")
  STDERR.print("*************************************************************\n\n")
  STDERR.print("  NOTE: If your headers were not found, try passing\n")
  STDERR.print("        --with-ruby-include=PATH_TO_HEADERS      \n\n")
  STDERR.print("*************************************************************\n\n")
  exit(1)
end

================================================
FILE: arg_scanner/lib/arg_scanner/options.rb
================================================
require 'ostruct'

module ArgScanner
  OPTIONS = OpenStruct.new(
      :enable_type_tracker => ENV['ARG_SCANNER_ENABLE_TYPE_TRACKER'],
      :enable_state_tracker => ENV['ARG_SCANNER_ENABLE_STATE_TRACKER'],
      :output_directory => ENV['ARG_SCANNER_DIR'],
      :catch_only_every_n_call => ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'] || 1,
      :project_root => ENV['ARG_SCANNER_PROJECT_ROOT'],
      :pipe_file_path => ENV['ARG_SCANNER_PIPE_FILE_PATH'] || '',
      :buffering => ENV['ARG_SCANNER_BUFFERING']
  )

  def OPTIONS.set_env
    ENV['ARG_SCANNER_ENABLE_TYPE_TRACKER'] = self.enable_type_tracker ? "1" : nil
    ENV['ARG_SCANNER_ENABLE_STATE_TRACKER'] = self.enable_state_tracker ? "1" : nil
    ENV['ARG_SCANNER_DIR'] = self.output_directory
    ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'] = self.catch_only_every_n_call.to_s
    ENV['ARG_SCANNER_PROJECT_ROOT'] = self.project_root
    ENV['ARG_SCANNER_PIPE_FILE_PATH'] = self.pipe_file_path
    ENV['ARG_SCANNER_BUFFERING'] = self.buffering ? "1" : nil
  end
end


================================================
FILE: arg_scanner/lib/arg_scanner/require_all.rb
================================================
# Copyright (c) 2009 Jarmo Pertman

# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

module RequireAll
  # A wonderfully simple way to load your code.
  #
  # The easiest way to use require_all is to just point it at a directory
  # containing a bunch of .rb files.  These files can be nested under
  # subdirectories as well:
  #
  #  require_all 'lib'
  #
  # This will find all the .rb files under the lib directory and load them.
  # The proper order to load them in will be determined automatically.
  #
  # If the dependencies between the matched files are unresolvable, it will
  # throw the first unresolvable NameError.
  #
  # You can also give it a glob, which will enumerate all the matching files:
  #
  #  require_all 'lib/**/*.rb'
  #
  # It will also accept an array of files:
  #
  #  require_all Dir.glob("blah/**/*.rb").reject { |f| stupid_file(f) }
  #
  # Or if you want, just list the files directly as arguments:
  #
  #  require_all 'lib/a.rb', 'lib/b.rb', 'lib/c.rb', 'lib/d.rb'
  #
  def require_all(*args)
    # Handle passing an array as an argument
    args.flatten!

    options = {:method => :require}
    options.merge!(args.pop) if args.last.is_a?(Hash)

    if args.empty?
      puts "no files were loaded due to an empty Array" if $DEBUG
      return false
    end

    if args.size > 1
      # Expand files below directories
      files = args.map do |path|
        if File.directory? path
          Dir[File.join(path, '**', '*.rb')]
        else
          path
        end
      end.flatten
    else
      arg = args.first
      begin
        # Try assuming we're doing plain ol' require compat
        stat = File.stat(arg)

        if stat.file?
          files = [arg]
        elsif stat.directory?
          files = Dir.glob File.join(arg, '**', '*.rb')
        else
          raise ArgumentError, "#{arg} isn't a file or directory"
        end
      rescue SystemCallError
        # If the stat failed, maybe we have a glob!
        files = Dir.glob arg

        # Maybe it's an .rb file and the .rb was omitted
        if File.file?(arg + '.rb')
          file = arg + '.rb'
          options[:method] != :autoload ? Kernel.send(options[:method], file) : __autoload(file, file, options)
          return true
        end

        # If we ain't got no files, the glob failed
        raise LoadError, "no such file to load -- #{arg}" if files.empty?
      end
    end

    return if files.empty?

    if options[:method] == :autoload
      files.map! { |file_| [file_, File.expand_path(file_)] }
      files.each do |file_, full_path|
        __autoload(file_, full_path, options)
      end

      return true
    end

    files.map! { |file_| File.expand_path file_ }
    files.sort!

    begin
      failed = []
      first_name_error = nil

      # Attempt to load each file, rescuing which ones raise NameError for
      # undefined constants.  Keep trying to successively reload files that
      # previously caused NameErrors until they've all been loaded or no new
      # files can be loaded, indicating unresolvable dependencies.
      files.each do |file_|
        begin
          Kernel.send(options[:method], file_)
        rescue NameError => ex
          failed << file_
          first_name_error ||= ex
        rescue ArgumentError => ex
          # Work around ActiveSuport freaking out... *sigh*
          #
          # ActiveSupport sometimes throws these exceptions and I really
          # have no idea why.  Code loading will work successfully if these
          # exceptions are swallowed, although I've run into strange
          # nondeterministic behaviors with constants mysteriously vanishing.
          # I've gone spelunking through dependencies.rb looking for what
          # exactly is going on, but all I ended up doing was making my eyes
          # bleed.
          #
          # FIXME: If you can understand ActiveSupport's dependencies.rb
          # better than I do I would *love* to find a better solution
          raise unless ex.message["is not missing constant"]

          STDERR.puts "Warning: require_all swallowed ActiveSupport 'is not missing constant' error"
          STDERR.puts ex.backtrace[0..9]
        end
      end

      # If this pass didn't resolve any NameErrors, we've hit an unresolvable
      # dependency, so raise one of the exceptions we encountered.
      if failed.size == files.size
        raise first_name_error
      else
        files = failed
      end
    end until failed.empty?

    true
  end

  # Works like require_all, but paths are relative to the caller rather than
  # the current working directory
  def require_rel(*paths)
    # Handle passing an array as an argument
    paths.flatten!
    return false if paths.empty?

    source_directory = File.dirname caller.first.sub(/:\d+$/, '')
    paths.each do |path|
      require_all File.join(source_directory, path)
    end
  end

  # Loads all files like require_all instead of requiring
  def load_all(*paths)
    require_all paths, :method => :load
  end

  # Loads all files by using relative paths of the caller rather than
  # the current working directory
  def load_rel(*paths)
    paths.flatten!
    return false if paths.empty?

    source_directory = File.dirname caller.first.sub(/:\d+$/, '')
    paths.each do |path|
      require_all File.join(source_directory, path), :method => :load
    end
  end

  # Performs Kernel#autoload on all of the files rather than requiring immediately.
  #
  # Note that all Ruby files inside of the specified directories should have same module name as
  # the directory itself and file names should reflect the class/module names.
  # For example if there is a my_file.rb in directories dir1/dir2/ then
  # there should be a declaration like this in my_file.rb:
  #   module Dir1
  #     module Dir2
  #       class MyFile
  #         ...
  #       end
  #     end
  #  end
  #
  # If the filename and namespaces won't match then my_file.rb will be loaded into wrong module!
  # Better to fix these files.
  #
  # Set $DEBUG=true to see how files will be autoloaded if experiencing any problems.
  #
  # If trying to perform autoload on some individual file or some inner module, then you'd have
  # to always specify *:base_dir* option to specify where top-level namespace resides.
  # Otherwise it's impossible to know the namespace of the loaded files.
  #
  # For example loading only my_file.rb from dir1/dir2 with autoload_all:
  #
  #   autoload_all File.dirname(__FILE__) + '/dir1/dir2/my_file',
  #                :base_dir => File.dirname(__FILE__) + '/dir1'
  #
  # WARNING: All modules will be created even if files themselves aren't loaded yet, meaning
  # that all the code which depends of the modules being loaded or not will not work, like usages
  # of define? and it's friends.
  #
  # Also, normal caveats of using Kernel#autoload apply - you have to remember that before
  # applying any monkey-patches to code using autoload, you'll have to reference the full constant
  # to load the code before applying your patch!

  def autoload_all(*paths)
    paths.flatten!
    return false if paths.empty?
    require "pathname"

    options = {:method => :autoload}
    options.merge!(paths.pop) if paths.last.is_a?(Hash)

    paths.each do |path|
      require_all path, {:base_dir => path}.merge(options)
    end
  end

  # Performs autoloading relatively from the caller instead of using current working directory
  def autoload_rel(*paths)
    paths.flatten!
    return false if paths.empty?
    require "pathname"

    options = {:method => :autoload}
    options.merge!(paths.pop) if paths.last.is_a?(Hash)

    source_directory = File.dirname caller.first.sub(/:\d+$/, '')
    paths.each do |path|
      file_path = Pathname.new(source_directory).join(path).to_s
      require_all file_path, {:method => :autoload,
                              :base_dir => source_directory}.merge(options)
    end
  end

  private

  def __autoload(file, full_path, options)
    last_module = "Object" # default constant where namespaces are created into
    begin
      base_dir = Pathname.new(options[:base_dir]).realpath
    rescue Errno::ENOENT
      raise LoadError, ":base_dir doesn't exist at #{options[:base_dir]}"
    end
    Pathname.new(file).realpath.descend do |entry|
      # skip until *entry* is same as desired directory
      # or anything inside of it avoiding to create modules
      # from the top-level directories
      next if (entry <=> base_dir) < 0

      # get the module into which a new module is created or
      # autoload performed
      mod = Object.class_eval(last_module)

      without_ext = entry.basename(entry.extname).to_s
      const = without_ext.split("_").map {|word| word.capitalize}.join

      if entry.directory?
        mod.class_eval "module #{const} end"
        last_module += "::#{const}"
      else
        mod.class_eval do
          puts "autoloading #{mod}::#{const} from #{full_path}" if $DEBUG
          autoload const, full_path
        end
      end
    end
  end

end

================================================
FILE: arg_scanner/lib/arg_scanner/starter.rb
================================================
# starter.rb is loaded with "ruby -r" option from bin/arg-scanner
# or by IDEA also with "ruby -r" option

unless ENV["ARG_SCANNER_ENABLE_STATE_TRACKER"].nil?
  require_relative 'state_tracker'
  ArgScanner::StateTracker.new
end

unless ENV["ARG_SCANNER_ENABLE_TYPE_TRACKER"].nil?
  require_relative 'arg_scanner'
  require_relative 'type_tracker'

  # instantiating type tracker will enable calls tracing and sending the data
  ArgScanner::TypeTracker.instance
end





================================================
FILE: arg_scanner/lib/arg_scanner/state_tracker.rb
================================================
require "set"
require_relative "require_all"
require_relative "workspace"


module ArgScanner
  class StateTracker
    def initialize
      @workspace = Workspace.new
      @workspace.on_process_start
      at_exit do
        begin
          require_extra_libs
          @workspace.open_output_json("classes") { |file| print_json(file) }
        ensure
          @workspace.on_process_exit
        end
      end
    end

    private
    def require_extra_libs
      begin
        RequireAll.require_all Rails.root.join('lib')
      rescue Exception => e
      end
      begin
        Rails.application.eager_load!
      rescue Exception => e
      end
    end

    def print_json(file)
      result = {
        :top_level_constants => parse_top_level_constants,
        :modules => modules_to_json,
        :load_path => $:
      }
      require "json"
      file.puts(JSON.dump(result))
    end

    def parse_top_level_constants
      Module.constants.select { |const| Module.const_defined?(const)}.map do |const|
        begin
          value = Module.const_get(const)
          (!value.is_a? Module) ? {
              :name => const,
              :class_name => value.class,
              :extended => get_extra_methods(value)} : nil
        rescue Exception => e
        end
      end.compact
    end

    def get_extra_methods(value)
      value.methods - value.public_methods
    end

    def method_to_json(method)
      ret = {
          :name => method.name,
          :parameters => method.parameters
      }
      unless method.source_location.nil?
        ret[:path] = method.source_location[0]
        ret[:line] = method.source_location[1]
      end
      ret
    rescue Exception => e
      nil
    end

    def module_to_json(mod)
      ret = {
        :name => mod.to_s,
        :type => mod.class.to_s,
        :singleton_class_ancestors => mod.singleton_class.ancestors.map{|it| it.to_s},
        :ancestors => mod.ancestors.map{|it| it.to_s}, # map to_s is needed because for example "Psych" parsed not correctly into JSON format
                                                       # it's parsed as: "{}\n" check it by launching in rails console: "JSON.generate(Psych)"
        :class_methods => mod.methods(false).map {|method| method_to_json(mod.method(method))}.compact,
        :instance_methods => mod.instance_methods(false).map {|method| method_to_json(mod.instance_method(method))}.compact
      }
      ret[:superclass] = mod.superclass if mod.is_a? Class
      ret
    rescue Exception => e
      nil
    end

    def modules_to_json
      ObjectSpace.each_object(Module).map {|mod| module_to_json(mod)}
    end
  end
end


================================================
FILE: arg_scanner/lib/arg_scanner/type_tracker.rb
================================================
require 'set'
require 'socket'
require 'singleton'
require 'thread'

require_relative 'options'

module ArgScanner

  class TypeTrackerPerformanceMonitor
    def initialize
      @enable_debug = ENV["ARG_SCANNER_DEBUG"]
      @call_counter = 0
      @handled_call_counter = 0
      @submitted_call_counter = 0
      @old_handled_call_counter = 0
      @time = Time.now
    end


    def on_call
      @submitted_call_counter += 1
    end

    def on_return
      @call_counter += 1

      if enable_debug && call_counter % 100000 == 0
        $stderr.puts("calls #{call_counter} handled #{handled_call_counter} submitted #{submitted_call_counter}"\
                     "delta  #{handled_call_counter - old_handled_call_counter} time #{Time.now - @time}")
        @old_handled_call_counter = handled_call_counter
        @time = Time.now
      end
    end

    def on_handled_return
      @handled_call_counter += 1
    end

    private

    attr_accessor :submitted_call_counter
    attr_accessor :handled_call_counter
    attr_accessor :old_handled_call_counter
    attr_accessor :call_counter
    attr_accessor :enable_debug

  end


  class TypeTracker
    include Singleton

    def initialize
      ArgScanner.init(ENV['ARG_SCANNER_PIPE_FILE_PATH'], ENV['ARG_SCANNER_BUFFERING'],
                      ENV['ARG_SCANNER_PROJECT_ROOT'], ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'])

      @enable_debug = ENV["ARG_SCANNER_DEBUG"]
      @performance_monitor = if @enable_debug then TypeTrackerPerformanceMonitor.new else nil end

      TracePoint.trace(:call, &ArgScanner.method(:handle_call))

      TracePoint.trace(:return, &ArgScanner.method(:handle_return))

      error_msg = ArgScanner.check_if_arg_scanner_ready()
      if error_msg != nil
        STDERR.puts error_msg
        Process.exit(1)
      end

      ObjectSpace.define_finalizer(self, proc { ArgScanner.destructor() })
    end

    attr_accessor :enable_debug
    attr_accessor :performance_monitor
    attr_accessor :prefix

  end
end


================================================
FILE: arg_scanner/lib/arg_scanner/version.rb
================================================
module ArgScanner
  VERSION = "0.3.3"
end


================================================
FILE: arg_scanner/lib/arg_scanner/workspace.rb
================================================
module ArgScanner
  class Workspace

    def initialize
      @dir = ENV["ARG_SCANNER_DIR"] || "."
      @pid_file = @dir+"/#{Process.pid}.pid"
    end

    def on_process_start
      File.open(@pid_file, "w") {}
    end


    def open_output_json(prefix)
      path = @dir + "/#{prefix}-#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}-#{Process.pid}.json"
      path_tmp_name = path + ".temp"
      File.open(path_tmp_name, "w") { |file| yield file }
      require 'fileutils'
      FileUtils.mv(path_tmp_name, path)
    end

    def on_process_exit
      require 'fileutils'
      FileUtils.rm(@pid_file)
    end

  end
end


================================================
FILE: arg_scanner/lib/arg_scanner.rb
================================================
require "arg_scanner/version"
require "arg_scanner/arg_scanner"
require "arg_scanner/type_tracker"
require "arg_scanner/state_tracker"

module ArgScanner
  # Your code goes here...
end


================================================
FILE: arg_scanner/test/helper.rb
================================================
$LOAD_PATH.unshift(File.dirname(__dir__) + '/../lib')
require "test-unit"
require "arg_scanner"

class TestTypeTracker
  include Singleton

  attr_reader :last_args_info
  attr_reader :last_call_info

  def initialize
    @tp = TracePoint.new(:call, :return) do |tp|
      case tp.event
        when :call
          ArgScanner.handle_call(tp)

          @last_args_info = ArgScanner.get_args_info.split ';'
          @last_call_info = ArgScanner.get_call_info
        when :return
          ArgScanner.handle_return(tp)
      end
    end
  end

  def enable(*args, &b)
    @tp.enable *args, &b
  end

  def signatures
    Thread.current[:signatures] ||= Array.new
  end

end

================================================
FILE: arg_scanner/test/test_args_info.rb
================================================
#!/usr/bin/env ruby
require File.expand_path("helper", File.dirname(__FILE__))
require 'date'

class TestArgsInfoWrapper

  def foo(a); end

  def foo2(a, b = 1); end

  def foo3(**rest); end

  def foo4(kw: :symbol, **rest1); end

  def foo5(kw:, **rest); end

  def foo6(a, *rest, b); end

  def initialize

    # @trace = TracePoint.new(:call) do |tp|
    #   case tp.event
    #     when :call
    #       tp.binding.local_variables.each { |v| p tp.binding.eval v.to_s }
    #       ArgScanner.handle_call(tp.lineno, tp.method_id, tp.path)
    #       @args_info = ArgScanner.get_args_info
    #       p @args_info
    #   end
    # end
  end
end

class TestArgsInfo < Test::Unit::TestCase

  # @!attribute [r] type_tracker
  #   @return [TestTypeTracker]
  attr_reader :type_tracker


  def setup
    @args_info_wrapper = TestArgsInfoWrapper.new
    @type_tracker = TestTypeTracker.instance
  end

  def teardown

  end

  def test_simple_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo3(a: Date.new, kkw: 'hi')
    end

    assert_equal ["KEYREST,Date,a", "KEYREST,String,kkw"], type_tracker.last_args_info
  end

  def test_empty_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo3()
    end

    assert_equal [], type_tracker.last_args_info
  end

  def test_req_and_opt_arg
    type_tracker.enable do
      @args_info_wrapper.foo2(Date.new)
    end

    assert_equal "REQ,Date,a", type_tracker.last_args_info[0]
    assert type_tracker.last_args_info[1] == "OPT,Fixnum,b" || type_tracker.last_args_info[1] == "OPT,Integer,b"
  end

  def test_optkw_and_empty_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo4(kw: Date.new)
    end

    assert_equal ["KEY,Date,kw"], type_tracker.last_args_info
  end

  def test_reqkw_and_empty_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo5(kw: Date.new)
    end

    assert_equal ["KEYREQ,Date,kw"], type_tracker.last_args_info
  end

  def test_reqkw_and_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo5(kw: Date.new, aa: true, bb: '1')
    end

    assert_equal ["KEYREQ,Date,kw", "KEYREST,TrueClass,aa", "KEYREST,String,bb"], type_tracker.last_args_info
  end

  def test_optkw_and_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo4(aa: :symbol, bb: '1')
    end

    assert_equal ["KEYREST,Symbol,aa", "KEYREST,String,bb"], type_tracker.last_args_info
  end

  def test_optkw_passed_and_kwrest
    type_tracker.enable do
      @args_info_wrapper.foo4(kw: 'bla-bla', aa: :symbol, bb: '1')
    end

    assert_equal ["KEY,String,kw", "KEYREST,Symbol,aa", "KEYREST,String,bb"], type_tracker.last_args_info
  end

  def test_rest
    type_tracker.enable do
      @args_info_wrapper.foo6(1, 'hi', Date.new, '1')
    end

    assert type_tracker.last_args_info[0] == "REQ,Fixnum,a" || type_tracker.last_args_info[0] == "REQ,Integer,a"
    assert type_tracker.last_args_info[1] == "REST,Array,rest"
    assert type_tracker.last_args_info[2] == "POST,String,b"
  end

  def test_empty_rest
    type_tracker.enable do
      @args_info_wrapper.foo6(1, '1')
    end

    assert type_tracker.last_args_info[0] == "REQ,Fixnum,a" || type_tracker.last_args_info[0] == "REQ,Integer,a"
    assert type_tracker.last_args_info[1] == "REST,Array,rest"
    assert type_tracker.last_args_info[2] == "POST,String,b"
  end
end


================================================
FILE: arg_scanner/test/test_call_info.rb
================================================
#!/usr/bin/env ruby
require File.expand_path("helper", File.dirname(__FILE__))

class TestCallInfoWrapper

  def sqr(z1 = 10, z2 = 11, z3 = 13, z4 = 14, z5, z6, z7, z8, y: '0', x: "40")

  end

  def sqr2(z0, z1 = 2, z2 = 10, z3 = 2, z4 = 0, y: 1, x: 30, z: '40')

  end

  def foo(a, b, c, *d, e)

  end

  def foo2(*args)

  end

  def foo3(b: 2, c: '3', **args)

  end

  def foo4(b: 2, c:, d: "1", dd: 1, ddd: '111', **args)

  end

  def foo5(b)

  end

end

class TestCallInfo < Test::Unit::TestCase

  # @!attribute [r] type_tracker
  #   @return [TestTypeTracker]
  attr_reader :type_tracker

  def setup
    @call_info_wrapper = TestCallInfoWrapper.new
    @type_tracker = TestTypeTracker.instance
  end

  def teardown

  end

  def test_simple
    type_tracker.enable do
      @call_info_wrapper.sqr2(10, 11)
    end

    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 2
    #assert type_tracker.last_call_info[0] == "sqr2"
    assert_equal 2, type_tracker.last_call_info[0]
  end

  def test_simple_req_arg
    type_tracker.enable do
      @call_info_wrapper.foo5(10)
    end

    assert_nil type_tracker.last_call_info
  end

  def test_simple_kw
    type_tracker.enable do
      @call_info_wrapper.sqr2(10, 11, x: 10, y: 1)
    end

    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 3
    #assert type_tracker.last_call_info[0] == "sqr2"
    assert_equal 4, type_tracker.last_call_info[0]
    assert_equal "x,y", type_tracker.last_call_info[1]
  end

  def test_rest
    type_tracker.enable do
      @call_info_wrapper.foo2(1, 2, 3, 4, 5, 6, 7, 8)
    end

    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 2
    #assert type_tracker.last_call_info[0] == "foo2"
    assert_equal 8, type_tracker.last_call_info[0]
  end

  def test_post_and_rest
    type_tracker.enable do
      @call_info_wrapper.foo(1, 2, 3, 4, 5, 6, 7, 8)
    end

    #coz it is obvious that all the arguments were passed (they are all required)
    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 2
    #assert type_tracker.last_call_info[0] == "foo"
    #assert type_tracker.last_call_info[0] == 8
  end

  def test_kwrest
    type_tracker.enable do
      @call_info_wrapper.foo3(a: 1, b: 2, c: 3, d: 4)
    end

    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 3
    #assert type_tracker.last_call_info[0] == "foo3"
    assert_equal 4, type_tracker.last_call_info[0]
    assert_equal "a,b,c,d", type_tracker.last_call_info[1]
  end

  def test_rest_and_reqkw_args
    type_tracker.enable do
      @call_info_wrapper.foo4(b: "hello", c: 'world', e: 1, f: "not")
    end

    assert_not_nil type_tracker.last_call_info
    #assert type_tracker.last_call_info.size == 3
    #assert type_tracker.last_call_info[0] == "foo4"
    assert_equal 4, type_tracker.last_call_info[0]
    assert_equal "b,c,e,f", type_tracker.last_call_info[1]

  end
end

================================================
FILE: arg_scanner/test/test_state_tracker.rb
================================================
require 'test/unit'
require 'tempfile'
require 'fileutils'
require 'json'

class StateTrackerTest < Test::Unit::TestCase

  class << self
    #Runs only once at start
    def startup
      file = Tempfile.new("StateTracker")
      dirname = file.path
      FileUtils.rm(dirname)
      file.close
      begin
        FileUtils.makedirs(dirname)
        system("echo exit | ARG_SCANNER_DIR=\"#{dirname}\" ARG_SCANNER_ENABLE_STATE_TRACKER=\"1\" irb -r\"#{File.dirname(__dir__)}/lib/arg_scanner/starter.rb\"  2> /dev/null")
        files = Dir["#{dirname}/*.json"]
        @@json = JSON.parse(File.read(files[0]))
      ensure
        FileUtils.rm_rf(dirname)
      end
    end
  end

  def test_has_struct
    assert_not_nil(get_class_with_name("Struct"))
  end

  def test_symbol_is_fine
    symbol = get_class_with_name("Symbol")
    assert_not_nil(symbol)
    assert_equal(symbol["type"], "Class")
    assert_equal(symbol["superclass"], "Object")
    assert_not_nil(symbol["singleton_class_ancestors"].find_index("Kernel"))
    assert_not_nil(symbol["ancestors"].find_index("Comparable"))
    assert_not_nil(get_class_method(symbol, "all_symbols"))
    assert_not_nil(get_instance_method(symbol, "match"))
    parameters = get_instance_method(symbol, "match")['parameters']
    assert_not_nil(parameters)
    assert_equal(parameters[0][0], (RUBY_VERSION < "2.4.0") ?  "req" : "rest")
  end

  def test_loaded_path_is_fine
    assert_not_nil(@@json["load_path"])
    assert_not_nil(@@json["load_path"][0])
  end

  def test_constant_is_fine
    assert_not_nil(@@json["top_level_constants"])
    assert_not_nil(@@json["top_level_constants"][0])
    assert_not_nil(@@json["top_level_constants"][0]["name"])
    assert_not_nil(@@json["top_level_constants"][0]["class_name"])
    assert_not_nil(@@json["top_level_constants"][0]["extended"])
  end

  private

  def get_class_method(symbol, name)
    get_named_entity(symbol, "class_methods", name)
  end

  def get_instance_method(symbol, name)
    get_named_entity(symbol, "instance_methods", name)
  end

  def get_class_with_name(name)
    get_named_entity(@@json, "modules", name)
  end

  def get_named_entity(obj, index, name)
    obj[index].find {|entity| entity["name"] == name}
  end

end


================================================
FILE: arg_scanner/util/state_filter.rb
================================================
#!/usr/bin/env ruby

require 'json'
require 'set'

if ARGV.length < 3
  puts("state_filter.rb <in-file> <out-file> [<list-of-names]")
  exit
end

json = JSON.parse(File.read(ARGV[0]))

modules2names = {}

json["modules"].each {|mod| modules2names[mod["name"]] = mod}

visited = Set.new
queue = Queue.new
ARGV[2..-1].each do |it|
  visited.add(it)
  queue.push(it)
end

until queue.empty? do
  elem = modules2names[queue.pop] || next
  (elem["singleton_class_included"] + elem["included"] + [elem["superclass"]]).each do |mod|
    queue.push(mod) if visited.add?(mod)
  end
end

output_modules = visited.map do |mod|
  modules2names[mod]
end.compact

File.write(ARGV[1], JSON.pretty_generate({:modules => output_modules, :load_path => json["load_path"]}))


================================================
FILE: build.gradle
================================================
buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7"
        classpath 'org.apache.httpcomponents:httpclient:4.5.2'
    }
}

allprojects {
    repositories {
        mavenCentral()
        maven {
            url 'https://dl.bintray.com/kotlin/exposed'
        }
    }

    apply plugin: 'java'
    apply plugin: 'kotlin'

    def project = it
    dependencies {
        if (project.name != 'ide-plugin') {
            compile 'org.jetbrains:annotations:15.0'
            compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
            compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
        }

        testCompile 'junit:junit:4.12'
        testCompile 'com.h2database:h2:1.4.193'
    }

    compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }

    test {
        systemProperties System.properties
        testLogging {
            exceptionFormat = 'full'
        }
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = '4.10.2'
}

subprojects {
    if (it.name in ['storage-server-api', 'lambda-update-handler', 'lambda-put-handler', 'contract-creator', 'state-tracker', 'ide-plugin']) {
        dependencies {
            compile 'com.google.code.gson:gson:2.8.0'
        }
    }
}



================================================
FILE: common/build.gradle
================================================
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

dependencies {
}

sourceSets {
    main.java.srcDirs = ['src/main/java']
    main.kotlin.srcDirs = ['src/main/java']

    test.kotlin.srcDirs = ['src/test/java']
}


================================================
FILE: common/src/main/java/org/jetbrains/ruby/codeInsight/Injector.kt
================================================
package org.jetbrains.ruby.codeInsight

/**
 * Dependency injection mechanism
 */
interface Injector {
    fun <T> getLogger(cl: Class<T>): Logger
}

@Volatile
private var _injector: Injector? = null
val injector: Injector
    get() {
        return _injector ?: throw IllegalStateException("Injector must be initialized before any usage")
    }

// Because the we don't know anything about injector initializators we assume that it can be
// potentially multi threaded but necessity of injector initialization thread safety isn't really investigated
@Synchronized
fun initInjector(injector: Injector) {
    check(_injector == null) {
        "Injector must be initialized only once"
    }
    _injector = injector
}


================================================
FILE: common/src/main/java/org/jetbrains/ruby/codeInsight/Logger.kt
================================================
package org.jetbrains.ruby.codeInsight

interface Logger {
    fun info(msg: String)
}


================================================
FILE: common/src/main/java/org/jetbrains/ruby/codeInsight/PrintToStdoutLogger.kt
================================================
package org.jetbrains.ruby.codeInsight

import java.text.SimpleDateFormat
import java.util.*

private val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

/**
 * Most basic [Logger] implementation
 */
class PrintToStdoutLogger(private val category: String) : Logger {
    constructor(cl : Class<*>) : this(cl.name)

    override fun info(msg: String) {
        println("${format.format(Calendar.getInstance())} [$category] $msg")
    }
}


================================================
FILE: contract-creator/build.gradle
================================================
sourceSets {
    main.java.srcDirs = ['src']
}

dependencies {
    compile project(':common')
    compile project(':ruby-call-signature')
    compile project(':storage-server-api')

//    compile 'com.h2database:h2:1.4.193'
}

task runServer(type: JavaExec) {
    classpath sourceSets.main.runtimeClasspath
    main = 'org.jetbrains.ruby.runtime.signature.server.SignatureServerKt'
}


================================================
FILE: contract-creator/src/org/jetbrains/ruby/runtime/signature/server/SignatureServer.kt
================================================
package org.jetbrains.ruby.runtime.signature.server

import com.google.gson.Gson
import com.google.gson.JsonParseException
import com.google.gson.JsonSyntaxException
import org.jetbrains.ruby.codeInsight.initInjector
import org.jetbrains.ruby.codeInsight.types.signature.CallInfo
import org.jetbrains.ruby.codeInsight.types.storage.server.DatabaseProvider
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.CallInfoTable
import org.jetbrains.ruby.runtime.signature.server.serialisation.ServerResponseBean
import org.jetbrains.ruby.runtime.signature.server.serialisation.toCallInfo
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.nio.file.Paths
import java.util.*
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong
import java.util.logging.Logger
import kotlin.concurrent.thread

private const val EXIT_COMMAND = "EXIT";

fun main(args: Array<String>) {
    initInjector(SignatureServerInjector)
    parseArgs(args).let {
        DatabaseProvider.connectToDB(it.dbFilePath, isDefaultDatabase = true)
    }

    val pipeFileName = SignatureServer().runServerAsync(isDaemon = false)
    println("Pass this to arg-scanner via --pipe-file-path: $pipeFileName")

    // Intercept Ctrl+C
    Runtime.getRuntime().addShutdownHook(thread(start = false) {
        File(pipeFileName).delete()
    })
}

private data class ParsedArgs(val dbFilePath: String)

private fun parseArgs(args: Array<String>): ParsedArgs {
    if (args.size != 1) {
        System.err.println("""
                One argument required: path-to-h2-db-file
                Or if you run it via gradle: ./gradlew contract-creator:runServer --args path-to-db
            """.trimIndent())
        System.exit(1)
    }
    return ParsedArgs(args.single())
}

class SignatureServer {
    companion object {
        private const val LOCAL_STORAGE_SIZE_LIMIT = 128

        @Suppress("ObjectPropertyName")
        private val _runningServers: MutableList<SignatureServer> = Collections.synchronizedList(mutableListOf())
        val runningServers: List<SignatureServer>
            get() = _runningServers
    }
    private val LOGGER = Logger.getLogger("SignatureServer")

    private val callInfoContainer = LinkedList<CallInfo>()

    private val gson = Gson()
    private val queue = ArrayBlockingQueue<String>(10024)
    private val isReady = AtomicBoolean(true)
    private var previousPollEndedWithFlush = false

    val readTime = AtomicLong(0)
    val jsonTime = AtomicLong(0)
    val addTime = AtomicLong(0)

    private val signatureHandler = SignatureHandler()
    private val pollJsonThread = PollJsonThread()

    fun isProcessingRequests() = !isReady.get()

    private fun generateTempFilePath(prefix: String = ""): String {
        val dirForTempFiles = System.getProperty("java.io.tmpdir")
        return Paths.get(dirForTempFiles, prefix + UUID.randomUUID()).toString()
    }

    /**
     * @return pipe filename path which should be passed to arg-scanner
     */
    fun runServerAsync(isDaemon: Boolean): String {
        isReady.set(false)
        _runningServers.add(this)
        LOGGER.info("Starting server")

        val pipeFileName = generateTempFilePath(prefix = "ruby-type-inference-pipe-")
        val proc: Process = Runtime.getRuntime().exec("mkfifo $pipeFileName")
        if (proc.waitFor() != 0) {
            throw RuntimeException("Cannot create pipe file")
        }

        signatureHandler.pipeFilePath = pipeFileName
        signatureHandler.isDaemon = isDaemon
        signatureHandler.start()

        pollJsonThread.isDaemon = isDaemon
        pollJsonThread.start()
        return pipeFileName
    }

    var afterFlushListener: (() -> Unit)? = null

    var afterExitListener: (() -> Unit)? = null

    /**
     * @return true when client won't send data anymore
     */
    private fun pollJson(): Boolean {
        val jsonString by lazy { if (previousPollEndedWithFlush) queue.take() else queue.poll() }
        if (callInfoContainer.size > LOCAL_STORAGE_SIZE_LIMIT || jsonString == null || jsonString == EXIT_COMMAND) {
            flushNewTuplesToMainStorage()
            previousPollEndedWithFlush = true
            return jsonString == EXIT_COMMAND
        }
        previousPollEndedWithFlush = false

        parseJson(jsonString)
        return false
    }

    private fun parseJson(jsonString: String) {
        val currCallInfo = ben(jsonTime) {
            try {
                return@ben gson.fromJson(jsonString, ServerResponseBean::class.java)?.toCallInfo()
            } catch (ex: Throwable) {
                when (ex) {
                    is JsonSyntaxException, is JsonParseException -> {
                        // Sometimes it's possible that some json fields contain quotation mark and we got JsonSyntaxException
                        LOGGER.severe("Cannot parse: $jsonString")
                    }
                    is IllegalStateException -> {
                        LOGGER.severe(ex.message)
                    }
                    else -> throw ex
                }
                return@ben null
            }
        }

        // filter, for example, such things #<Class:DidYouMean::Jaro>
        if (currCallInfo?.methodInfo?.classInfo?.classFQN?.startsWith("#<") == true) {
            return
        }

        if (currCallInfo != null) {
            ben(addTime) { callInfoContainer.add(currCallInfo) }
        }
    }

    private fun flushNewTuplesToMainStorage() {
        DatabaseProvider.defaultDatabaseTransaction {
            for (callInfo in callInfoContainer) {
                CallInfoTable.insertInfoIfNotContains(callInfo)
            }
        }
        callInfoContainer.clear()
        afterFlushListener?.invoke()
    }

    private inner class SignatureHandler internal constructor() : Thread() {
        var pipeFilePath: String = ""

        override fun run() {
            try {
                var missed = 0
                var br = FileInputStream(pipeFilePath).bufferedReader()
                var currString: String? = ""
                do {
                    // continue when EOF is reached because EOF doesn't mean that program
                    // traced by arg-scanner is died. Program could simply call `Kernel.exec`
                    // See CallStatCompletionTest.testRubyExecWithBuffering and
                    // CallStatCompletionTest.testRubyExecWithoutBuffering
                    currString = ben(readTime) { br.readLine() }

                    if (currString != null) {
                        queue.put(currString)
                    } else {
                        missed++
                        br.close()
                        // If don't reassign reader then `readLine` will always return `null`
                        br = FileInputStream(pipeFilePath).bufferedReader()
                    }

                    // 1000 is just threshold for safety
                } while (currString != EXIT_COMMAND && missed < 1000)
            } catch (e: IOException) {
                LOGGER.severe("Error in SignatureHandler")
            } finally {
                File(pipeFilePath).delete()
            }
        }
    }

    private inner class PollJsonThread : Thread() {
        override fun run() {
            while (true) {
                if (pollJson()) {
                    isReady.set(true)
                    afterExitListener?.invoke()
                    _runningServers.remove(this@SignatureServer)
                    break
                }
            }
        }
    }
}

fun <T> ben(x: AtomicLong, F: ()->T): T {
    val start = System.nanoTime()
    try {
        return F.invoke()
    }
    finally {
        x.addAndGet(System.nanoTime() - start)
    }
}

================================================
FILE: contract-creator/src/org/jetbrains/ruby/runtime/signature/server/SignatureServerInjector.kt
================================================
package org.jetbrains.ruby.runtime.signature.server

import org.jetbrains.ruby.codeInsight.Injector
import org.jetbrains.ruby.codeInsight.Logger
import org.jetbrains.ruby.codeInsight.PrintToStdoutLogger

object SignatureServerInjector : Injector {
    override fun <T> getLogger(cl: Class<T>): Logger {
        return PrintToStdoutLogger(cl)
    }
}


================================================
FILE: contract-creator/src/org/jetbrains/ruby/runtime/signature/server/serialisation/ServerResponseBean.kt
================================================
package org.jetbrains.ruby.runtime.signature.server.serialisation

import org.jetbrains.ruby.codeInsight.types.signature.*

data class ServerResponseBean(
        val method_name: String,
        /**
         * Number of unnamedArguments passed by user explicitly
         *
         * For example for method:
         * def foo(a, b = 1); end
         *
         * This method invocation have only one explicit argument
         * foo(4)
         *
         * But this method invocation have two explicit unnamedArguments
         * foo(4, 5)
         */
        val call_info_argc: Int,
        val args_info: String,
        val visibility: String,
        val path: String,
        val lineno: Int,
        val receiver_name: String,
        val return_type_name: String)

// explicit here means that this unnamedArguments was explicitly provided by user
// for example:
// def foo(a, b = 1); end
// foo(1)    # here only `a` is explicitly provided
// foo(1, 5) # here `a` and `b` are both explicitly provided
private data class Arg(val paramInfo: ParameterInfo, val type: String, var explicit: Boolean)

private const val PARAMETER_MODIFIER_INDEX_IN_ATTRIBUTES = 0
private const val PARAMETER_TYPE_INDEX_IN_ATTRIBUTES = 1
private const val PARAMETER_NAME_INDEX_IN_ATTRIBUTES = 2
private const val NUMBER_OF_ATTRIBUTES_FOR_PARAMETER = 3

/**
 * @throws IllegalStateException if [ServerResponseBean] is not correctly formed
 */
fun ServerResponseBean.toCallInfo(): CallInfo {
    var argc = this.call_info_argc

    val args = this.args_info.takeIf { it != "" }?.split(";")?.map {
        val parts: List<String> = it.split(",")
        val modifier = parts[PARAMETER_MODIFIER_INDEX_IN_ATTRIBUTES]
        val type = parts[PARAMETER_TYPE_INDEX_IN_ATTRIBUTES]

        val name = if (parts.size == NUMBER_OF_ATTRIBUTES_FOR_PARAMETER) {
            // It's possible that parameter in ruby doesn't have name, for example:
            // def foo(*); end
            parts[PARAMETER_NAME_INDEX_IN_ATTRIBUTES]
        } else {
            ""
        }

        // If argc == -1 then all args are explicitly passed
        return@map Arg(ParameterInfo(name, ParameterInfo.Type.valueOf(modifier)), type, explicit = argc == -1)
    } ?: emptyList()

    if (argc != -1) {
        for (arg in args) {
            if (arg.paramInfo.isNamedParameter ||
                    arg.paramInfo.modifier == ParameterInfo.Type.REQ ||
                    arg.paramInfo.modifier == ParameterInfo.Type.POST) {
                arg.explicit = true
                argc--
            }
        }

        for (arg in args) {
            if (argc <= 0) {
                break
            }
            if (arg.paramInfo.modifier == ParameterInfo.Type.OPT) {
                arg.explicit = true
                argc--
            }
        }

        for (arg in args) {
            if (argc <= 0) {
                break
            }
            if (arg.paramInfo.modifier == ParameterInfo.Type.REST) {
                arg.explicit = true
                argc--
            }
        }

        check(argc == 0 || args.any { it.paramInfo.modifier == ParameterInfo.Type.BLOCK } && argc == 1) {
            "Failed to parse this bean: ${this.toString()}"
        }
    }

    val namedArgumentsNamesToTypes = args.asSequence().filter { it.paramInfo.isNamedParameter }
            .map { ArgumentNameAndType(it.paramInfo.name, it.type) }.toList()

    val unnamedArgumentsTypes = args.asSequence().filter { !it.paramInfo.isNamedParameter }
            .map { arg ->
                ArgumentNameAndType(arg.paramInfo.name, arg.type.takeIf { arg.explicit }
                        ?: ArgumentNameAndType.IMPLICITLY_PASSED_ARGUMENT_TYPE)
            }.toList()

    val methodInfo = MethodInfo.Impl(
            ClassInfo.Impl(gemInfoFromFilePathOrNull(this.path), this.receiver_name),
            this.method_name,
            RVisibility.valueOf(this.visibility),
            Location(this.path, this.lineno))

    return CallInfoImpl(methodInfo, namedArgumentsNamesToTypes, unnamedArgumentsTypes, this.return_type_name)
}


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Wed Nov 07 19:25:40 MSK 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip


================================================
FILE: gradle.properties
================================================
# Available idea versions:
# https://www.jetbrains.com/intellij-repository/releases
# https://www.jetbrains.com/intellij-repository/snapshots

# ruby plugin versions can be found here:
# https://plugins.jetbrains.com/plugin/1293-ruby/versions

kotlin_version=1.2.70
ideaVersion=IU-193.5233.102
rubyPluginVersion=193.5233.57
exposedVersion=0.17.3


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=$(save "$@")

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: ide-plugin/CHANGELOG.md
================================================
## 0.1.1 (15 Dec 2017)

* (#17) Fix "find usages" action for dynamic symbols which resolve to text-based
  definitions.

## 0.1 (29 Nov 2017)

Initial plugin version

* Collect State action

  Adds on_exit hook which dumps class/module includes structure and contained methods
  which can be used for resolution/completion later. 

* Collect Type action

  Enables call tracing (with a considerable slowdown) and dumps return types which
  can be used for better type inference.

* Symbol/Type provider to improve resolution and type inference based on the collected
  data.

================================================
FILE: ide-plugin/build.gradle
================================================
buildscript {
    repositories {
        maven { url 'https://dl.bintray.com/jetbrains/intellij-plugin-service' }
    }
}

plugins {
    id "org.jetbrains.intellij" version "0.3.11"
}

dependencies {
    def withoutKotlinAndMySql = {
        exclude group: 'org.jetbrains.kotlin'
        exclude group: 'mysql'
    }
    def withoutSlfAndKotlinAndMySql = {
        exclude group: 'org.slf4j'//, module: 'slf4j-api'
        exclude group: 'org.jetbrains.kotlin'
        exclude group: 'mysql'
    }

    compile project(':common')
    compile project(':ruby-call-signature'), withoutKotlinAndMySql
    compile project(':storage-server-api'), withoutSlfAndKotlinAndMySql
    compile project(':contract-creator'), withoutSlfAndKotlinAndMySql
    compile project(':state-tracker'), withoutSlfAndKotlinAndMySql

    // https://mvnrepository.com/artifact/com.h2database/h2
    compile group: 'com.h2database', name: 'h2', version: '1.4.199'

}

sourceSets {
    main.java.srcDirs = ['src']
    main.resources.srcDirs = ['resources']
    test.java.srcDirs = ['src/test/java']
    test.resources.srcDirs=['src/test/testData']
}

intellij {
    version ideaVersion
    pluginName 'ruby-runtime-stats'
    plugins = ["yaml", "org.jetbrains.plugins.ruby:$rubyPluginVersion"]
}

patchPluginXml {
    sinceBuild '193.5233.102'
    untilBuild '193.*'
    version '0.3.3'
}

prepareSandbox.doLast {
    def destDir = "$it.destinationDir/$intellij.pluginName"
    copy {
        from sourceSets.main.resources.include("**/*.rb", "**/*.db")
        into destDir
    }
}

test {
    testLogging.showStandardStreams = true
}



================================================
FILE: ide-plugin/resources/META-INF/plugin.xml
================================================
<idea-plugin>
    <id>org.jetbrains.ruby-runtime-stats</id>
    <name>Ruby Dynamic Code Insight</name>
    <vendor email="" url="http://www.jetbrains.com">JetBrains</vendor>
    <depends>com.intellij.modules.ruby</depends>
    <description><![CDATA[
        <p>This plugin provides additional Code Insight intelligence to improve resolution, find usages and refactoring
        capabilities.</p>

        <p>The data is obtained via user project execution altered by a special tracker which stores symbol
        hierarchy, method return types, etc.</p>
    ]]></description>

    <change-notes><![CDATA[
        <a href="https://github.com/JetBrains/ruby-type-inference/blob/master/ide-plugin/CHANGELOG.md">Changelog</a>
    ]]>
    </change-notes>

    <applicationListeners>
        <listener class="org.jetbrains.plugins.ruby.ruby.codeInsight.ProjectLifecycleListenerImpl"
                  topic="com.intellij.openapi.project.ProjectManagerListener"/>
        <listener class="org.jetbrains.plugins.ruby.ruby.codeInsight.RubyDynamicCodeInsightPluginAppLifecyctlListener"
                  topic="com.intellij.ide.AppLifecycleListener"
                  activeInHeadlessMode="true"/>
    </applicationListeners>

    <extensions defaultExtensionNs="com.intellij">
        <executor implementation="com.intellij.execution.executors.RunWithTypeTrackerExecutor"/>
        <!--<executor implementation="com.intellij.execution.executors.CollectStateExecutor"/>-->

        <programRunner implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.types.RubyRunWithTypeTrackerRunner"/>
        <programRunner implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.types.RubyCollectStateRunner"/>

        <projectService serviceImplementation="org.jetbrains.plugins.ruby.settings.RubyTypeContractsSettings"/>

        <intentionAction>
            <className>org.jetbrains.plugins.ruby.ruby.intentions.AddContractAnnotationIntention</className>
        </intentionAction>

        <intentionAction>
            <className>org.jetbrains.plugins.ruby.ruby.intentions.RemoveCollectedInfoIntention</className>
        </intentionAction>

        <postStartupActivity implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.TrackerDataLoader"/>

        <applicationConfigurable groupId="language"
                                 groupWeight="130"
                                 instance="org.jetbrains.plugins.ruby.settings.RubyTypeContractsConfigurable"/>

    </extensions>

    <extensions defaultExtensionNs="org.jetbrains.plugins.ruby">

        <rubyTypeProvider implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.types.RubyParameterTypeProvider"/>
        <symbolicTypeInferenceProvider implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.types.ReturnTypeSymbolicTypeInferenceProvider"/>

        <symbolProvider implementation="org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker.ClassHierarchySymbolProvider"/>

        <runConfigurationExtension
                implementation="org.jetbrains.plugins.ruby.ruby.run.configuration.RunWithTypeTrackerRunConfigurationExtension"/>
    </extensions>

    <actions>
        <group id="ruby.contracts.group"
               text="Type Contracts"
               popup="true">
            <add-to-group group-id="RUBY_TOOLS" anchor="before" relative-to-action="BUNDLER_ACTIONS"/>
            <action class="org.jetbrains.plugins.ruby.ruby.actions.ExportContractsAction"
                    id="ruby.contracts.export"
                    text="Export type contracts"/>
            <action class="org.jetbrains.plugins.ruby.ruby.actions.ImportContractsAction"
                    id="ruby.contracts.import"
                    text="Import type contracts"/>
        </group>
        <group id="ruby.ancestors_extractor.group"
               text="Export ancestors (for rails applications only!)"
               popup="true" internal="true">
            <add-to-group group-id="RUBY_TOOLS" anchor="before" relative-to-action="BUNDLER_ACTIONS"/>
            <action class="org.jetbrains.plugins.ruby.ruby.actions.ExportAncestorsByObjectSpaceAction"
                    id="ruby.ancestors_extractor.export_by_objectspace"
                    text="Export ancestors by Ruby's objectspace"/>
            <action class="org.jetbrains.plugins.ruby.ruby.actions.ExportAncestorsByRubymineAction"
                    id="ruby.ancestors_extractor.export_by_rubymine"
                    text="Export ancestors by Rubymine"/>
            <action class="org.jetbrains.plugins.ruby.ruby.actions.ExportAncestorsDiffAction"
                    id="ruby.ancestors_extractor.diff"
                    text="Export ancestors diff"/>
        </group>
    </actions>

</idea-plugin>

================================================
FILE: ide-plugin/src/com/intellij/execution/executors/CollectStateExecutor.kt
================================================
package com.intellij.execution.executors

import com.intellij.execution.Executor
import com.intellij.icons.AllIcons
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.wm.ToolWindowId
import javax.swing.Icon

class CollectStateExecutor : Executor() {

    private val myIcon = AllIcons.General.GearPlain

    override fun getToolWindowId(): String {
        return ToolWindowId.RUN
    }

    override fun getToolWindowIcon(): Icon {
        return myIcon
    }

    override fun getIcon(): Icon {
        return myIcon
    }

    override fun getDisabledIcon(): Icon? {
        return null
    }

    override fun getDescription(): String {
        return "Run selected configuration with collecting state"
    }

    override fun getActionName(): String {
        return "CollectState"
    }

    override fun getId(): String {
        return EXECUTOR_ID
    }

    override fun getStartActionText(): String {
        return "Run with Collecting State"
    }

    override fun getContextActionId(): String {
        return "RunCollectState"
    }

    override fun getHelpId(): String? {
        return null
    }

    override fun getStartActionText(configurationName: String): String {
        val name = escapeMnemonicsInConfigurationName(
                StringUtil.first(configurationName, 30, true))
        return "Run" + (if (StringUtil.isEmpty(name)) "" else " '$name'") + " with Collecting State"
    }

    private fun escapeMnemonicsInConfigurationName(configurationName: String): String {
        return configurationName.replace("_", "__")
    }

    companion object {
        val EXECUTOR_ID = "CollectState"
    }
}

================================================
FILE: ide-plugin/src/com/intellij/execution/executors/RunWithTypeTrackerExecutor.java
================================================
package com.intellij.execution.executors;

import com.intellij.execution.Executor;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.net.URL;

public class RunWithTypeTrackerExecutor extends Executor {
    @NotNull
    public static final String EXECUTOR_ID = "RunWithTypeTracker";

    @NotNull
    private final Icon myIcon;

    public RunWithTypeTrackerExecutor() {
        final URL iconURL = RunWithTypeTrackerExecutor.class.getClassLoader().getResource(
                UIUtil.isUnderDarcula() ? "runWithTypeTracker_dark.svg" : "runWithTypeTracker.svg");
        final Icon icon = IconLoader.findIcon(iconURL);
        myIcon = icon != null ? icon : AllIcons.General.Error;
    }

    @Override
    public String getToolWindowId() {
        return ToolWindowId.RUN;
    }

    @Override
    public Icon getToolWindowIcon() {
        return myIcon;
    }

    @NotNull
    @Override
    public Icon getIcon() {
        return myIcon;
    }

    @Nullable
    @Override
    public Icon getDisabledIcon() {
        return null;
    }

    @NotNull
    @Override
    public String getDescription() {
        return "Run selected configuration with Type Tracker";
    }

    @NotNull
    @Override
    public String getActionName() {
        return "Run with Type Tracker";
    }

    @NotNull
    @Override
    public String getId() {
        return EXECUTOR_ID;
    }

    @NotNull
    public String getStartActionText() {
        return "Run with Type Tracker";
    }

    @NotNull
    @Override
    public String getContextActionId() {
        return "Run with Type Tracker";
    }

    @Nullable
    @Override
    public String getHelpId() {
        return null;
    }

    @NotNull
    @Override
    public String getStartActionText(@NotNull final String configurationName) {
        final String name = escapeMnemonicsInConfigurationName(
                StringUtil.first(configurationName, 30, true));
        return "Run" + (StringUtil.isEmpty(name) ? "" :  " '" + name + "'") + " with Type Tracker";
    }

    @NotNull
    private static String escapeMnemonicsInConfigurationName(@NotNull final String configurationName) {
        return configurationName.replace("_", "__");
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/IdePluginLogger.kt
================================================
package org.jetbrains.plugins.ruby

import org.jetbrains.ruby.codeInsight.Logger

class IdePluginLogger(private val intellijPlatformLogger: com.intellij.openapi.diagnostic.Logger) : Logger {
    override fun info(msg: String) {
        intellijPlatformLogger.info(msg)
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/PluginResourceUtil.java
================================================
package org.jetbrains.plugins.ruby;

import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.extensions.PluginId;
import org.jetbrains.annotations.NotNull;

import java.io.File;

public final class PluginResourceUtil {
    private static final String PLUGIN_ID = "org.jetbrains.ruby-runtime-stats";

    private PluginResourceUtil() {
    }

    @NotNull
    public static String getPluginResourcesPath() {
        final IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId(PLUGIN_ID));
        if (plugin == null) {
            throw new AssertionError("Nonsense: this plugin is not registered");
        }
        final File pluginHome = plugin.getPath();
        if (pluginHome == null) {
            throw new AssertionError("Corrupted plugin: could not find home");
        }
        return pluginHome.getPath() + "/";
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/RubyDynamicCodeInsightPluginInjector.kt
================================================
package org.jetbrains.plugins.ruby

import org.jetbrains.ruby.codeInsight.Injector
import org.jetbrains.ruby.codeInsight.Logger

object RubyDynamicCodeInsightPluginInjector : Injector {
    override fun <T> getLogger(cl: Class<T>): Logger {
        return IdePluginLogger(com.intellij.openapi.diagnostic.Logger.getInstance(cl))
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ancestorsextractor/AncestorsExtractor.kt
================================================
package org.jetbrains.plugins.ruby.ancestorsextractor

import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.ThrowableComputable
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.SymbolUtil
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.util.SymbolHierarchy
import java.io.FileWriter
import java.io.PrintWriter

/**
 * Keeps Ruby module [name] and it's [ancestors]
 */
data class RubyModule(val name: String, val ancestors: List<String>)

/**
 * Ancestors extractor for Ruby's project's modules
 */
interface AncestorsExtractorBase {
    /**
     * Extract ancestors for every Ruby's Module in [project]
     */
    fun extractAncestors(project: Project, sdk: Sdk): List<RubyModule>

    /**
     * Set [RailsConsoleRunner.Listener] for [RailsConsoleRunner]
     * @see RailsConsoleRunner.Listener
     */
    var listener: RailsConsoleRunner.Listener?
}

/**
 * Extract ancestors for Ruby's modules the way how RubyMine sees them.
 * If you don't provide [allModulesNames] this implementation works only for Ruby on Rails project as in case when
 * you don't provide [allModulesNames] all modules names would be taken from Ruby on Rails console ("bin/rails console")
 */
class AncestorsExtractorByRubyMine(private val allModulesNames: List<String>? = null,
                                   override var listener: RailsConsoleRunner.Listener? = null) : AncestorsExtractorBase {
    /**
     * Implementation of [AncestorsExtractorBase.extractAncestors] based on how RubyMine sees ancestors
     */
    override fun extractAncestors(project: Project, sdk: Sdk): List<RubyModule> {
        val allModulesNamesLocal: List<String> = allModulesNames ?: extractAllModulesNames(project, sdk)
        // I don't know why but seems that SymbolScopeUtil#getAncestorsCaching needs to be called
        // inside ReadAction otherwise sometimes I get Exception
        return ReadAction.compute(ThrowableComputable<List<RubyModule>, Exception> {
            allModulesNamesLocal.map { RubyModule(it, extractAncestorsFor(it, project)) }
        })
    }

    /**
     * Extract all modules names from Ruby on Rails project.
     */
    private fun extractAllModulesNames(project: Project, sdk: Sdk): List<String> {
        val tempFile = createTempFile(prefix = "modules", suffix = ".json")
        try {
            val rubyCode = """
                require 'json'
                open("${tempFile.path}", "w") do |f|
                  f.puts JSON.generate(ObjectSpace.each_object(Module).to_a.map {|from| from.to_s})
                end
            """.trimIndent()
            return RailsConsoleRunner(listener).extractFromRubyOnRailsConsole(project, sdk, Array<String>::class.java,
                    tempFile.path, rubyCode, eagerLoad = true).toList()
        } finally {
            tempFile.delete()
        }
    }

    private fun extractAncestorsFor(rubyModuleName: String, project: Project): List<String> {
        return SymbolUtil.findConstantByFQN(project, rubyModuleName)?.let {
            SymbolHierarchy.getAncestorsCaching(it, null)
        }?.map { it.symbol.fqnWithNesting.toString() } ?: listOf()
    }
}

/**
 * Extract ancestors the way Ruby's Module.ancestors method does this.
 * This implementation works only for Ruby on Rails project.
 * @see <a href="https://ruby-doc.org/core-2.1.0/Module.html#method-i-ancestors">Ruby's Module.ancestors</a>
 */
class AncestorsExtractorByObjectSpace(override var listener: RailsConsoleRunner.Listener? = null) : AncestorsExtractorBase {
    /**
     * Implementation of [AncestorsExtractorBase.extractAncestors] based on Ruby's Module.ancestors method
     * @see <a href="https://ruby-doc.org/core-2.1.0/Module.html#method-i-ancestors">Ruby's Module.ancestors</a>
     */
    override fun extractAncestors(project: Project, sdk: Sdk): List<RubyModule> {
        val tempFile = createTempFile(prefix = "module-ancestors-pair", suffix = ".json")
        try {
            val rubyCode = """
                objects = ObjectSpace.each_object(Module).to_a; nil # nil is to prevent irb to print big objects output
                objects = objects.map {|mod| {:name => mod.to_s, :ancestors => mod.ancestors.map {|from| from.to_s}}}; nil
                require 'json'
                open("${tempFile.path}", "w") do |f|
                  f.puts JSON.generate(objects)
                end
            """.trimIndent()
            return RailsConsoleRunner(listener).extractFromRubyOnRailsConsole(project, sdk, Array<RubyModule>::class.java,
                    tempFile.path, rubyCode, eagerLoad = true).toList()
        } finally {
            tempFile.delete()
        }
    }

    /**
     * Extract information about where ruby includes was performed
     * @return Map where key is [String] with the following format: "**ancestor**#**includer**" and value is [String]
     * containing ruby file path and line number where **ancestor** was included by **includer**
     */
    fun extractIncludes(project: Project, sdk: Sdk): Map<String, String> {
        val tempWhereIncluded = createTempFile(prefix = "where-included", suffix = ".json")
        val tempRubyCodeFile = createTempFile(prefix = "preload-temp-script", suffix = ".rb")
        try {
            PrintWriter(FileWriter(tempRubyCodeFile)).use {
                it.println("""
                    END {
                        require 'json'
                        open("${tempWhereIncluded.path}", "w") { |f| f.puts JSON.generate(RubyDetectIncludeUniqueModuleName.get) }
                    }

                    module RubyDetectIncludeUniqueModuleName
                        @@map = {}
                        def self.get
                            return @@map
                        end

                        def append_features(mod)
                            # self included by mod
                            @@map["#{self.to_s}##{mod.to_s}"] = caller_locations()[1].to_s
                            super
                        end
                    end

                    Module.prepend RubyDetectIncludeUniqueModuleName
                """.trimIndent())
            }
            @Suppress("UNCHECKED_CAST")
            return RailsConsoleRunner(listener).extractFromRubyOnRailsConsole(project, sdk, Map::class.java,
                    tempWhereIncluded.path, rubyCode = "", eagerLoad = true,
                    rubyConsoleArguments = arrayOf("-r", tempRubyCodeFile.path)) as Map<String, String>
        } finally {
            tempRubyCodeFile.delete()
            tempWhereIncluded.delete()
        }
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ancestorsextractor/RailsConsoleRunner.kt
================================================
package org.jetbrains.plugins.ruby.ancestorsextractor

import com.google.gson.Gson
import com.intellij.execution.ExecutionException
import com.intellij.execution.ExecutionModes
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessListener
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.ThrowableComputable
import org.jetbrains.plugins.ruby.ancestorsextractor.RailsConsoleRunner.Listener
import org.jetbrains.plugins.ruby.rails.Rails3Constants
import org.jetbrains.plugins.ruby.rails.Rails4Constants
import org.jetbrains.plugins.ruby.ruby.RubyUtil
import org.jetbrains.plugins.ruby.ruby.run.context.RubyScriptExecutionContext
import java.io.File
import java.io.IOException
import java.io.PrintWriter
import java.nio.file.Paths

/**
 * Runs some Ruby code on Ruby on Rails console ("bin/rails console")
 */
class RailsConsoleRunner(
        /**
         * Set [Listener]. There is only one possible [Listener]. Feel free to change it to
         * addListener to have multiple listeners if you want
         */
        private var listener: RailsConsoleRunner.Listener?) {

    data class RailsConsoleExecutionResult(val stdout: String, val stderr: String)

    /**
     * Extract information left by [rubyCode] in [tempJSONFilPath]
     * @param clazz which kind of information [rubyCode] left in [tempJSONFilPath].
     * Note: Do not use [List] here because [Gson] won't parse it, use [Array] instead
     * @param tempJSONFilPath path to temp file where [rubyCode] left information which
     * can be converted from JSON to [clazz]
     * @param rubyCode Your ruby code which should leave some JSON information in [tempJSONFilPath]
     * @param eagerLoad Works like you set `eager_load` variable inside config/environments/LOADED_ENVIRONMENT.rb
     * @param rubyConsoleArguments additional arguments to pass to ruby interpreter
     * @throws ExecutionException when error occurred either while executing [rubyCode] either
     * while trying to read data from JSON left by Ruby
     * @throws IllegalStateException when getter [Project.getBasePath] of [project] returns `null`
     */
    @Throws(ExecutionException::class, IllegalStateException::class)
    fun <T> extractFromRubyOnRailsConsole(project: Project, sdk: Sdk, clazz: Class<T>, tempJSONFilPath: String,
                                          rubyCode: String, eagerLoad: Boolean,
                                          rubyConsoleArguments: Array<String> = arrayOf()): T {
        val projectDirPath = project.basePath ?: throw IllegalStateException("Seems that project is default. " +
                "Quote from com.intellij.openapi.project.Project#getBasePath JavaDoc")

        val rubyCodeToExec = if (eagerLoad) {
            """
                Rails.application.eager_load!; nil # nil is to prevent irb to print big output

            """.trimIndent() + rubyCode
        } else {
            rubyCode
        }

        val railsConsoleExecutionResult = runRailsConsole(projectDirPath, sdk,
                rubyCodeToExec, rubyConsoleArguments, railsConsoleArguments = arrayOf("--environment=development"))

        val ret = ReadAction.compute(ThrowableComputable<T?, Exception> {
            val file = File(tempJSONFilPath)
            return@ThrowableComputable try {
                file.inputStream().bufferedReader().use {
                    Gson().fromJson(it.readLine(), clazz)
                }
            } catch (ex: IOException) {
                null
            }
        })

        listener?.informationWasExtractedFromIRB()

        return ret ?: throw ExecutionException("""
            |Error occurred either in the following Ruby code (ruby was launched with these arguments: ${rubyConsoleArguments.contentToString()}):
            |$rubyCodeToExec

            |stdout of this Ruby code execution:
            |${railsConsoleExecutionResult.stdout}

            |stderr of this Ruby code execution:
            |${railsConsoleExecutionResult.stderr}

            |either while trying to read data from JSON left by Ruby
        """.trimMargin())
    }

    /**
     * Run [toExec] in ruby on rails console ("bin/rails console"). You can use it for example for generating
     * some temp json files to later parse them in Kotlin/Java
     * @param projectDirPath Path to project dir
     * @param toExec Newline separated [String] to execute in ruby on rails console
     * @param rubyConsoleArguments additional arguments to pass to ruby interpreter
     * @param railsConsoleArguments additional arguments to pass to "bin/rails console"
     * @throws ExecutionException when error occurred while launching rails console
     */
    @Throws(ExecutionException::class)
    fun runRailsConsole(projectDirPath: String, sdk: Sdk, toExec: String,
                        rubyConsoleArguments: Array<String> = arrayOf(),
                        railsConsoleArguments: Array<String> = arrayOf()): RailsConsoleExecutionResult {
        val executionMode = ExecutionModes.SameThreadMode(false)
        executionMode.addProcessListener(object : ProcessListener {
            override fun processTerminated(event: ProcessEvent) { }
            override fun processWillTerminate(event: ProcessEvent, willBeDestroyed: Boolean) { }
            override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { }

            override fun startNotified(event: ProcessEvent) {
                PrintWriter(event.processHandler.processInput, true).use { it.println(toExec); it.println("quit") }
            }
        })

        val processOutput = RubyScriptExecutionContext.create(Paths.get(projectDirPath, Rails4Constants.CONSOLE4_SCRIPT).toString(), sdk)
//                .withInterpreterOptions(*rubyConsoleArguments) todo API doesn't exist anymore :(
                // todo replace with .withInterpreterOptions when API becomes available
                .withAdditionalEnvs(mapOf(RubyUtil.RUBYOPT to rubyConsoleArguments.joinToString(separator = " ")))
                .withArguments(Rails3Constants.CONSOLE, *railsConsoleArguments)
                .withExecutionMode(executionMode)
                .withWorkingDirPath(projectDirPath).executeScript()
                ?: throw ExecutionException("Error occurred while launching rails console")

        listener?.irbConsoleExecuted()

        return RailsConsoleRunner.RailsConsoleExecutionResult(
                processOutput.stdout,
                processOutput.stderr
        )
    }

    /**
     * [Listener] of particular events in [RailsConsoleRunner].
     */
    interface Listener {
        /**
         * It would be called first
         */
        fun irbConsoleExecuted()

        /**
         * It would be called second
         */
        fun informationWasExtractedFromIRB()
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportAncestorsActions.kt
================================================
package org.jetbrains.plugins.ruby.ruby.actions

import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorBase
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorByObjectSpace
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorByRubyMine
import org.jetbrains.plugins.ruby.ancestorsextractor.RubyModule
import java.io.PrintWriter

/**
 * Base class representation for exporting Ruby on Rails project's modules' ancestors
 */
abstract class ExportAncestorsActionBase(
        whatToExport: String,
        generateFilename: (Project) -> String,
        private val extractor: AncestorsExtractorBase
) : ExportFileActionBase(whatToExport, generateFilename, extensions = arrayOf("txt"),
        numberOfProgressBarFractions = 5) {

    override fun backgroundProcess(absoluteFilePath: String, module: Module?, sdk: Sdk?, project: Project) {
        moveProgressBarForward()
        extractor.listener = ProgressListener()
        val ancestors: List<RubyModule> = try {
            extractor.extractAncestors(project, sdk ?: throw IllegalStateException("Ruby SDK is not set"))
        } catch(ex: Throwable) {
            PrintWriter(absoluteFilePath).use {
                it.println(ex.message)
            }
            return
        }
        moveProgressBarForward()
        PrintWriter(absoluteFilePath).use { printWriter ->
            ancestors.forEach {
                printWriter.println("Module: ${it.name}")
                printWriter.print("Ancestors: ")
                if (it.ancestors.isEmpty()) printWriter.print("Nothing found")
                it.ancestors.forEach { printWriter.print("$it ") }
                printWriter.print("\n\n")
            }
        }
        moveProgressBarForward()
    }
}

class ExportAncestorsByObjectSpaceAction : ExportAncestorsActionBase(
        whatToExport = "ancestors by ObjectSpace",
        generateFilename = { project -> "ancestors-by-objectspace-${project.name}" },
        extractor = AncestorsExtractorByObjectSpace()
)

class ExportAncestorsByRubymineAction : ExportAncestorsActionBase(
        whatToExport = "ancestors by RubyMine",
        generateFilename = { project -> "ancestors-by-rubymine-${project.name}" },
        extractor = AncestorsExtractorByRubyMine()
)

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportAncesttorsDiffAction.kt
================================================
package org.jetbrains.plugins.ruby.ruby.actions

import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorByObjectSpace
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorByRubyMine
import org.jetbrains.plugins.ruby.ancestorsextractor.RubyModule
import java.io.PrintWriter

class ExportAncestorsDiffAction : ExportFileActionBase(whatToExport = "ancestors diff",
        generateFilename = { project: Project ->  "ancestors-diff-${project.name}" }, extensions = arrayOf("txt"),
        numberOfProgressBarFractions = 9) {
    override fun backgroundProcess(absoluteFilePath: String, module: Module?, sdk: Sdk?, project: Project) {
        moveProgressBarForward()
        val byObjectSpace: List<RubyModule>
        val byRubyMine: List<RubyModule>
        val ancestorHashSymbolIncluderToWhereIncluded: Map<String, String>
        try {
            val listener = ProgressListener()
            val ancestorsExtractorByObjectSpace = AncestorsExtractorByObjectSpace(listener)

            // Here all listener methods would be called
            byObjectSpace = ancestorsExtractorByObjectSpace.extractAncestors(project, sdk ?: throw IllegalStateException("Ruby SDK is not set"))

            // The second place where all listener methods would be called
            ancestorHashSymbolIncluderToWhereIncluded = ancestorsExtractorByObjectSpace.extractIncludes(project, sdk)

            // Provide all modulesNames same as in byObjectSpace for easy ancestors comparison
            val allModulesNames: List<String> = byObjectSpace.map { it.name }

            // The third place where all listener methods would be called
            byRubyMine = AncestorsExtractorByRubyMine(allModulesNames, listener)
                    .extractAncestors(project, sdk)
        } catch (ex: Throwable) {
            PrintWriter(absoluteFilePath).use {
                it.println(ex.message)
            }
            return
        }

        moveProgressBarForward()
        PrintWriter(absoluteFilePath).use { printWriter ->
            val objectSpaceIterator = byObjectSpace.iterator()
            val rubymineIterator = byRubyMine.iterator()
            while (objectSpaceIterator.hasNext() && rubymineIterator.hasNext()) {
                val a = objectSpaceIterator.next()
                val b = rubymineIterator.next()
                assert(a.name == b.name)
                printWriter.println("Module: ${a.name}")
                var same = true
                a.ancestors.filter { !b.ancestors.contains(it) }.let {
                    if (!it.isEmpty()) {
                        same = false
                        printWriter.print("Ancestors in ObjectSpace only: ")
                        it.forEach {
                            val whereIncluded = ancestorHashSymbolIncluderToWhereIncluded[it + "#" + a.name]
                            var toPrint = it
                            if (whereIncluded != null) {
                                toPrint += "($whereIncluded)"
                            }
                            printWriter.print("$toPrint ")
                        }
                        printWriter.println()
                    }
                }
                b.ancestors.filter { !a.ancestors.contains(it) }.let {
                    if (!it.isEmpty()) {
                        same = false
                        printWriter.print("Ancestors in RubyMine only: ")
                        it.forEach { printWriter.print("$it ") }
                        printWriter.println()
                    }
                }
                if (same) {
                    printWriter.println("No difference in ancestors list")
                }
                printWriter.println()
            }
        }
        moveProgressBarForward()
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportFileActionBase.kt
================================================
package org.jetbrains.plugins.ruby.ruby.actions

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.fileChooser.FileSaverDescriptor
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl
import com.intellij.openapi.fileChooser.ex.FileSaverDialogImpl
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.util.ProgressWindow
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.ui.DialogBuilder
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.ThrowableComputable
import org.jetbrains.plugins.ruby.ancestorsextractor.AncestorsExtractorBase
import org.jetbrains.plugins.ruby.ancestorsextractor.RailsConsoleRunner
import org.jetbrains.plugins.ruby.ruby.RModuleUtil

/**
 * Base class representing file export action with "save to" dialog
 * @param whatToExport Will be shown in "save to" dialog
 * @param generateFilename Generate filename for "save to" dialog
 * @param extensions Array of available extensions for exported file
 * @param description Description in "save to" dialog
 */
abstract class ExportFileActionBase(
        private val whatToExport: String,
        private val generateFilename: (Project) -> String,
        private val extensions: Array<String>,
        private val description: String = "",
        private val numberOfProgressBarFractions: Int? = null
) : DumbAwareAction() {
    final override fun actionPerformed(e: AnActionEvent) {
        val project = e.project ?: return
        val dialog = FileSaverDialogImpl(FileSaverDescriptor(
                "Export $whatToExport",
                description,
                *extensions), project)
        val fileWrapper = dialog.save(null, generateFilename(project)) ?: return

        val module: Module? = RModuleUtil.getInstance().getModule(e.dataContext)
        val sdk: Sdk? = RModuleUtil.getInstance().findRubySdkForModule(module)

        try {
            ProgressManager.getInstance().runProcessWithProgressSynchronously(ThrowableComputable<Unit, Exception> {
                return@ThrowableComputable backgroundProcess(fileWrapper.file.absolutePath, module, sdk, project)
            }, "Exporting $whatToExport", false, project)
        } catch (ex: Exception) {
            Messages.showErrorDialog(ex.message, "Error while exporting $whatToExport")
        }
    }

    /**
     * In this method implementation you can do you job needed for file export and then file exporting itself.
     *
     * @param absoluteFilePath absolute file path which user have chosen to save file to.
     * @param module module from the context of action it invoked
     * @param sdk sdk from the context of action it invoked
     * @param project project from the context of action it invoked
     */
    protected abstract fun backgroundProcess(absoluteFilePath: String, module: Module?, sdk: Sdk?, project: Project)

    @Throws(IllegalStateException::class)
    protected fun moveProgressBarForward() {
        if (numberOfProgressBarFractions == null) throw IllegalStateException("You cannot call moveProgressBarForward() " +
                "method when progressBarFractions property is null")
        val progressIndicator = ProgressManager.getInstance().progressIndicator
        if (progressIndicator is ProgressWindow) {
            progressIndicator.fraction = minOf(1.0, progressIndicator.fraction + 1.0/numberOfProgressBarFractions)
        }
    }

    /**
     * You can use to set as [AncestorsExtractorBase.listener] because every [ProgressListener]
     * method call just calls [moveProgressBarForward]
     */
    protected inner class ProgressListener : RailsConsoleRunner.Listener {
        override fun irbConsoleExecuted() {
            moveProgressBarForward()
        }

        override fun informationWasExtractedFromIRB() {
            moveProgressBarForward()
        }
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ImportExportContractsAction.kt
================================================
package org.jetbrains.plugins.ruby.ruby.actions

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.ThrowableComputable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.plugins.ruby.ruby.codeInsight.types.resetAllRubyTypeProviderAndIDEACaches
import org.jetbrains.ruby.codeInsight.types.signature.CallInfo
import org.jetbrains.ruby.codeInsight.types.storage.server.DatabaseProvider
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.CallInfoRow
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.CallInfoTable
import java.io.File

const val CHUNK_SIZE = 1500

fun Database.copyTo(destination: Database, moveProgressBar: Boolean) {
    var progressIndicator: ProgressIndicator? = null
    var count: Int? = null

    if (moveProgressBar) {
        progressIndicator = ProgressManager.getInstance().progressIndicator
        count = transaction(this) { CallInfoTable.selectAll().count() }
    }

    var offset = 0
    while (true) {
        val info: List<CallInfo> = transaction(this) {
            CallInfoRow.wrapRows(CallInfoTable.selectAll().limit(CHUNK_SIZE, offset)).map { it.copy() }
        }
        if (info.isEmpty()) {
            break
        }

        transaction(destination) {
            info.forEach { CallInfoTable.insertInfoIfNotContains(it) }
        }

        offset += CHUNK_SIZE

        if (moveProgressBar) {
            progressIndicator!!.fraction = offset.toDouble() / count!!
        }
    }
}

class ExportContractsAction : ExportFileActionBase(
        whatToExport = "Type contracts",
        generateFilename = { project: Project -> "${project.name}-type-contracts" },
        extensions = arrayOf("mv.db")
) {
    override fun backgroundProcess(absoluteFilePath: String, module: Module?, sdk: Sdk?, project: Project) {
        exportContractsToFile(absoluteFilePath, moveProgressBar = true)
    }

    companion object {
        fun exportContractsToFile(pathToExport: String, moveProgressBar: Boolean) {
            check(pathToExport.endsWith(DatabaseProvider.H2_DB_FILE_EXTENSION)) {
                "Path to export must end with .mv.db"
            }
            File(pathToExport).delete()

            val databaseToExportTo = DatabaseProvider.connectToDB(pathToExport)

            DatabaseProvider.defaultDatabase!!.copyTo(databaseToExportTo, moveProgressBar)
        }
    }
}

class ImportContractsAction : DumbAwareAction() {
    override fun actionPerformed(e: AnActionEvent) {
        val project = e.project
        val files = FileChooserDialogImpl(
                FileChooserDescriptor(true, false, false, false, false, false),
                project).choose(project)

        if (files.isEmpty()) {
            return
        }

        try {
            ProgressManager.getInstance().runProcessWithProgressSynchronously(ThrowableComputable<Unit, Exception> {
                files.forEach { importContractsFromFile(it.path, moveProgressBar = true) }
                return@ThrowableComputable
            }, "Importing type contracts", false, project)
            resetAllRubyTypeProviderAndIDEACaches(project)
        } catch (ex: Exception) {
            Messages.showErrorDialog(ex.message, "Error while importing type contracts")
        }
    }

    companion object {
        fun importContractsFromFile(pathToImportFrom: String, moveProgressBar: Boolean) {
            check(pathToImportFrom.endsWith(DatabaseProvider.H2_DB_FILE_EXTENSION)) {
                "Path to import from must end with .mv.db"
            }

            val dbToImportFrom = DatabaseProvider.connectToDB(pathToImportFrom)

            dbToImportFrom.copyTo(DatabaseProvider.defaultDatabase!!, moveProgressBar)
        }
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/ProjectLifecycleListenerImpl.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight

import com.google.gson.Gson
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManagerListener
import org.jetbrains.plugins.ruby.ruby.persistent.TypeInferenceDirectory
import org.jetbrains.plugins.ruby.util.runServerAsyncInIDEACompatibleMode
import org.jetbrains.ruby.codeInsight.types.storage.server.DatabaseProvider
import org.jetbrains.ruby.runtime.signature.server.SignatureServer
import java.io.File
import java.io.PrintWriter
import java.nio.file.Paths

/**
 * Short [Project] description for `rubymine-type-tracer`
 */
data class ProjectDescription(val projectName: String, val projectPath: String, val pipeFilePath: String) {
    /**
     * @param project default projects are not allowed!
     */
    constructor(project: Project, pipeFilePath: String) : this(project.name, project.basePath!!, pipeFilePath)
}

/**
 * This directory is needed for `rubymine-type-tracker` script
 *
 * In this directory we keep files named the same as currently opened projects in RubyMine.
 * Each file contains projectPath of pipe file required for arg-scanner.
 */
private val openedProjectsDir = File(System.getProperty("java.io.tmpdir")!!).resolve(".ruby-type-inference")
        .also { it.mkdirs() }

/**
 * This registered in `plugin.xml` and it's constructor called every time RubyMine starts
 */
class ProjectLifecycleListenerImpl : ProjectManagerListener {
    private val gson = Gson()

    private companion object {
        @Volatile
        private var initialized: Boolean = false
    }

    override fun projectOpened(project: Project) {
        if (!project.isDefault) {
            connectToDB(project.name)

            // This server is used for `rubymine-type-tracker` script
            startNewBackgroundInfinityServer(project)
        }
    }

    override fun projectClosed(project: Project) {
        if (!project.isDefault) {
            val projectDescription = readProjectDescription(project, deleteJsonAfterRead = true)
            File(projectDescription.pipeFilePath).delete()
        }
    }

    private fun connectToDB(projectName: String) {
        val filePath = Paths.get(
                TypeInferenceDirectory.RUBY_TYPE_INFERENCE_DIRECTORY.toString(),
                projectName).toString() + DatabaseProvider.H2_DB_FILE_EXTENSION

        DatabaseProvider.connectToDB(filePath, isDefaultDatabase = true)
    }

    /**
     * Starts server for `rubymine-type-tracker` script
     */
    private fun startNewBackgroundInfinityServer(project: Project): Boolean {
        if (project.isDefault) {
            return false
        }

        val server = SignatureServer()
        val pipeFilePath: String = server.runServerAsyncInIDEACompatibleMode(project)

        writeProjectDescription(ProjectDescription(project, pipeFilePath))

        server.afterExitListener = {
            startNewBackgroundInfinityServer(project)
        }
        return true
    }

    private fun writeProjectDescription(description: ProjectDescription) {
        val jsonFile: File = openedProjectsDir.resolve(description.projectName)
        PrintWriter(jsonFile).use { it.println(gson.toJson(description)) }
    }

    private fun readProjectDescription(project: Project, deleteJsonAfterRead: Boolean = false): ProjectDescription {
        val jsonFile: File = openedProjectsDir.resolve(project.name)
        val json: String = jsonFile.bufferedReader().use { it.readText() }
        val description = gson.fromJson<ProjectDescription>(json, ProjectDescription::class.java)!!
        if (deleteJsonAfterRead) {
            jsonFile.delete()
        }
        return description
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/RubyDynamicCodeInsightPluginAppLifecyctlListener.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight

import com.intellij.ide.AppLifecycleListener
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.ruby.RubyDynamicCodeInsightPluginInjector
import org.jetbrains.ruby.codeInsight.initInjector

class RubyDynamicCodeInsightPluginAppLifecyctlListener : AppLifecycleListener {
    override fun appStarting(projectFromCommandLine: Project?) {
        initInjector(RubyDynamicCodeInsightPluginInjector)
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/TrackerDataLoader.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight

import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker.RubyClassHierarchyWithCaching

class TrackerDataLoader : StartupActivity, DumbAware {
    override fun runActivity(project: Project) {

        ModuleManager.getInstance(project).modules.forEach {
            RubyClassHierarchyWithCaching.loadFromSystemDirectory(it)
        }

    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/stateTracker/ClassHierarchySymbolProvider.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker

import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiElement
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.RubySymbolProviderBase
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.fqn.FQN
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.Symbol
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.v2.SymbolPsiProcessor
import org.jetbrains.plugins.ruby.ruby.lang.psi.RPsiElement

class ClassHierarchySymbolProvider : RubySymbolProviderBase() {
    override fun processDynamicSymbols(symbol: Symbol, element: RPsiElement?, fqn: FQN, processor: SymbolPsiProcessor,
                                       invocationPoint: PsiElement?): Boolean {
        if (element == null) {
            return true
        }

        val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return true
        val hierarchy = RubyClassHierarchyWithCaching.getInstance(module)?: return true
        hierarchy.getMembersWithCaching(fqn.fullPath, symbol.rootSymbol).forEach {
            if (!processor.process(it)) {
                return false
            }
        }
        return true
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/stateTracker/RubyClassHierarchyWithCaching.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker

import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Key
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Type
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Types
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.RMethodSyntheticSymbol
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.Symbol
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.SymbolUtil
import org.jetbrains.plugins.ruby.ruby.persistent.TypeInferenceDirectory
import org.jetbrains.plugins.ruby.settings.RubyTypeContractsSettings
import org.jetbrains.ruby.stateTracker.*
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream

class RubyClassHierarchyWithCaching private constructor(private val rubyClassHierachy: RubyClassHierarchy) {
    private val lookupCache = ContainerUtil.createConcurrentWeakMap<Pair<String, String>, RubyMethod>()
    private val membersCache = ContainerUtil.createConcurrentWeakMap<String, Set<Symbol>>()

    fun getTypeForConstant(constant: String): RubyConstant? {
        return rubyClassHierachy.topLevelConstants[constant]
    }

    fun getMembersWithCaching(moduleName: String, topLevel: Symbol) : Set<Symbol> {
        val module = rubyClassHierachy.getRubyModule(moduleName) ?: return emptySet()
        return getMembersWithCaching(module, topLevel)
    }

    private fun lookupInstanceMethodWithCaching(module: RubyModule, methodName: String) : RubyMethod? {
        return lookupCache.computeIfAbsent(Pair(module.name, methodName)) { lookupInstanceMethod(module, methodName) }
    }

    private fun lookupInstanceMethod(module: RubyModule, methodName: String): RubyMethod? {
        val ownResult = module.instanceMethods.firstOrNull {it.name == methodName}
        if (ownResult != null) {
            return ownResult
        }

        module.instanceDirectAncestors.forEach {
            val result = lookupInstanceMethodWithCaching(it, methodName)
            if (result != null) {
                return result
            }
        }

        if (module is RubyClass && module.superClass != RubyClass.EMPTY) {
            val result = lookupInstanceMethodWithCaching(module.superClass, methodName)
            if (result != null) {
                return result
            }
        }

        return null
    }

    private fun getMembersWithCaching(module: RubyModule, topLevel: Symbol) : Set<Symbol> {
        return membersCache.computeIfAbsent(module.name) { getMembers(module, topLevel) }
    }

    private fun getMembers(module: RubyModule, topLevel: Symbol) : Set<Symbol> {
        val set = HashSet<Symbol>()
        val symbol = SymbolUtil.findSymbolInHierarchy(topLevel, module.name, Types.MODULE_OR_CLASS, topLevel.psiElement)
        set.addAll(module.instanceMethods.map {  RMethodSyntheticSymbol(topLevel.project, Type.INSTANCE_METHOD, it, symbol) })
        set.addAll(module.classMethods.map {  RMethodSyntheticSymbol(topLevel.project, Type.CLASS_METHOD, it, symbol) })

        module.instanceDirectAncestors.forEach { set.addAll(getMembersWithCaching(it, topLevel))}
        module.classDirectAncestors.forEach{ set.addAll(getMembersWithCaching(it, topLevel)) }

        if (module is RubyClass && module.superClass != RubyClass.EMPTY) {
            set.addAll(getMembersWithCaching(module.superClass, topLevel))
        }

        return set
    }

    companion object {
        private val CLASS_HIERARCHY_KEY = Key<RubyClassHierarchyWithCaching>("org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker.ClassHierarchy")

        private val CLASS_HIERARCHY_FILENAME = "-class-hierarchy.json.gz"

        fun loadFromSystemDirectory(module: Module): RubyClassHierarchyWithCaching? {
            val file = File(TypeInferenceDirectory.RUBY_TYPE_INFERENCE_DIRECTORY.toFile(),
                    module.project.name + "-" + module.name + CLASS_HIERARCHY_FILENAME)
            if (!file.exists()) {
                return null
            }
            FileInputStream(file).use {
                GZIPInputStream(it).use {
                    val json = it.reader(Charsets.UTF_8).use{ it.readText() }
                    return createClassHierarchyFromJson(json, module)
                }
            }
        }

        @Synchronized
        fun updateAndSaveToSystemDirectory(jsons: List<String>, module: Module) {
            val json = RubyClassHierarchyLoader.mergeJsons(jsons)
            createClassHierarchyFromJson(json, module)
            FileOutputStream(File(TypeInferenceDirectory.RUBY_TYPE_INFERENCE_DIRECTORY.toFile(),
                    module.project.name + "-" + module.name + CLASS_HIERARCHY_FILENAME)).use {
                GZIPOutputStream(it).use {
                    it.writer(Charsets.UTF_8).use { it.write(json) }
                }
            }
        }

        private fun createClassHierarchyFromJson(json: String, module: Module) : RubyClassHierarchyWithCaching {
            val rubyClassHierarchy = RubyClassHierarchyWithCaching(RubyClassHierarchyLoader.fromJson(json))
            module.putUserData(RubyClassHierarchyWithCaching.CLASS_HIERARCHY_KEY,
                    rubyClassHierarchy)
            return rubyClassHierarchy

        }

        fun getInstance(module: Module): RubyClassHierarchyWithCaching? {
            if (!ServiceManager.getService(module.project, RubyTypeContractsSettings::class.java).stateTrackerEnabled) {
                return null
            }
            val ret = module.getUserData(CLASS_HIERARCHY_KEY)
            if (ret != null) {
                return ret
            }
            return null
        }
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/symbols/structure/RMethodSyntheticSymbol.java
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure;

import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.rdoc.yard.psi.RangeInDocumentFakePsiElement;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Type;
import org.jetbrains.plugins.ruby.ruby.codeInsight.types.RType;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RPsiElement;
import org.jetbrains.plugins.ruby.ruby.lang.psi.controlStructures.methods.ArgumentInfo;
import org.jetbrains.plugins.ruby.ruby.lang.psi.controlStructures.methods.Visibility;
import org.jetbrains.plugins.ruby.ruby.lang.psi.impl.controlStructures.methods.RCommandArgumentListImpl;
import org.jetbrains.plugins.ruby.ruby.lang.psi.methodCall.RCall;
import org.jetbrains.ruby.stateTracker.Location;
import org.jetbrains.ruby.stateTracker.RubyMethod;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class RMethodSyntheticSymbol extends SymbolImpl implements RMethodSymbol {
    @NotNull
    private final Visibility myVisibility;
    @NotNull
    private final List<ArgumentInfo> myArgsInfo;
    @Nullable
    private final String myPath;

    private final int myLineno;

    public RMethodSyntheticSymbol(@NotNull final Project project,
                                  @NotNull final Type type,
                                  @NotNull final RubyMethod rubyMethod,
                                  @Nullable final Symbol parent) {
        super(project, rubyMethod.getName(), type, parent);
        myVisibility = Visibility.PUBLIC;
        myArgsInfo = toArgsInfo(rubyMethod.getArguments());
        final Location location = rubyMethod.getLocation();
        if (location != null) {
            myPath = location.getPath();
            myLineno = location.getLineNo();
        } else {
            myPath = "";
            myLineno = 0;
        }
    }

    private List<ArgumentInfo> toArgsInfo(List<RubyMethod.ArgInfo> arguments) {
        return arguments.stream().map(RMethodSyntheticSymbol::toArgumentInfo).collect(Collectors.toList());
    }

    private static ArgumentInfo toArgumentInfo(final @NotNull RubyMethod.ArgInfo argInfo) {
        ArgumentInfo.Type type;
        switch (argInfo.getKind()) {
            case REQ:
                type = ArgumentInfo.Type.SIMPLE;
                break;
            case OPT:
                type = ArgumentInfo.Type.PREDEFINED;
                break;
            case REST:
                type = ArgumentInfo.Type.ARRAY;
                break;
            case KEY:
                type = ArgumentInfo.Type.NAMED;
                break;
            case KEY_REST:
                type = ArgumentInfo.Type.HASH;
                break;
            case KEY_REQ:
                type = ArgumentInfo.Type.KEYREQ;
                break;
            case BLOCK:
                type = ArgumentInfo.Type.BLOCK;
                break;
            default:
                throw new IllegalArgumentException(argInfo.getKind().toString());
        }
        return new ArgumentInfo(argInfo.getName(), type);
    }

    @NotNull
    @Override
    public String getName() {
        //noinspection ConstantConditions
        return super.getName();
    }

    @Override
    public @Nullable
    List<ArgumentInfo> getArgumentInfos() {
        return myArgsInfo;
    }

    @Override
    @Nullable
    public List<ArgumentInfo> getArgumentInfos(boolean includeDefaultArgs) {
        return null;
    }

    @Nullable
    @Override
    public RType getCallType(@Nullable final RCall call) {
        return null;
    }

    @NotNull
    @Override
    public String getArgsPresentation() {
        if (myArgsInfo.isEmpty()) {
            return "";
        } else {
            return "(" + RCommandArgumentListImpl.getPresentableName(myArgsInfo) + ")";
        }
    }

    @Override
    public boolean isSynthetic() {
        return false;
    }

    @NotNull
    @Override
    public Visibility getVisibility() {
        return myVisibility;
    }

    @Override
    public PsiElement getPsiElement() {
        if (myPath == null) {
            return null;
        }
        final VirtualFile virtualFile = VirtualFileManager.getInstance().findFileByUrl(VfsUtilCore.pathToUrl(myPath));
        if (virtualFile == null) {
            return null;
        }

        final PsiFile file = PsiManager.getInstance(getProject()).findFile(virtualFile);
        if (file == null) {
            return null;
        }

        return CachedValuesManager.getCachedValue(file, () ->
                CachedValueProvider.Result.create(calcElement(file), file));
    }

    @NotNull
    @Override
    public Collection<PsiElement> getAllDeclarations(PsiElement invocationPoint) {
        final PsiElement psiElement = getPsiElement();
        return psiElement == null ? Collections.emptyList() : Collections.singletonList(psiElement);
    }

    @Nullable
    private PsiElement calcElement(@NotNull PsiFile file) {
        final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
        if (document == null) {
            return null;
        }
        return ReadAction.compute(() -> {
            int offset = document.getLineStartOffset(myLineno);
            int nextLineOffset = document.getLineEndOffset(myLineno);
            int curOffset = offset;
            PsiElement psiElement;
            do {
                psiElement = file.findElementAt(curOffset);
                if (psiElement == null) {
                    return null;
                }
                curOffset = psiElement.getTextRange().getEndOffset();
            } while (!(psiElement instanceof RPsiElement) && curOffset < nextLineOffset);

            if (psiElement instanceof RPsiElement) {
                return psiElement;
            }

            psiElement = file.findElementAt(offset);
            if (psiElement == null) {
                return null;
            }
            final int startElementOffset = psiElement.getTextRange().getStartOffset();
            final int endElementOffset = psiElement.getTextRange().getEndOffset();
            int start = offset - startElementOffset;
            int end = Math.min(nextLineOffset - startElementOffset, endElementOffset - startElementOffset);
            return new MyFakeElement(psiElement, new TextRange(start, end), getName());
        });
    }

    private static class MyFakeElement extends RangeInDocumentFakePsiElement {
        @NotNull
        private final String myName;

        MyFakeElement(@NotNull PsiElement parent, @NotNull TextRange rangeInParent, @NotNull String name) {
            super(parent, rangeInParent);
            myName = name;
        }

        @NotNull
        @Override
        public String getName() {
            return myName;
        }

        @Override
        public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
            throw new IncorrectOperationException("not supported");
        }
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyCollectStateRunner.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.types

import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.RunProfile
import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.executors.CollectStateExecutor
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.plugins.ruby.ruby.run.configuration.AbstractRubyRunConfiguration
import org.jetbrains.plugins.ruby.ruby.run.configuration.CollectExecSettings
import org.jetbrains.plugins.ruby.ruby.run.configuration.RubyAbstractCommandLineState
import org.jetbrains.plugins.ruby.ruby.run.configuration.RubyProgramRunner
import java.io.IOException

class RubyCollectStateRunner : RubyProgramRunner() {

    override fun canRun(executorId: String, profile: RunProfile): Boolean {
        return executorId == CollectStateExecutor.EXECUTOR_ID && profile is AbstractRubyRunConfiguration<*>
    }

    @Throws(ExecutionException::class)
    override fun doExecute(state: RunProfileState,
                           environment: ExecutionEnvironment): RunContentDescriptor? {
        if (state is RubyAbstractCommandLineState) {
            val newConfig = state.config.clone()
            val pathToState =  tryGenerateTmpDirPath()

            CollectExecSettings.putTo(newConfig,
                    CollectExecSettings.createSettings(true,
                            false,
                            true,
                            pathToState
                    ))
            val newState = newConfig.getState(environment.executor, environment)
            if (newState != null) {
                return super.doExecute(newState, environment)
            }
        }

        return null
    }


    private fun tryGenerateTmpDirPath(): String? {
        try {
            val tmpDir = FileUtil.createTempDirectory("state-tracker", "")
            return tmpDir.absolutePath
        } catch (ignored: IOException) {
            return null
        }

    }

    override fun getRunnerId(): String {
        return RUBY_COLLECT_STATE_RUNNER_ID
    }

    companion object {
        private val RUBY_COLLECT_STATE_RUNNER_ID = "RubyCollectState"
    }
}

================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyRunWithTypeTrackerRunner.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.types

import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.RunProfile
import com.intellij.execution.configurations.RunProfileState
import com.intellij.execution.executors.RunWithTypeTrackerExecutor
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.plugins.ruby.ruby.run.configuration.AbstractRubyRunConfiguration
import org.jetbrains.plugins.ruby.ruby.run.configuration.CollectExecSettings
import org.jetbrains.plugins.ruby.ruby.run.configuration.RubyAbstractCommandLineState
import org.jetbrains.plugins.ruby.ruby.run.configuration.RubyProgramRunner
import org.jetbrains.plugins.ruby.settings.RubyTypeContractsSettings
import java.io.IOException

class RubyRunWithTypeTrackerRunner : RubyProgramRunner() {

    @Throws(ExecutionException::class)
    override fun doExecute(state: RunProfileState,
                           environment: ExecutionEnvironment): RunContentDescriptor? {
        if (state is RubyAbstractCommandLineState) {
            val (_, _, typeTrackerEnabled) = ServiceManager.getService(environment.project, RubyTypeContractsSettings::class.java)
            val newConfig = state.config.clone()
            val pathToState = tryGenerateTmpDirPath()

            CollectExecSettings.putTo(newConfig,
                    CollectExecSettings.createSettings(true,
                            typeTrackerEnabled,
                            false,
                            pathToState
                    ))
            val newState = newConfig.getState(environment.executor, environment)
            if (newState != null) {
                return super.doExecute(newState, environment)
            }
        }

        return null
    }

    override fun preloaderAllowed(): Boolean = false

    private fun tryGenerateTmpDirPath(): String? = try {
        val tmpDir = FileUtil.createTempDirectory("type-tracker", "")
        tmpDir.absolutePath
    } catch (ignored: IOException) {
        null
    }

    override fun canRun(executorId: String, profile: RunProfile): Boolean {
        return executorId == RunWithTypeTrackerExecutor.EXECUTOR_ID && profile is AbstractRubyRunConfiguration<*>
    }

    override fun getRunnerId(): String {
        return RUBY_COLLECT_TYPE_RUNNER_ID
    }

    companion object {
        private val RUBY_COLLECT_TYPE_RUNNER_ID = "RubyCollectType"
    }
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyTypeProvider.kt
================================================
package org.jetbrains.plugins.ruby.ruby.codeInsight.types

import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.ruby.ruby.codeInsight.AbstractRubyTypeProvider
import org.jetbrains.plugins.ruby.ruby.codeInsight.IncomingType
import org.jetbrains.plugins.ruby.ruby.codeInsight.resolve.ResolveUtil
import org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker.RubyClassHierarchyWithCaching
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.SymbolicExecutionContext
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.SymbolicExpressionProvider
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.SymbolicTypeInferenceProvider
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.TypeInferenceComponent
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.instance.TypeInferenceInstance
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.symbolicExpression.SymbolicCall
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbolicExecution.symbolicExpression.SymbolicExpression
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Type
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.Symbol
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.util.SymbolHierarchy
import org.jetbrains.plugins.ruby.ruby.codeInsight.types.impl.REmptyType
import org.jetbrains.plugins.ruby.ruby.lang.psi.RPsiElement
import org.jetbrains.plugins.ruby.ruby.lang.psi.RubyPsiUtil
import org.jetbrains.plugins.ruby.ruby.lang.psi.expressions.RExpression
import org.jetbrains.plugins.ruby.ruby.lang.psi.variables.RIdentifier
import org.jetbrains.ruby.codeInsight.types.signature.*
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.CallInfoTable
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.RSignatureProviderImpl
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException

/**
 * Cache where we store last accessed [CallInfo]s. Thread safe
 */
private val registeredCallInfosCache: MutableMap<MethodInfo, List<CallInfo>>
        = ContainerUtil.createConcurrentSoftKeySoftValueMap<MethodInfo, List<CallInfo>>()

fun resetAllRubyTypeProviderAndIDEACaches(project: Project?) {
    registeredCallInfosCache.clear()
    // Clears IDEAs caches about inferred types
    ServiceManager.getService(project ?: return, TypeInferenceContext::class.java)?.clear()
}

fun getCachedOrComputedRegisteredCallInfo(methodInfo: MethodInfo): List<CallInfo> {
    return registeredCallInfosCache.getOrPut(methodInfo) {
        RSignatureProviderImpl.getRegisteredCallInfos(methodInfo)
    }
}

class RubyParameterTypeProvider : AbstractRubyTypeProvider() {
    override fun createTypeBySymbol(symbol: Symbol): RType? {
        return null
    }

    override fun createTypeByRExpression(expr: RExpression): RType? {
        val symbol = ResolveUtil.resolveToSymbolWithCaching(expr.reference, false)
        if (symbol?.type == Type.CONSTANT) {
            val module = ModuleUtilCore.findModuleForPsiElement(expr) ?: return null
            val classHierarchyWithCaching = RubyClassHierarchyWithCaching.getInstance(module) ?: return null
            val path = symbol?.fqnWithNesting?.fullPath ?: return null
            val constant = classHierarchyWithCaching.getTypeForConstant(path) ?: return null
            val originType = RTypeFactory.createTypeByFQN(expr.project, constant.type)
            val mixins = constant.extended.map { RTypeFactory.createTypeByFQN(expr.project, it) }
            if (mixins.isNotEmpty()) {
                return RTypeUtil.union(originType,  mixins.reduce { acc, it -> RTypeUtil.union(acc, it) })
            }
            return originType
        }
        if (expr is RIdentifier && expr.isParameter) {
            val method = RubyPsiUtil.getContainingRMethod(expr) ?: return null
            val rubyModuleName = RubyPsiUtil.getContainingRClassOrModule(method)?.fqn?.fullPath ?: "Object"

            val info = MethodInfo.Impl(ClassInfo(rubyModuleName), method.fqn.shortName, RVisibility.PUBLIC)

            val callInfos: List<CallInfo> = getCachedOrComputedRegisteredCallInfo(info)

            val returnType = callInfos.map { callInfo ->
                val typeName = expr.name?.let { callInfo.getTypeNameByArgumentName(it) } ?: return@map REmptyType.INSTANCE
                return@map RTypeFactory.createTypeClassName(typeName, expr)
            }.unionTypesSmart()
            return if (returnType == REmptyType.INSTANCE) {
                // If we don't have any information about type then return null
                // in order to allow to RubyMine try to determine type itself
                null
            } else {
                returnType
            }
        }
        return null
    }
}

/**
 * Provides types for Ruby method return values. Type providing based on information collection into [CallInfoTable]
 */
class ReturnTypeSymbolicTypeInferenceProvider : SymbolicTypeInferenceProvider {
    companion object {
        private val pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1)
    }

    override fun evaluateSymbolicCall(symbolicCall: SymbolicCall,
                                      context: SymbolicExecutionContext,
                                      callContext: TypeInferenceInstance.CallContext,
                                      provider: SymbolicExpressionProvider,
                                      component: TypeInferenceComponent): SymbolicExpression? {
        ProgressManager.checkCanceled()
        val job: () -> SymbolicExpression? = {
            evaluateSymbolicCallPoolJob(symbolicCall, context, callContext, component)
        }

        val future: Future<SymbolicExpression?> = pool.submit(job)
        return try {
            // This method is run under read action and we cannot afford to spend a lot of time determining time.
            // Otherwise we got glitches see: https://youtrack.jetbrains.com/issue/RUBY-25433
            future.get(100, TimeUnit.MILLISECONDS)
        } catch (ex: TimeoutException) {
            null
        }
    }

    private fun evaluateSymbolicCallPoolJob(symbolicCall: SymbolicCall,
                                            context: SymbolicExecutionContext,
                                            callContext: TypeInferenceInstance.CallContext,
                                            component: TypeInferenceComponent): SymbolicExpression? {
        var unnamedArgsTypes: List<String?> = emptyList()
        var namedArgsTypes: List<ArgumentNameAndType?> = emptyList()
        var receiverTypesConsideringAncestors: List<String> = emptyList()
        ReadAction.run<Exception> {
            ProgressManager.checkCanceled()
            val exactReceiverType: RType = SymbolicTypeInferenceProvider.getReceiverType(symbolicCall, component, callContext)

            // reversed because getAncestorsCaching gives us list of ancestors ordered from parent to end children
            // This list already include exactReceiverType
            receiverTypesConsideringAncestors = RTypeUtil.getBirthTypeSymbol(exactReceiverType)
                    ?.let { SymbolHierarchy.getAncestorsCaching(it, callContext.invocationPoint) }
                    ?.map { it.symbol.fqnWithNesting.toString() }?.reversed() ?: emptyList()

            val typeInferenceComponent = context.getComponent(TypeInferenceComponent::class.java)

            unnamedArgsTypes = symbolicCall.arguments.asSequence().filter { it.type != IncomingType.ASSOC }
                    .map { typeInferenceComponent.getTypeForSymbolicExpression(it.expression).name }.toList()

            namedArgsTypes = symbolicCall.arguments.asSequence().filter { it.type == IncomingType.ASSOC }
                    .map {
                        val type = typeInferenceComponent.getTypeForSymbolicExpression(it.expression).name ?: return@map null
                        return@map ArgumentNameAndType(it.keyName, type)
                    }.toList()
        }

        for (receiverTypeName in receiverTypesConsideringAncestors) {
            val methodInfo = MethodInfo.Impl(ClassInfo.Impl(null, receiverTypeName), symbolicCall.name)

            val registeredCallInfos = getCachedOrComputedRegisteredCallInfo(methodInfo)

            val registeredReturnTypes: List<String> = registeredCallInfos
                    .asSequence()
                    .filter { argumentsMatch(it, unnamedArgsTypes, namedArgsTypes) }
                    .map { it.returnType }
                    .toList()
                    .takeIf { !it.isEmpty() }
                    ?: registeredCallInfos.map { it.returnType }

            val returnType = registeredReturnTypes
                    .mapNotNull {
                        RTypeFactory.createTypeClassName(it, callContext.invocationPoint as? RPsiElement
                                ?: return@mapNotNull null)
                    }
                    .unionTypesSmart()

            if (returnType != REmptyType.INSTANCE) {
                component.updateSymbolicExpressionType(symbolicCall, returnType)
                return symbolicCall
            }
        }
        // If we don't have any information about type then return null
        // in order to allow to RubyMine try to determine type itself
        return null
    }

    private fun argumentsMatch(oneOfExpected: CallInfo, actualUnnamedArgs: List<String?>, actualNamedArgs: List<ArgumentNameAndType?>): Boolean {
        if (oneOfExpected.unnamedArguments.size != actualUnnamedArgs.size || oneOfExpected.namedArguments.size != actualNamedArgs.size) {
            return false
        }

        if (oneOfExpected.unnamedArguments.zip(actualUnnamedArgs).any {
                    it.first.type != ArgumentNameAndType.IMPLICITLY_PASSED_ARGUMENT_TYPE &&
                            it.second != null &&
                            it.first.type != it.second
                }) {
            return false
        }

        if (oneOfExpected.namedArguments.zip(actualNamedArgs).any {
                    it.second != null &&
                            it.first.type != ArgumentNameAndType.IMPLICITLY_PASSED_ARGUMENT_TYPE &&
                            it.second!!.type != ArgumentNameAndType.IMPLICITLY_PASSED_ARGUMENT_TYPE &&
                            it.first.type != it.second!!.type
                }) {
            return false
        }

        return true
    }
}

/**
 * The same as [unionTypes] but also get rid of [REmptyType] and duplicates
 */
private fun List<RType>.unionTypesSmart(): RType = filter { it != REmptyType.INSTANCE }.distinct().unionTypes()

private fun List<RType>.unionTypes(): RType {
    if (size == 0) {
        return REmptyType.INSTANCE
    }

    if (size == 1) {
        return first()
    }
    val m = size / 2
    return RTypeUtil.union(subList(0, m).unionTypes(), subList(m, size).unionTypes())
}


================================================
FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/intentions/AddContractAnnotationIntention.java
================================================
package org.jetbrains.plugins.ruby.ruby.intentions;

import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.exposed.dao.EntityID;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.fqn.FQN;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RFile;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RubyElementFactory;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RubyPsiUtil;
import org.jetbrains.plugins.ruby.ruby.lang.psi.controlStructures.methods.RMethod;
import org.jetbrains.plugins.ruby.ruby.lang.psi.variables.RFName;
import org.jetbrains.ruby.codeInsight.types.signature.*;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.ContractTransition;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.ReferenceContractTransition;
import org.jetbrains.ruby.codeInsight.types.signature.contractTransition.TypedContractTransition;
import org.jetbrains.ruby.codeInsight.types.storage.server.DatabaseProvider;
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.MethodInfoTable;
import org.jetbrains.ruby.codeInsight.types.storage.server.impl.RSignatureProviderImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AddContractAnnotationIntention extends BaseRubyMethodIntentionAction {
    @Nls(capitalization = Nls.Capitalization.Sentence)
    @NotNull
    @Override
    public String getFamilyName() {
        return getText();
    }

    public boolean isAvailable(@NotNull final Project project, final @NotNull Editor editor, @NotNull final PsiFile file) {
        if (!super.isAvailable(project, editor, file)) {
            return false;
        }
        if (!file.isWritable()) {
            return false;
        }
        RFName rfName = getRFName(editor, file);
        if (rfName == null) {
            return false;
        }
        RMethod method = RubyPsiUtil.getContainingRMethod(rfName);
        if (method == null) {
            return false;
        }

        MethodInfo methodInfo = createMethodInfo(method);

        EntityID<Integer> found = methodInfo != null ? DatabaseProvider.defaultDatabaseTransaction(
                transaction -> MethodInfoTable.INSTANCE.findRowId(methodInfo)) : null;
        return found != null;
    }

    pu
Download .txt
gitextract_m6imoy85/

├── .gitignore
├── .travis.yml
├── FEATURES.md
├── LICENSE
├── README.md
├── arg_scanner/
│   ├── .gitignore
│   ├── Gemfile
│   ├── LICENSE.txt
│   ├── README.md
│   ├── Rakefile
│   ├── arg_scanner.gemspec
│   ├── bin/
│   │   ├── arg-scanner
│   │   ├── console
│   │   ├── rubymine-type-tracker
│   │   └── setup
│   ├── ext/
│   │   └── arg_scanner/
│   │       ├── arg_scanner.c
│   │       ├── arg_scanner.h
│   │       └── extconf.rb
│   ├── lib/
│   │   ├── arg_scanner/
│   │   │   ├── options.rb
│   │   │   ├── require_all.rb
│   │   │   ├── starter.rb
│   │   │   ├── state_tracker.rb
│   │   │   ├── type_tracker.rb
│   │   │   ├── version.rb
│   │   │   └── workspace.rb
│   │   └── arg_scanner.rb
│   ├── test/
│   │   ├── helper.rb
│   │   ├── test_args_info.rb
│   │   ├── test_call_info.rb
│   │   └── test_state_tracker.rb
│   └── util/
│       └── state_filter.rb
├── build.gradle
├── common/
│   ├── build.gradle
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── jetbrains/
│                       └── ruby/
│                           └── codeInsight/
│                               ├── Injector.kt
│                               ├── Logger.kt
│                               └── PrintToStdoutLogger.kt
├── contract-creator/
│   ├── build.gradle
│   └── src/
│       └── org/
│           └── jetbrains/
│               └── ruby/
│                   └── runtime/
│                       └── signature/
│                           └── server/
│                               ├── SignatureServer.kt
│                               ├── SignatureServerInjector.kt
│                               └── serialisation/
│                                   └── ServerResponseBean.kt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── ide-plugin/
│   ├── CHANGELOG.md
│   ├── build.gradle
│   ├── resources/
│   │   └── META-INF/
│   │       └── plugin.xml
│   └── src/
│       ├── com/
│       │   └── intellij/
│       │       └── execution/
│       │           └── executors/
│       │               ├── CollectStateExecutor.kt
│       │               └── RunWithTypeTrackerExecutor.java
│       ├── org/
│       │   └── jetbrains/
│       │       └── plugins/
│       │           └── ruby/
│       │               ├── IdePluginLogger.kt
│       │               ├── PluginResourceUtil.java
│       │               ├── RubyDynamicCodeInsightPluginInjector.kt
│       │               ├── ancestorsextractor/
│       │               │   ├── AncestorsExtractor.kt
│       │               │   └── RailsConsoleRunner.kt
│       │               ├── ruby/
│       │               │   ├── actions/
│       │               │   │   ├── ExportAncestorsActions.kt
│       │               │   │   ├── ExportAncesttorsDiffAction.kt
│       │               │   │   ├── ExportFileActionBase.kt
│       │               │   │   └── ImportExportContractsAction.kt
│       │               │   ├── codeInsight/
│       │               │   │   ├── ProjectLifecycleListenerImpl.kt
│       │               │   │   ├── RubyDynamicCodeInsightPluginAppLifecyctlListener.kt
│       │               │   │   ├── TrackerDataLoader.kt
│       │               │   │   ├── stateTracker/
│       │               │   │   │   ├── ClassHierarchySymbolProvider.kt
│       │               │   │   │   └── RubyClassHierarchyWithCaching.kt
│       │               │   │   ├── symbols/
│       │               │   │   │   └── structure/
│       │               │   │   │       └── RMethodSyntheticSymbol.java
│       │               │   │   └── types/
│       │               │   │       ├── RubyCollectStateRunner.kt
│       │               │   │       ├── RubyRunWithTypeTrackerRunner.kt
│       │               │   │       └── RubyTypeProvider.kt
│       │               │   ├── intentions/
│       │               │   │   ├── AddContractAnnotationIntention.java
│       │               │   │   ├── BaseRubyMethodIntentionAction.kt
│       │               │   │   └── RemoveCollectedInfoIntention.kt
│       │               │   ├── persistent/
│       │               │   │   └── TypeInferenceDirectory.kt
│       │               │   └── run/
│       │               │       └── configuration/
│       │               │           ├── CollectExecSettings.java
│       │               │           └── RunWithTypeTrackerRunConfigurationExtension.java
│       │               ├── settings/
│       │               │   ├── RubyTypeContractsConfigurable.kt
│       │               │   ├── RubyTypeContractsConfigurableUI.kt
│       │               │   └── RubyTypeContractsSettings.kt
│       │               └── util/
│       │                   └── SignatureServerUtil.kt
│       └── test/
│           ├── java/
│           │   ├── CallStatCompletionTest.kt
│           │   └── org/
│           │       └── jetbrains/
│           │           └── plugins/
│           │               └── ruby/
│           │                   └── ruby/
│           │                       └── actions/
│           │                           └── ImportExportTests.kt
│           └── testData/
│               ├── anonymous_module_method_call_test.rb
│               ├── call_info_of_nested_class_test.rb
│               ├── duplicates_in_callinfo_table_test.rb
│               ├── forget_call_info_when_arguments_number_changed_test_part_1.rb
│               ├── forget_call_info_when_arguments_number_changed_test_part_2.rb
│               ├── in_project_root_test/
│               │   ├── gem_like.rb
│               │   └── in_project_root_test.rb
│               ├── merge_test1.rb
│               ├── merge_test1_to_run.rb
│               ├── merge_test2.rb
│               ├── merge_test2_to_run.rb
│               ├── method_without_parameters_test.rb
│               ├── multiple_execution_test1.rb
│               ├── multiple_execution_test2.rb
│               ├── multiple_execution_test2_to_run.rb
│               ├── ref_links_test.rb
│               ├── ref_links_test_to_run.rb
│               ├── ruby_exec_part_2.rb
│               ├── ruby_exec_test.rb
│               ├── sample_kw_test.rb
│               ├── sample_kw_test_to_run.rb
│               ├── sample_test.rb
│               ├── sample_test_to_run.rb
│               ├── save_types_between_launches_test_part_1.rb
│               ├── save_types_between_launches_test_part_2.rb
│               ├── simple_call_info_collection_test.rb
│               ├── simple_call_info_collection_test_multiple_functions_test.rb
│               ├── simple_call_info_collection_with_multiple_arguments_test.rb
│               └── top_level_methods_call_info_collection_test.rb
├── ruby-call-signature/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── org/
│       │           └── jetbrains/
│       │               └── ruby/
│       │                   └── codeInsight/
│       │                       └── types/
│       │                           └── signature/
│       │                               ├── CallInfo.kt
│       │                               ├── ClassInfo.kt
│       │                               ├── GemInfo.kt
│       │                               ├── MethodInfo.kt
│       │                               ├── ParameterInfo.java
│       │                               ├── RSignatureContract.java
│       │                               ├── RSignatureContractContainer.kt
│       │                               ├── RSignatureContractNode.java
│       │                               ├── RTuple.java
│       │                               ├── SignatureContract.kt
│       │                               ├── SignatureInfo.kt
│       │                               ├── contractTransition/
│       │                               │   ├── ContractTransition.java
│       │                               │   ├── ReferenceContractTransition.java
│       │                               │   ├── TransitionHelper.java
│       │                               │   └── TypedContractTransition.java
│       │                               └── serialization/
│       │                                   ├── MethodInfoSerialization.kt
│       │                                   ├── RmcDirectory.kt
│       │                                   ├── SignatureContractSerialization.kt
│       │                                   └── TestSerialization.kt
│       └── test/
│           └── java/
│               └── org/
│                   └── jetbrains/
│                       └── ruby/
│                           └── codeInsight/
│                               └── types/
│                                   └── signature/
│                                       ├── GemInfoFromPathTest.kt
│                                       ├── SignatureContractMergeTest.kt
│                                       ├── SignatureContractSerializationTest.kt
│                                       └── SignatureContractTestBase.kt
├── settings.gradle
├── signature-viewer/
│   ├── build.gradle
│   └── src/
│       └── org/
│           └── jetbrains/
│               └── ruby/
│                   └── runtime/
│                       └── signature/
│                           ├── DBViewer.kt
│                           ├── EraseLocation.kt
│                           ├── SignatureExport.kt
│                           ├── SignatureImport.kt
│                           ├── SignatureViewer.kt
│                           └── SplitDB.kt
├── state-tracker/
│   ├── build.gradle
│   └── src/
│       ├── main/
│       │   └── java/
│       │       └── org/
│       │           └── jetbrains/
│       │               └── ruby/
│       │                   └── stateTracker/
│       │                       ├── RubyClassHierarchy.kt
│       │                       └── RubyClassHierarchyLoader.kt
│       └── test/
│           └── java/
│               ├── org/
│               │   └── jetbrains/
│               │       └── ruby/
│               │           └── stateTracker/
│               │               ├── RubyClassHierarchyLoaderNonStandardModuleTypeTest.kt
│               │               └── RubyClassHierarchyLoaderTest.kt
│               └── testData/
│                   ├── classes.json
│                   └── non-standard-module-type.json
└── storage-server-api/
    ├── build.gradle
    └── src/
        ├── main/
        │   └── java/
        │       └── org/
        │           └── jetbrains/
        │               └── ruby/
        │                   └── codeInsight/
        │                       └── types/
        │                           ├── signature/
        │                           │   └── serialization/
        │                           │       └── BlobSerialization.kt
        │                           └── storage/
        │                               └── server/
        │                                   ├── DatabaseProvider.kt
        │                                   ├── RSignatureProvider.java
        │                                   ├── RSignatureStorage.java
        │                                   ├── StorageException.java
        │                                   ├── impl/
        │                                   │   ├── IntIdTableWithPossibleDependency.kt
        │                                   │   ├── RSignatureProviderImpl.kt
        │                                   │   ├── RowConversions.kt
        │                                   │   └── Schema.kt
        │                                   └── testutil/
        │                                       └── DatabaseTestUtils.kt
        └── test/
            └── java/
                └── org/
                    └── jetbrains/
                        └── ruby/
                            └── codeInsight/
                                └── types/
                                    └── storage/
                                        └── server/
                                            └── impl/
                                                └── RSignatureProviderTest.kt
Download .txt
SYMBOL INDEX (380 symbols across 59 files)

FILE: arg_scanner/ext/arg_scanner/arg_scanner.c
  type rb_trace_arg_t (line 30) | typedef struct rb_trace_arg_struct rb_trace_arg_t;
  type call_info_t (line 48) | typedef struct
  type signature_t (line 54) | typedef struct
  function call_info_t_free (line 98) | static void call_info_t_free(call_info_t s)
  function signature_t_free (line 103) | static void signature_t_free(signature_t *s)
  function signature_t_free_partially (line 115) | static void signature_t_free_partially(signature_t *s)
  function gint (line 125) | static gint
  function gint (line 146) | static gint
  function start_with (line 166) | inline int start_with(const char *str, const char *prefix) {
  function file_exists (line 184) | static int file_exists(const char *file_path) {
  function VALUE (line 188) | static VALUE init(VALUE self, VALUE pipe_file_path, VALUE buffering,
  function Init_arg_scanner (line 221) | void Init_arg_scanner() {
  function push_to_call_stack (line 244) | inline void push_to_call_stack(signature_t *signature) {
  function signature_t (line 248) | inline signature_t *pop_from_call_stack() {
  function is_call_stack_empty (line 260) | inline int is_call_stack_empty() {
  function signature_t (line 267) | inline signature_t *top_of_call_stack() {
  function rb_control_frame_t (line 274) | rb_control_frame_t *
  function VALUE (line 286) | static VALUE exit_from_handle_call_skipping_call() {
  function VALUE (line 291) | static VALUE
  function VALUE (line 352) | static VALUE
  function call_info_t (line 408) | static call_info_t
  function contains (line 555) | static int contains(const char *const *container, const char *element) {
  function join_kw_names_and_types (line 583) | static int join_kw_names_and_types(VALUE key, VALUE val, VALUE ignored) {
  function VALUE (line 775) | static VALUE
  function VALUE (line 791) | static VALUE
  function is_call_info_needed (line 823) | static bool
  function VALUE (line 839) | static VALUE
  function VALUE (line 849) | static VALUE

FILE: arg_scanner/ext/arg_scanner/extconf.rb
  class NilClass (line 8) | class NilClass
    method empty? (line 9) | def empty?; true; end
  function real_have_header (line 13) | def real_have_header(header_name)

FILE: arg_scanner/lib/arg_scanner.rb
  type ArgScanner (line 6) | module ArgScanner

FILE: arg_scanner/lib/arg_scanner/options.rb
  type ArgScanner (line 3) | module ArgScanner
    function set_env (line 14) | def OPTIONS.set_env

FILE: arg_scanner/lib/arg_scanner/require_all.rb
  type RequireAll (line 22) | module RequireAll
    function require_all (line 49) | def require_all(*args)
    function require_rel (line 161) | def require_rel(*paths)
    function load_all (line 173) | def load_all(*paths)
    function load_rel (line 179) | def load_rel(*paths)
    function autoload_all (line 225) | def autoload_all(*paths)
    function autoload_rel (line 239) | def autoload_rel(*paths)
    function __autoload (line 257) | def __autoload(file, full_path, options)

FILE: arg_scanner/lib/arg_scanner/state_tracker.rb
  type ArgScanner (line 6) | module ArgScanner
    class StateTracker (line 7) | class StateTracker
      method initialize (line 8) | def initialize
      method require_extra_libs (line 22) | def require_extra_libs
      method print_json (line 33) | def print_json(file)
      method parse_top_level_constants (line 43) | def parse_top_level_constants
      method get_extra_methods (line 56) | def get_extra_methods(value)
      method method_to_json (line 60) | def method_to_json(method)
      method module_to_json (line 74) | def module_to_json(mod)
      method modules_to_json (line 90) | def modules_to_json

FILE: arg_scanner/lib/arg_scanner/type_tracker.rb
  type ArgScanner (line 8) | module ArgScanner
    class TypeTrackerPerformanceMonitor (line 10) | class TypeTrackerPerformanceMonitor
      method initialize (line 11) | def initialize
      method on_call (line 21) | def on_call
      method on_return (line 25) | def on_return
      method on_handled_return (line 36) | def on_handled_return
    class TypeTracker (line 51) | class TypeTracker
      method initialize (line 54) | def initialize

FILE: arg_scanner/lib/arg_scanner/version.rb
  type ArgScanner (line 1) | module ArgScanner

FILE: arg_scanner/lib/arg_scanner/workspace.rb
  type ArgScanner (line 1) | module ArgScanner
    class Workspace (line 2) | class Workspace
      method initialize (line 4) | def initialize
      method on_process_start (line 9) | def on_process_start
      method open_output_json (line 14) | def open_output_json(prefix)
      method on_process_exit (line 22) | def on_process_exit

FILE: arg_scanner/test/helper.rb
  class TestTypeTracker (line 5) | class TestTypeTracker
    method initialize (line 11) | def initialize
    method enable (line 25) | def enable(*args, &b)
    method signatures (line 29) | def signatures

FILE: arg_scanner/test/test_args_info.rb
  class TestArgsInfoWrapper (line 5) | class TestArgsInfoWrapper
    method foo (line 7) | def foo(a); end
    method foo2 (line 9) | def foo2(a, b = 1); end
    method foo3 (line 11) | def foo3(**rest); end
    method foo4 (line 13) | def foo4(kw: :symbol, **rest1); end
    method foo5 (line 15) | def foo5(kw:, **rest); end
    method foo6 (line 17) | def foo6(a, *rest, b); end
    method initialize (line 19) | def initialize
  class TestArgsInfo (line 33) | class TestArgsInfo < Test::Unit::TestCase
    method setup (line 40) | def setup
    method teardown (line 45) | def teardown
    method test_simple_kwrest (line 49) | def test_simple_kwrest
    method test_empty_kwrest (line 57) | def test_empty_kwrest
    method test_req_and_opt_arg (line 65) | def test_req_and_opt_arg
    method test_optkw_and_empty_kwrest (line 74) | def test_optkw_and_empty_kwrest
    method test_reqkw_and_empty_kwrest (line 82) | def test_reqkw_and_empty_kwrest
    method test_reqkw_and_kwrest (line 90) | def test_reqkw_and_kwrest
    method test_optkw_and_kwrest (line 98) | def test_optkw_and_kwrest
    method test_optkw_passed_and_kwrest (line 106) | def test_optkw_passed_and_kwrest
    method test_rest (line 114) | def test_rest
    method test_empty_rest (line 124) | def test_empty_rest

FILE: arg_scanner/test/test_call_info.rb
  class TestCallInfoWrapper (line 4) | class TestCallInfoWrapper
    method sqr (line 6) | def sqr(z1 = 10, z2 = 11, z3 = 13, z4 = 14, z5, z6, z7, z8, y: '0', x:...
    method sqr2 (line 10) | def sqr2(z0, z1 = 2, z2 = 10, z3 = 2, z4 = 0, y: 1, x: 30, z: '40')
    method foo (line 14) | def foo(a, b, c, *d, e)
    method foo2 (line 18) | def foo2(*args)
    method foo3 (line 22) | def foo3(b: 2, c: '3', **args)
    method foo4 (line 26) | def foo4(b: 2, c:, d: "1", dd: 1, ddd: '111', **args)
    method foo5 (line 30) | def foo5(b)
  class TestCallInfo (line 36) | class TestCallInfo < Test::Unit::TestCase
    method setup (line 42) | def setup
    method teardown (line 47) | def teardown
    method test_simple (line 51) | def test_simple
    method test_simple_req_arg (line 62) | def test_simple_req_arg
    method test_simple_kw (line 70) | def test_simple_kw
    method test_rest (line 82) | def test_rest
    method test_post_and_rest (line 93) | def test_post_and_rest
    method test_kwrest (line 105) | def test_kwrest
    method test_rest_and_reqkw_args (line 117) | def test_rest_and_reqkw_args

FILE: arg_scanner/test/test_state_tracker.rb
  class StateTrackerTest (line 6) | class StateTrackerTest < Test::Unit::TestCase
    method startup (line 10) | def startup
    method test_has_struct (line 26) | def test_has_struct
    method test_symbol_is_fine (line 30) | def test_symbol_is_fine
    method test_loaded_path_is_fine (line 44) | def test_loaded_path_is_fine
    method test_constant_is_fine (line 49) | def test_constant_is_fine
    method get_class_method (line 59) | def get_class_method(symbol, name)
    method get_instance_method (line 63) | def get_instance_method(symbol, name)
    method get_class_with_name (line 67) | def get_class_with_name(name)
    method get_named_entity (line 71) | def get_named_entity(obj, index, name)

FILE: ide-plugin/src/com/intellij/execution/executors/RunWithTypeTrackerExecutor.java
  class RunWithTypeTrackerExecutor (line 15) | public class RunWithTypeTrackerExecutor extends Executor {
    method RunWithTypeTrackerExecutor (line 22) | public RunWithTypeTrackerExecutor() {
    method getToolWindowId (line 29) | @Override
    method getToolWindowIcon (line 34) | @Override
    method getIcon (line 39) | @NotNull
    method getDisabledIcon (line 45) | @Nullable
    method getDescription (line 51) | @NotNull
    method getActionName (line 57) | @NotNull
    method getId (line 63) | @NotNull
    method getStartActionText (line 69) | @NotNull
    method getContextActionId (line 74) | @NotNull
    method getHelpId (line 80) | @Nullable
    method getStartActionText (line 86) | @NotNull
    method escapeMnemonicsInConfigurationName (line 94) | @NotNull

FILE: ide-plugin/src/org/jetbrains/plugins/ruby/PluginResourceUtil.java
  class PluginResourceUtil (line 10) | public final class PluginResourceUtil {
    method PluginResourceUtil (line 13) | private PluginResourceUtil() {
    method getPluginResourcesPath (line 16) | @NotNull

FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/symbols/structure/RMethodSyntheticSymbol.java
  class RMethodSyntheticSymbol (line 35) | public class RMethodSyntheticSymbol extends SymbolImpl implements RMetho...
    method RMethodSyntheticSymbol (line 45) | public RMethodSyntheticSymbol(@NotNull final Project project,
    method toArgsInfo (line 62) | private List<ArgumentInfo> toArgsInfo(List<RubyMethod.ArgInfo> argumen...
    method toArgumentInfo (line 66) | private static ArgumentInfo toArgumentInfo(final @NotNull RubyMethod.A...
    method getName (line 96) | @NotNull
    method getArgumentInfos (line 103) | @Override
    method getArgumentInfos (line 109) | @Override
    method getCallType (line 115) | @Nullable
    method getArgsPresentation (line 121) | @NotNull
    method isSynthetic (line 131) | @Override
    method getVisibility (line 136) | @NotNull
    method getPsiElement (line 142) | @Override
    method getAllDeclarations (line 161) | @NotNull
    method calcElement (line 168) | @Nullable
    class MyFakeElement (line 203) | private static class MyFakeElement extends RangeInDocumentFakePsiEleme...
      method MyFakeElement (line 207) | MyFakeElement(@NotNull PsiElement parent, @NotNull TextRange rangeIn...
      method getName (line 212) | @NotNull
      method setName (line 218) | @Override

FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/intentions/AddContractAnnotationIntention.java
  class AddContractAnnotationIntention (line 36) | public class AddContractAnnotationIntention extends BaseRubyMethodIntent...
    method getFamilyName (line 37) | @Nls(capitalization = Nls.Capitalization.Sentence)
    method isAvailable (line 44) | public boolean isAvailable(@NotNull final Project project, final @NotN...
    method invoke (line 67) | public void invoke(@NotNull final Project project, final Editor editor...
    method createMethodInfo (line 90) | @Nullable
    method dfs (line 102) | private void dfs(@NotNull SignatureNode v, @NotNull StringBuilder curr...
    method addEdge (line 119) | private StringBuilder addEdge(@NotNull StringBuilder currentLine, Stri...
    method edgeToStr (line 123) | private String edgeToStr(ContractTransition edge) {
    method getTextByRubyFunctionNamePsiElement (line 131) | @NotNull

FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/run/configuration/CollectExecSettings.java
  class CollectExecSettings (line 7) | public class CollectExecSettings {
    method isArgScannerEnabled (line 18) | public boolean isArgScannerEnabled() {
    method isStateTrackerEnabled (line 22) | public boolean isStateTrackerEnabled() {
    method setStateTrackerEnabled (line 26) | public void setStateTrackerEnabled(boolean myStateTrackerEnabled) {
    method setArgScannerEnabled (line 30) | public void setArgScannerEnabled(boolean myArgScannerEnabled) {
    method isTypeTrackerEnabled (line 34) | public boolean isTypeTrackerEnabled() {
    method setTypeTrackerEnabled (line 38) | public void setTypeTrackerEnabled(boolean myTypeTrackerEnabled) {
    method getOutputDirectory (line 42) | @Nullable
    method setReturnTypeTrackerPath (line 47) | public void setReturnTypeTrackerPath(final @Nullable String path) {
    method getFrom (line 51) | @NotNull
    method putTo (line 57) | public static void putTo(@NotNull final AbstractRubyRunConfiguration c...
    method createSettings (line 62) | public static CollectExecSettings createSettings(final boolean argScan...

FILE: ide-plugin/src/org/jetbrains/plugins/ruby/ruby/run/configuration/RunWithTypeTrackerRunConfigurationExtension.java
  class RunWithTypeTrackerRunConfigurationExtension (line 37) | public class RunWithTypeTrackerRunConfigurationExtension extends RubyRun...
    method readExternal (line 58) | @Override
    method getEditorTitle (line 64) | @Nullable
    method isApplicableFor (line 70) | @Override
    method isEnabledFor (line 75) | @Override
    method patchCommandLine (line 82) | @Override
    method validateConfiguration (line 122) | @Override
    method getRequireKeyForGem (line 157) | @Nullable
    method attachToProcess (line 171) | @Override
    method checkForPidFiles (line 187) | private boolean checkForPidFiles(final @NotNull File directory) {
    method waitAllProcess (line 192) | private void waitAllProcess(final @NotNull File directory,
    method processStateTrackerResult (line 204) | private void processStateTrackerResult(final @NotNull CollectExecSetti...

FILE: ide-plugin/src/test/testData/anonymous_module_method_call_test.rb
  type A (line 1) | module A
    function foo (line 2) | def self.foo(a, b)

FILE: ide-plugin/src/test/testData/call_info_of_nested_class_test.rb
  type M (line 1) | module M
    class A (line 2) | class A
      method foo (line 3) | def foo(a)

FILE: ide-plugin/src/test/testData/duplicates_in_callinfo_table_test.rb
  function foo (line 1) | def foo(a)

FILE: ide-plugin/src/test/testData/forget_call_info_when_arguments_number_changed_test_part_1.rb
  class A (line 1) | class A
    method foo (line 2) | def foo(a)

FILE: ide-plugin/src/test/testData/forget_call_info_when_arguments_number_changed_test_part_2.rb
  class A (line 1) | class A
    method foo (line 2) | def foo(a, b)

FILE: ide-plugin/src/test/testData/in_project_root_test/gem_like.rb
  function catch (line 1) | def catch(a); end
  function dont_catch_2 (line 3) | def dont_catch_2(a); end
  function catch_2 (line 5) | def catch_2(a)
  function dont_catch_3 (line 9) | def dont_catch_3(&a)
  function catch_3 (line 13) | def catch_3(&a)

FILE: ide-plugin/src/test/testData/in_project_root_test/in_project_root_test.rb
  function foo (line 7) | def foo(a); end

FILE: ide-plugin/src/test/testData/merge_test1.rb
  class A (line 1) | class A
  class C (line 5) | class C
  class B1 (line 9) | class B1
    method test1 (line 10) | def test1
    method test2 (line 14) | def test2
  class B2 (line 19) | class B2
    method test3 (line 20) | def test3
    method test4 (line 24) | def test4

FILE: ide-plugin/src/test/testData/merge_test1_to_run.rb
  class A (line 1) | class A
  class C (line 5) | class C
  class B1 (line 9) | class B1
    method test1 (line 10) | def test1
    method test2 (line 14) | def test2
  class B2 (line 19) | class B2
    method test3 (line 20) | def test3
    method test4 (line 24) | def test4

FILE: ide-plugin/src/test/testData/merge_test2.rb
  class A (line 1) | class A
  class C (line 5) | class C
  class B1 (line 9) | class B1
    method test1 (line 10) | def test1
    method test2 (line 14) | def test2
  class B2 (line 19) | class B2
    method test3 (line 20) | def test3
    method test4 (line 24) | def test4

FILE: ide-plugin/src/test/testData/merge_test2_to_run.rb
  class A (line 1) | class A
  class C (line 5) | class C
  class B1 (line 9) | class B1
    method test1 (line 10) | def test1
    method test2 (line 14) | def test2
  class B2 (line 19) | class B2
    method test3 (line 20) | def test3
    method test4 (line 24) | def test4

FILE: ide-plugin/src/test/testData/method_without_parameters_test.rb
  function foo (line 1) | def foo

FILE: ide-plugin/src/test/testData/multiple_execution_test1.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/multiple_execution_test2.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/multiple_execution_test2_to_run.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/ref_links_test.rb
  class A (line 1) | class A
  class B (line 6) | class B
    method test1 (line 7) | def test1
    method test2 (line 11) | def test2

FILE: ide-plugin/src/test/testData/ref_links_test_to_run.rb
  class A (line 1) | class A
  class B (line 5) | class B
    method test1 (line 6) | def test1
    method test2 (line 10) | def test2

FILE: ide-plugin/src/test/testData/ruby_exec_part_2.rb
  function bar (line 1) | def bar(a); end

FILE: ide-plugin/src/test/testData/ruby_exec_test.rb
  function foo (line 1) | def foo(a); end

FILE: ide-plugin/src/test/testData/sample_kw_test.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/sample_kw_test_to_run.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/sample_test.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/sample_test_to_run.rb
  class A (line 3) | class A
  class C (line 7) | class C
  class B (line 11) | class B
    method test1 (line 12) | def test1
    method test2 (line 16) | def test2

FILE: ide-plugin/src/test/testData/save_types_between_launches_test_part_1.rb
  class A (line 1) | class A
    method foo (line 2) | def foo(a)

FILE: ide-plugin/src/test/testData/save_types_between_launches_test_part_2.rb
  class A (line 1) | class A
    method foo (line 2) | def foo(a)

FILE: ide-plugin/src/test/testData/simple_call_info_collection_test.rb
  class AClass (line 1) | class AClass
    method foo (line 2) | def foo(a)

FILE: ide-plugin/src/test/testData/simple_call_info_collection_test_multiple_functions_test.rb
  class A (line 1) | class A
    method foo (line 2) | def foo(a, b)
    method bar (line 6) | def bar(a)

FILE: ide-plugin/src/test/testData/simple_call_info_collection_with_multiple_arguments_test.rb
  class AClass (line 1) | class AClass
    method foo (line 2) | def foo(a, b)

FILE: ide-plugin/src/test/testData/top_level_methods_call_info_collection_test.rb
  function foo (line 1) | def foo(a, b)

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/ParameterInfo.java
  class ParameterInfo (line 5) | public class ParameterInfo {
    method ParameterInfo (line 11) | public ParameterInfo(@NotNull final String name, @NotNull final Type m...
    method getName (line 16) | @NotNull
    method getModifier (line 21) | @NotNull
    method isNamedParameter (line 26) | public boolean isNamedParameter() {
    method equals (line 30) | @Override
    method hashCode (line 42) | @Override
    type Type (line 59) | public enum Type {

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RSignatureContract.java
  class RSignatureContract (line 13) | public class RSignatureContract implements SignatureContract {
    method RSignatureContract (line 24) | public RSignatureContract(@NotNull RTuple tuple) {
    method RSignatureContract (line 38) | public RSignatureContract(@NotNull List<ParameterInfo> argsInfo,
    method RSignatureContract (line 50) | private RSignatureContract(@NotNull SignatureContract source) {
    method getStartNode (line 93) | @NotNull
    method getArgsInfo (line 99) | @NotNull
    method getNodeCount (line 105) | public int getNodeCount() {
    method copy (line 109) | @NotNull
    method addRTuple (line 134) | public synchronized boolean addRTuple(@NotNull RTuple tuple) {
    method minimize (line 169) | synchronized void minimize() {
    method getLevels (line 221) | @TestOnly
    method AddToBfsQueueAndUse (line 227) | private void AddToBfsQueueAndUse(@NotNull SignatureNode oldNode, @NotN...
    method mergeWith (line 239) | public synchronized boolean mergeWith(@NotNull SignatureContract addit...
    method createNodeAndAddToLevels (line 304) | @Nullable
    method mergeMutably (line 321) | @Nullable
    class Immutable (line 334) | private static class Immutable implements SignatureContract {
      method Immutable (line 343) | private Immutable(@NotNull SignatureNode startNode, int nodeCount, @...
      method getNodeCount (line 349) | @Override
      method getStartNode (line 354) | @NotNull
      method getArgsInfo (line 360) | @NotNull
    class PairOfNodes (line 367) | private static class PairOfNodes {
      method pairGoByTransition (line 373) | @NotNull
      method PairOfNodes (line 378) | PairOfNodes(@NotNull SignatureNode node1, @NotNull SignatureNode nod...

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RSignatureContractNode.java
  class RSignatureContractNode (line 9) | public class RSignatureContractNode implements SignatureNode {
    method RSignatureContractNode (line 14) | public RSignatureContractNode() {
    method addLink (line 18) | public void addLink(final @NotNull ContractTransition transition, @Not...
    method getTransitions (line 22) | @NotNull

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RTuple.java
  class RTuple (line 7) | public class RTuple {
    method RTuple (line 19) | public RTuple(@NotNull final MethodInfo methodInfo,
    method getMethodInfo (line 29) | @NotNull
    method getArgsInfo (line 34) | @NotNull
    method getArgsTypes (line 39) | @NotNull
    method getReturnTypeName (line 44) | @NotNull
    method equals (line 49) | @Override
    method hashCode (line 62) | @Override

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/ContractTransition.java
  type ContractTransition (line 8) | public interface ContractTransition {
    method getValue (line 16) | @NotNull

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/ReferenceContractTransition.java
  class ReferenceContractTransition (line 9) | public class ReferenceContractTransition implements ContractTransition {
    method ReferenceContractTransition (line 13) | public ReferenceContractTransition(int mask) {
    method getValue (line 17) | @NotNull
    method getMask (line 41) | public int getMask() {
    method equals (line 45) | @Override
    method hashCode (line 55) | @Override

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/TransitionHelper.java
  class TransitionHelper (line 7) | public class TransitionHelper {
    method TransitionHelper (line 8) | private TransitionHelper() {
    method calculateTransition (line 11) | @NotNull
    method getNewMask (line 21) | private static int getNewMask(@NotNull List<String> argsTypes, int arg...

FILE: ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/TypedContractTransition.java
  class TypedContractTransition (line 9) | public class TypedContractTransition implements ContractTransition {
    method TypedContractTransition (line 14) | public TypedContractTransition(@NotNull String type) {
    method getValue (line 18) | @NotNull
    method getType (line 24) | @NotNull
    method equals (line 29) | @Override
    method hashCode (line 39) | @Override

FILE: storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/RSignatureProvider.java
  type RSignatureProvider (line 27) | public interface RSignatureProvider {
    method getRegisteredGems (line 28) | @NotNull
    method getClosestRegisteredGem (line 31) | @Nullable
    method getRegisteredClasses (line 34) | @NotNull
    method getAllClassesWithFQN (line 37) | @NotNull
    method getRegisteredMethods (line 40) | @NotNull
    method getRegisteredCallInfos (line 47) | @NotNull
    method getSignature (line 50) | @Nullable
    method deleteSignature (line 53) | void deleteSignature(@NotNull MethodInfo method) throws StorageException;
    method putSignature (line 55) | void putSignature(@NotNull SignatureInfo signatureInfo) throws Storage...

FILE: storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/RSignatureStorage.java
  type RSignatureStorage (line 9) | public interface RSignatureStorage<T extends RSignatureStorage.Packet> e...
    method readPacket (line 11) | default void readPacket(@NotNull T packet) throws StorageException {
    method formPackets (line 26) | @NotNull
    class ExportDescriptor (line 29) | class ExportDescriptor {
      method ExportDescriptor (line 35) | public ExportDescriptor(boolean include, @NotNull Collection<GemInfo...
      method isInclude (line 40) | public boolean isInclude() {
      method getGemsToIncludeOrExclude (line 44) | @NotNull
    type Packet (line 50) | interface Packet {
      method getSignatures (line 51) | Collection<SignatureInfo> getSignatures();

FILE: storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/StorageException.java
  class StorageException (line 3) | @SuppressWarnings("unused")
    method StorageException (line 5) | public StorageException() {
    method StorageException (line 8) | public StorageException(String message) {
    method StorageException (line 12) | public StorageException(String message, Throwable cause) {
    method StorageException (line 16) | public StorageException(Throwable cause) {
    method StorageException (line 20) | public StorageException(String message, Throwable cause, boolean enabl...
Condensed preview — 160 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (450K chars).
[
  {
    "path": ".gitignore",
    "chars": 90,
    "preview": "/build/\nout/\n*/build/\n.gradle\n\n.idea/\n\n**/*.iml\n**/.rakeTasks\narg_scanner/arg_scanner.iml\n"
  },
  {
    "path": ".travis.yml",
    "chars": 1028,
    "preview": "language: ruby\ndist: trusty\nos:\n  - linux\n#  - osx\n\nrvm:\n  - 2.3.3\n  - 2.4.2\n  - ruby-head\n\nmatrix:\n  fast_finish: true\n"
  },
  {
    "path": "FEATURES.md",
    "chars": 599,
    "preview": "# ruby-type-inference features\n\nThis doc contains `ruby-type-inference` features which can be useful \nfor you after runn"
  },
  {
    "path": "LICENSE",
    "chars": 11351,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 5047,
    "preview": "Automated Type Contracts Generation [![JetBrains incubator project](http://jb.gg/badges/incubator.svg)](https://confluen"
  },
  {
    "path": "arg_scanner/.gitignore",
    "chars": 116,
    "preview": "*.iml\n\n.bundle/\n.yardoc\nGemfile.lock\n_yardoc/\ncoverage/\ndoc/\npkg/\nspec/reports/\ntmp/\n*.bundle\n*.so\n*.o\n*.a\nmkmf.log\n"
  },
  {
    "path": "arg_scanner/Gemfile",
    "chars": 134,
    "preview": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in arg_scanner.gemspec\ngemspec\n\ngroup :test do\n  gem 't"
  },
  {
    "path": "arg_scanner/LICENSE.txt",
    "chars": 1076,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017 JetBrains\n\nPermission is hereby granted, free of charge, to any person obtaini"
  },
  {
    "path": "arg_scanner/README.md",
    "chars": 1939,
    "preview": "# ArgScanner [![Gem Version](https://badge.fury.io/rb/arg_scanner.svg)](https://badge.fury.io/rb/arg_scanner)\n\n`arg_scan"
  },
  {
    "path": "arg_scanner/Rakefile",
    "chars": 486,
    "preview": "require \"bundler/gem_tasks\"\nrequire \"rake/extensiontask\"\nrequire 'rake/testtask'\n\nBASE_TEST_FILE_LIST = Dir['test/**/tes"
  },
  {
    "path": "arg_scanner/arg_scanner.gemspec",
    "chars": 1584,
    "preview": "# coding: utf-8\nlib = File.expand_path('../lib', __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequi"
  },
  {
    "path": "arg_scanner/bin/arg-scanner",
    "chars": 2403,
    "preview": "#!/usr/bin/env ruby\n\nrequire 'optparse'\nrequire 'arg_scanner/options'\nrequire 'arg_scanner/version'\n\noptions = ArgScanne"
  },
  {
    "path": "arg_scanner/bin/console",
    "chars": 336,
    "preview": "#!/usr/bin/env ruby\n\nrequire \"bundler/setup\"\nrequire \"arg_scanner\"\n\n# You can add fixtures and/or initialization code he"
  },
  {
    "path": "arg_scanner/bin/rubymine-type-tracker",
    "chars": 2146,
    "preview": "#!/usr/bin/env ruby\n# This is small script for launching type tracker under RubyMine's provided server. Acts like arg-sc"
  },
  {
    "path": "arg_scanner/bin/setup",
    "chars": 131,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\nIFS=$'\\n\\t'\nset -vx\n\nbundle install\n\n# Do any other automated setup that you need "
  },
  {
    "path": "arg_scanner/ext/arg_scanner/arg_scanner.c",
    "chars": 27612,
    "preview": "#include \"arg_scanner.h\"\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <asser"
  },
  {
    "path": "arg_scanner/ext/arg_scanner/arg_scanner.h",
    "chars": 173,
    "preview": "#ifndef ARG_SCANNER_H\n#define ARG_SCANNER_H 1\n\n#include \"ruby.h\"\n#include \"vm_core.h\"\n#include \"version.h\"\n#include \"ise"
  },
  {
    "path": "arg_scanner/ext/arg_scanner/extconf.rb",
    "chars": 2428,
    "preview": "require \"mkmf\"\n\nRbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']\n\nrequire \"debase/ruby_core_source\"\nrequire \"nat"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/options.rb",
    "chars": 1027,
    "preview": "require 'ostruct'\n\nmodule ArgScanner\n  OPTIONS = OpenStruct.new(\n      :enable_type_tracker => ENV['ARG_SCANNER_ENABLE_T"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/require_all.rb",
    "chars": 10072,
    "preview": "# Copyright (c) 2009 Jarmo Pertman\n\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of "
  },
  {
    "path": "arg_scanner/lib/arg_scanner/starter.rb",
    "chars": 469,
    "preview": "# starter.rb is loaded with \"ruby -r\" option from bin/arg-scanner\n# or by IDEA also with \"ruby -r\" option\n\nunless ENV[\"A"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/state_tracker.rb",
    "chars": 2657,
    "preview": "require \"set\"\nrequire_relative \"require_all\"\nrequire_relative \"workspace\"\n\n\nmodule ArgScanner\n  class StateTracker\n    d"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/type_tracker.rb",
    "chars": 2007,
    "preview": "require 'set'\nrequire 'socket'\nrequire 'singleton'\nrequire 'thread'\n\nrequire_relative 'options'\n\nmodule ArgScanner\n\n  cl"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/version.rb",
    "chars": 42,
    "preview": "module ArgScanner\n  VERSION = \"0.3.3\"\nend\n"
  },
  {
    "path": "arg_scanner/lib/arg_scanner/workspace.rb",
    "chars": 620,
    "preview": "module ArgScanner\n  class Workspace\n\n    def initialize\n      @dir = ENV[\"ARG_SCANNER_DIR\"] || \".\"\n      @pid_file = @di"
  },
  {
    "path": "arg_scanner/lib/arg_scanner.rb",
    "chars": 185,
    "preview": "require \"arg_scanner/version\"\nrequire \"arg_scanner/arg_scanner\"\nrequire \"arg_scanner/type_tracker\"\nrequire \"arg_scanner/"
  },
  {
    "path": "arg_scanner/test/helper.rb",
    "chars": 674,
    "preview": "$LOAD_PATH.unshift(File.dirname(__dir__) + '/../lib')\nrequire \"test-unit\"\nrequire \"arg_scanner\"\n\nclass TestTypeTracker\n "
  },
  {
    "path": "arg_scanner/test/test_args_info.rb",
    "chars": 3337,
    "preview": "#!/usr/bin/env ruby\nrequire File.expand_path(\"helper\", File.dirname(__FILE__))\nrequire 'date'\n\nclass TestArgsInfoWrapper"
  },
  {
    "path": "arg_scanner/test/test_call_info.rb",
    "chars": 3050,
    "preview": "#!/usr/bin/env ruby\nrequire File.expand_path(\"helper\", File.dirname(__FILE__))\n\nclass TestCallInfoWrapper\n\n  def sqr(z1 "
  },
  {
    "path": "arg_scanner/test/test_state_tracker.rb",
    "chars": 2243,
    "preview": "require 'test/unit'\nrequire 'tempfile'\nrequire 'fileutils'\nrequire 'json'\n\nclass StateTrackerTest < Test::Unit::TestCase"
  },
  {
    "path": "arg_scanner/util/state_filter.rb",
    "chars": 755,
    "preview": "#!/usr/bin/env ruby\n\nrequire 'json'\nrequire 'set'\n\nif ARGV.length < 3\n  puts(\"state_filter.rb <in-file> <out-file> [<lis"
  },
  {
    "path": "build.gradle",
    "chars": 1422,
    "preview": "buildscript {\n    repositories {\n        jcenter()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath \"o"
  },
  {
    "path": "common/build.gradle",
    "chars": 328,
    "preview": "buildscript {\n    repositories {\n        jcenter()\n    }\n\n    dependencies {\n        classpath \"org.jetbrains.kotlin:kot"
  },
  {
    "path": "common/src/main/java/org/jetbrains/ruby/codeInsight/Injector.kt",
    "chars": 717,
    "preview": "package org.jetbrains.ruby.codeInsight\n\n/**\n * Dependency injection mechanism\n */\ninterface Injector {\n    fun <T> getLo"
  },
  {
    "path": "common/src/main/java/org/jetbrains/ruby/codeInsight/Logger.kt",
    "chars": 87,
    "preview": "package org.jetbrains.ruby.codeInsight\n\ninterface Logger {\n    fun info(msg: String)\n}\n"
  },
  {
    "path": "common/src/main/java/org/jetbrains/ruby/codeInsight/PrintToStdoutLogger.kt",
    "chars": 439,
    "preview": "package org.jetbrains.ruby.codeInsight\n\nimport java.text.SimpleDateFormat\nimport java.util.*\n\nprivate val format = Simpl"
  },
  {
    "path": "contract-creator/build.gradle",
    "chars": 384,
    "preview": "sourceSets {\n    main.java.srcDirs = ['src']\n}\n\ndependencies {\n    compile project(':common')\n    compile project(':ruby"
  },
  {
    "path": "contract-creator/src/org/jetbrains/ruby/runtime/signature/server/SignatureServer.kt",
    "chars": 7840,
    "preview": "package org.jetbrains.ruby.runtime.signature.server\n\nimport com.google.gson.Gson\nimport com.google.gson.JsonParseExcepti"
  },
  {
    "path": "contract-creator/src/org/jetbrains/ruby/runtime/signature/server/SignatureServerInjector.kt",
    "chars": 350,
    "preview": "package org.jetbrains.ruby.runtime.signature.server\n\nimport org.jetbrains.ruby.codeInsight.Injector\nimport org.jetbrains"
  },
  {
    "path": "contract-creator/src/org/jetbrains/ruby/runtime/signature/server/serialisation/ServerResponseBean.kt",
    "chars": 4096,
    "preview": "package org.jetbrains.ruby.runtime.signature.server.serialisation\n\nimport org.jetbrains.ruby.codeInsight.types.signature"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 233,
    "preview": "#Wed Nov 07 19:25:40 MSK 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradle.properties",
    "chars": 346,
    "preview": "# Available idea versions:\n# https://www.jetbrains.com/intellij-repository/releases\n# https://www.jetbrains.com/intellij"
  },
  {
    "path": "gradlew",
    "chars": 5296,
    "preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up"
  },
  {
    "path": "gradlew.bat",
    "chars": 2260,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "ide-plugin/CHANGELOG.md",
    "chars": 574,
    "preview": "## 0.1.1 (15 Dec 2017)\n\n* (#17) Fix \"find usages\" action for dynamic symbols which resolve to text-based\n  definitions.\n"
  },
  {
    "path": "ide-plugin/build.gradle",
    "chars": 1607,
    "preview": "buildscript {\n    repositories {\n        maven { url 'https://dl.bintray.com/jetbrains/intellij-plugin-service' }\n    }\n"
  },
  {
    "path": "ide-plugin/resources/META-INF/plugin.xml",
    "chars": 4736,
    "preview": "<idea-plugin>\n    <id>org.jetbrains.ruby-runtime-stats</id>\n    <name>Ruby Dynamic Code Insight</name>\n    <vendor email"
  },
  {
    "path": "ide-plugin/src/com/intellij/execution/executors/CollectStateExecutor.kt",
    "chars": 1659,
    "preview": "package com.intellij.execution.executors\n\nimport com.intellij.execution.Executor\nimport com.intellij.icons.AllIcons\nimpo"
  },
  {
    "path": "ide-plugin/src/com/intellij/execution/executors/RunWithTypeTrackerExecutor.java",
    "chars": 2479,
    "preview": "package com.intellij.execution.executors;\n\nimport com.intellij.execution.Executor;\nimport com.intellij.icons.AllIcons;\ni"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/IdePluginLogger.kt",
    "chars": 277,
    "preview": "package org.jetbrains.plugins.ruby\n\nimport org.jetbrains.ruby.codeInsight.Logger\n\nclass IdePluginLogger(private val inte"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/PluginResourceUtil.java",
    "chars": 921,
    "preview": "package org.jetbrains.plugins.ruby;\n\nimport com.intellij.ide.plugins.IdeaPluginDescriptor;\nimport com.intellij.ide.plugi"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/RubyDynamicCodeInsightPluginInjector.kt",
    "chars": 336,
    "preview": "package org.jetbrains.plugins.ruby\n\nimport org.jetbrains.ruby.codeInsight.Injector\nimport org.jetbrains.ruby.codeInsight"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ancestorsextractor/AncestorsExtractor.kt",
    "chars": 6718,
    "preview": "package org.jetbrains.plugins.ruby.ancestorsextractor\n\nimport com.intellij.openapi.application.ReadAction\nimport com.int"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ancestorsextractor/RailsConsoleRunner.kt",
    "chars": 6963,
    "preview": "package org.jetbrains.plugins.ruby.ancestorsextractor\n\nimport com.google.gson.Gson\nimport com.intellij.execution.Executi"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportAncestorsActions.kt",
    "chars": 2420,
    "preview": "package org.jetbrains.plugins.ruby.ruby.actions\n\nimport com.intellij.openapi.module.Module\nimport com.intellij.openapi.p"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportAncesttorsDiffAction.kt",
    "chars": 3941,
    "preview": "package org.jetbrains.plugins.ruby.ruby.actions\n\nimport com.intellij.openapi.module.Module\nimport com.intellij.openapi.p"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ExportFileActionBase.kt",
    "chars": 4173,
    "preview": "package org.jetbrains.plugins.ruby.ruby.actions\n\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intel"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/actions/ImportExportContractsAction.kt",
    "chars": 4304,
    "preview": "package org.jetbrains.plugins.ruby.ruby.actions\n\nimport com.intellij.openapi.actionSystem.AnActionEvent\nimport com.intel"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/ProjectLifecycleListenerImpl.kt",
    "chars": 3691,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight\n\nimport com.google.gson.Gson\nimport com.intellij.openapi.project.Pro"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/RubyDynamicCodeInsightPluginAppLifecyctlListener.kt",
    "chars": 477,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight\n\nimport com.intellij.ide.AppLifecycleListener\nimport com.intellij.op"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/TrackerDataLoader.kt",
    "chars": 593,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight\n\nimport com.intellij.openapi.module.ModuleManager\nimport com.intelli"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/stateTracker/ClassHierarchySymbolProvider.kt",
    "chars": 1224,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker\n\nimport com.intellij.openapi.module.ModuleUtilCore\nimpo"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/stateTracker/RubyClassHierarchyWithCaching.kt",
    "chars": 5903,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.stateTracker\n\nimport com.intellij.openapi.components.ServiceManager\n"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/symbols/structure/RMethodSyntheticSymbol.java",
    "chars": 7724,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure;\n\nimport com.intellij.openapi.application.ReadActi"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyCollectStateRunner.kt",
    "chars": 2304,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.types\n\nimport com.intellij.execution.ExecutionException\nimport com.i"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyRunWithTypeTrackerRunner.kt",
    "chars": 2590,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.types\n\nimport com.intellij.execution.ExecutionException\nimport com.i"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/codeInsight/types/RubyTypeProvider.kt",
    "chars": 11297,
    "preview": "package org.jetbrains.plugins.ruby.ruby.codeInsight.types\n\nimport com.intellij.openapi.application.ReadAction\nimport com"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/intentions/AddContractAnnotationIntention.java",
    "chars": 6214,
    "preview": "package org.jetbrains.plugins.ruby.ruby.intentions;\n\nimport com.intellij.openapi.editor.Editor;\nimport com.intellij.open"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/intentions/BaseRubyMethodIntentionAction.kt",
    "chars": 1151,
    "preview": "package org.jetbrains.plugins.ruby.ruby.intentions\n\nimport com.intellij.codeInsight.intention.impl.BaseIntentionAction\ni"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/intentions/RemoveCollectedInfoIntention.kt",
    "chars": 1506,
    "preview": "package org.jetbrains.plugins.ruby.ruby.intentions\n\nimport com.intellij.openapi.editor.Editor\nimport com.intellij.openap"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/persistent/TypeInferenceDirectory.kt",
    "chars": 409,
    "preview": "package org.jetbrains.plugins.ruby.ruby.persistent\n\nimport com.intellij.openapi.application.PathManager\nimport com.intel"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/run/configuration/CollectExecSettings.java",
    "chars": 2685,
    "preview": "package org.jetbrains.plugins.ruby.ruby.run.configuration;\n\nimport com.intellij.openapi.util.Key;\nimport org.jetbrains.a"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/ruby/run/configuration/RunWithTypeTrackerRunConfigurationExtension.java",
    "chars": 10190,
    "preview": "package org.jetbrains.plugins.ruby.ruby.run.configuration;\n\nimport com.intellij.execution.configurations.GeneralCommandL"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/settings/RubyTypeContractsConfigurable.kt",
    "chars": 648,
    "preview": "package org.jetbrains.plugins.ruby.settings\n\nimport com.intellij.openapi.options.ConfigurableBase\n\nclass RubyTypeContrac"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/settings/RubyTypeContractsConfigurableUI.kt",
    "chars": 6292,
    "preview": "package org.jetbrains.plugins.ruby.settings\n\nimport com.intellij.openapi.options.ConfigurableUi\nimport com.intellij.open"
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/settings/RubyTypeContractsSettings.kt",
    "chars": 1649,
    "preview": "package org.jetbrains.plugins.ruby.settings\n\nimport com.intellij.openapi.components.PersistentStateComponent\nimport com."
  },
  {
    "path": "ide-plugin/src/org/jetbrains/plugins/ruby/util/SignatureServerUtil.kt",
    "chars": 727,
    "preview": "package org.jetbrains.plugins.ruby.util\n\nimport com.intellij.openapi.project.Project\nimport org.jetbrains.plugins.ruby.r"
  },
  {
    "path": "ide-plugin/src/test/java/CallStatCompletionTest.kt",
    "chars": 14343,
    "preview": "import com.intellij.execution.ExecutionException\nimport com.intellij.openapi.diagnostic.Logger\nimport com.intellij.testF"
  },
  {
    "path": "ide-plugin/src/test/java/org/jetbrains/plugins/ruby/ruby/actions/ImportExportTests.kt",
    "chars": 4336,
    "preview": "package org.jetbrains.plugins.ruby.ruby.actions\n\nimport junit.framework.Assert\nimport junit.framework.TestCase\nimport or"
  },
  {
    "path": "ide-plugin/src/test/testData/anonymous_module_method_call_test.rb",
    "chars": 71,
    "preview": "module A\n  def self.foo(a, b)\n    true\n  end\nend\n\nA.foo(\"hey\", :symbol)"
  },
  {
    "path": "ide-plugin/src/test/testData/call_info_of_nested_class_test.rb",
    "chars": 82,
    "preview": "module M\n  class A\n    def foo(a)\n      a\n    end\n  end\nend\n\na = M::A.new\na.foo(a)"
  },
  {
    "path": "ide-plugin/src/test/testData/duplicates_in_callinfo_table_test.rb",
    "chars": 131,
    "preview": "def foo(a)\n  if a == \"str\"\n    return a\n  end\n  false\nend\n\nfoo(\"str\")\nfoo(\"not str\")\n3.times { foo(\"str\") }\n3.times { fo"
  },
  {
    "path": "ide-plugin/src/test/testData/forget_call_info_when_arguments_number_changed_test_part_1.rb",
    "chars": 69,
    "preview": "class A\n    def foo(a)\n        :symbol\n    end\nend\n\nA.new.foo(\"Hey\")\n"
  },
  {
    "path": "ide-plugin/src/test/testData/forget_call_info_when_arguments_number_changed_test_part_2.rb",
    "chars": 72,
    "preview": "class A\n    def foo(a, b)\n        b\n    end\nend\n\nA.new.foo(true, false)\n"
  },
  {
    "path": "ide-plugin/src/test/testData/in_project_root_test/gem_like.rb",
    "chars": 159,
    "preview": "def catch(a); end\n\ndef dont_catch_2(a); end\n\ndef catch_2(a)\n  dont_catch_2(a)\nend\n\ndef dont_catch_3(&a)\n  yield(a)\nend\n\n"
  },
  {
    "path": "ide-plugin/src/test/testData/in_project_root_test/in_project_root_test.rb",
    "chars": 99,
    "preview": "require_relative 'gem_like'\n\ncatch('hey')\n\ncatch_2('bro')\n\ndef foo(a); end\n\ncatch_3(&method(:foo))\n"
  },
  {
    "path": "ide-plugin/src/test/testData/merge_test1.rb",
    "chars": 246,
    "preview": "class A\n\nend\n\nclass C\n\nend\n\nclass B1\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nclass B2\n  def test3\n\n  end\n\n  def test"
  },
  {
    "path": "ide-plugin/src/test/testData/merge_test1_to_run.rb",
    "chars": 238,
    "preview": "class A\n\nend\n\nclass C\n\nend\n\nclass B1\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nclass B2\n  def test3\n\n  end\n\n  def test"
  },
  {
    "path": "ide-plugin/src/test/testData/merge_test2.rb",
    "chars": 246,
    "preview": "class A\n\nend\n\nclass C\n\nend\n\nclass B1\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nclass B2\n  def test3\n\n  end\n\n  def test"
  },
  {
    "path": "ide-plugin/src/test/testData/merge_test2_to_run.rb",
    "chars": 238,
    "preview": "class A\n\nend\n\nclass C\n\nend\n\nclass B1\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nclass B2\n  def test3\n\n  end\n\n  def test"
  },
  {
    "path": "ide-plugin/src/test/testData/method_without_parameters_test.rb",
    "chars": 24,
    "preview": "def foo\n  \"hey\"\nend\n\nfoo"
  },
  {
    "path": "ide-plugin/src/test/testData/multiple_execution_test1.rb",
    "chars": 177,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/multiple_execution_test2.rb",
    "chars": 194,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/multiple_execution_test2_to_run.rb",
    "chars": 187,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/ref_links_test.rb",
    "chars": 200,
    "preview": "class A\n\nend\n\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef doo(a, b, c)\n  a\nend\nFoo\n\nA.ne"
  },
  {
    "path": "ide-plugin/src/test/testData/ref_links_test_to_run.rb",
    "chars": 191,
    "preview": "class A\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef doo(a, b, c)\n  a\nend\nFoo\n\nA.new"
  },
  {
    "path": "ide-plugin/src/test/testData/ruby_exec_part_2.rb",
    "chars": 27,
    "preview": "def bar(a); end\n\nbar(true)\n"
  },
  {
    "path": "ide-plugin/src/test/testData/ruby_exec_test.rb",
    "chars": 111,
    "preview": "def foo(a); end\n\nfoo(\"string\")\n\nKernel.exec(\"ruby\", \"#{File.expand_path(\"..\", __FILE__)}/ruby_exec_part_2.rb\")\n"
  },
  {
    "path": "ide-plugin/src/test/testData/sample_kw_test.rb",
    "chars": 189,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/sample_kw_test_to_run.rb",
    "chars": 181,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/sample_test.rb",
    "chars": 171,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/sample_test_to_run.rb",
    "chars": 163,
    "preview": "require 'date'\n\nclass A\n\nend\n\nclass C\n\nend\n\nclass B\n  def test1\n\n  end\n\n  def test2\n\n  end\nend\n\nA.class_eval <<Foo\ndef f"
  },
  {
    "path": "ide-plugin/src/test/testData/save_types_between_launches_test_part_1.rb",
    "chars": 338,
    "preview": "class A\n    def foo(a)\n        if a == \"str1\"\n            return :symbol\n        end\n        if a == \"str2\"\n            "
  },
  {
    "path": "ide-plugin/src/test/testData/save_types_between_launches_test_part_2.rb",
    "chars": 336,
    "preview": "class A\n    def foo(a)\n        if a == \"str1\"\n            return :symbol\n        end\n        if a == \"str2\"\n            "
  },
  {
    "path": "ide-plugin/src/test/testData/simple_call_info_collection_test.rb",
    "chars": 157,
    "preview": "class AClass\n    def foo(a)\n        if a.kind_of? String\n            :symbol\n        else\n            true\n        end\n "
  },
  {
    "path": "ide-plugin/src/test/testData/simple_call_info_collection_test_multiple_functions_test.rb",
    "chars": 170,
    "preview": "class A\n    def foo(a, b)\n        a || b\n    end\n\n    def bar(a)\n        a && A.new\n    end\nend\n\na = A.new\n\na.foo(\"Hey\","
  },
  {
    "path": "ide-plugin/src/test/testData/simple_call_info_collection_with_multiple_arguments_test.rb",
    "chars": 96,
    "preview": "class AClass\n    def foo(a, b)\n        /some regex/\n    end\nend\n\nAClass.new.foo(\"String\", true)\n"
  },
  {
    "path": "ide-plugin/src/test/testData/top_level_methods_call_info_collection_test.rb",
    "chars": 156,
    "preview": "def foo(a, b)\n    if a == \"str\"\n      return /some regex/\n    end\n    a || b\nend\n\nfoo(true, false)\nfoo(false, :symbol)\nf"
  },
  {
    "path": "ruby-call-signature/build.gradle",
    "chars": 120,
    "preview": "apply plugin: 'java'\n\nsourceSets {\n    main.java.srcDirs = ['src/main/java']\n    test.java.srcDirs = ['src/test/java']\n}"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/CallInfo.kt",
    "chars": 2944,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nconst val ARGUMENTS_TYPES_SEPARATOR = \";\"\n\ninterface CallInfo {\n"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/ClassInfo.kt",
    "chars": 767,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\n\ninterface ClassInfo {\n    val gemInfo: GemInfo?\n    val classFQ"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/GemInfo.kt",
    "chars": 1052,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\ninterface GemInfo {\n    val name: String\n    val version: String"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/MethodInfo.kt",
    "chars": 1346,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\ninterface MethodInfo {\n    val classInfo: ClassInfo\n    val name"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/ParameterInfo.java",
    "chars": 1722,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature;\n\nimport org.jetbrains.annotations.NotNull;\n\npublic class Paramet"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RSignatureContract.java",
    "chars": 14553,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature;\n\nimport kotlin.Pair;\nimport org.jetbrains.annotations.NotNull;\ni"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RSignatureContractContainer.kt",
    "chars": 1335,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nclass RSignatureContractContainer {\n\n    private val myContracts"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RSignatureContractNode.java",
    "chars": 779,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains."
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/RTuple.java",
    "chars": 1713,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature;\n\nimport org.jetbrains.annotations.NotNull;\n\nimport java.util.Lis"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/SignatureContract.kt",
    "chars": 7414,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nimport org.jetbrains.ruby.codeInsight.types.signature.contractTr"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/SignatureInfo.kt",
    "chars": 491,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\ninterface SignatureInfo {\n    val methodInfo: MethodInfo\n    val"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/ContractTransition.java",
    "chars": 609,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.contractTransition;\n\nimport org.jetbrains.annotations.NotNull;\n\ni"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/ReferenceContractTransition.java",
    "chars": 1326,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.contractTransition;\n\nimport org.jetbrains.annotations.NotNull;\n\ni"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/TransitionHelper.java",
    "chars": 922,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.contractTransition;\n\nimport org.jetbrains.annotations.NotNull;\n\ni"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/contractTransition/TypedContractTransition.java",
    "chars": 989,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.contractTransition;\n\nimport org.jetbrains.annotations.NotNull;\n\ni"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/serialization/MethodInfoSerialization.kt",
    "chars": 6583,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.serialization\n\nimport org.jetbrains.ruby.codeInsight.types.signat"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/serialization/RmcDirectory.kt",
    "chars": 2040,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.serialization\n\nimport org.jetbrains.ruby.codeInsight.types.signat"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/serialization/SignatureContractSerialization.kt",
    "chars": 3170,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.serialization\n\nimport org.jetbrains.ruby.codeInsight.types.signat"
  },
  {
    "path": "ruby-call-signature/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/serialization/TestSerialization.kt",
    "chars": 2638,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.serialization\n\nimport java.io.DataInput\nimport java.io.DataOutput"
  },
  {
    "path": "ruby-call-signature/src/test/java/org/jetbrains/ruby/codeInsight/types/signature/GemInfoFromPathTest.kt",
    "chars": 1355,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nimport junit.framework.TestCase\nimport org.junit.Test\n\nclass Gem"
  },
  {
    "path": "ruby-call-signature/src/test/java/org/jetbrains/ruby/codeInsight/types/signature/SignatureContractMergeTest.kt",
    "chars": 1761,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nimport org.junit.Test\n\nclass SignatureContractMergeTest : Signat"
  },
  {
    "path": "ruby-call-signature/src/test/java/org/jetbrains/ruby/codeInsight/types/signature/SignatureContractSerializationTest.kt",
    "chars": 3754,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nimport org.jetbrains.ruby.codeInsight.types.signature.serializat"
  },
  {
    "path": "ruby-call-signature/src/test/java/org/jetbrains/ruby/codeInsight/types/signature/SignatureContractTestBase.kt",
    "chars": 5192,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature\n\nimport junit.framework.TestCase\nimport org.jetbrains.ruby.codeIn"
  },
  {
    "path": "settings.gradle",
    "chars": 216,
    "preview": "include 'ruby-call-signature', 'storage-server-api', 'lambda-update-handler', 'lambda-put-handler'\ninclude 'contract-cre"
  },
  {
    "path": "signature-viewer/build.gradle",
    "chars": 1074,
    "preview": "version 'unspecified'\n\napply plugin: 'java'\n\nsourceCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n}\n\n\ndependenci"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/DBViewer.kt",
    "chars": 1445,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.exposed.sql.transactions.transaction\nimport org.jetbr"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/EraseLocation.kt",
    "chars": 1018,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.exposed.sql.transactions.transaction\nimport org.jetbr"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/SignatureExport.kt",
    "chars": 1559,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.ruby.codeInsight.types.signature.SignatureInfo\nimport"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/SignatureImport.kt",
    "chars": 1030,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.ruby.codeInsight.types.signature.serialization.RmcDir"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/SignatureViewer.kt",
    "chars": 2186,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.ruby.codeInsight.types.signature.SignatureContract\nim"
  },
  {
    "path": "signature-viewer/src/org/jetbrains/ruby/runtime/signature/SplitDB.kt",
    "chars": 1629,
    "preview": "package org.jetbrains.ruby.runtime.signature\n\nimport org.jetbrains.exposed.sql.Database\nimport org.jetbrains.exposed.sql"
  },
  {
    "path": "state-tracker/build.gradle",
    "chars": 751,
    "preview": "buildscript {\n    ext.kotlin_version = '1.2.70'\n\n    repositories {\n        mavenCentral()\n    }\n    dependencies {\n    "
  },
  {
    "path": "state-tracker/src/main/java/org/jetbrains/ruby/stateTracker/RubyClassHierarchy.kt",
    "chars": 3501,
    "preview": "package org.jetbrains.ruby.stateTracker\n\ninterface RubyClassHierarchy {\n    val loadPaths: List<String>\n\n    val topLeve"
  },
  {
    "path": "state-tracker/src/main/java/org/jetbrains/ruby/stateTracker/RubyClassHierarchyLoader.kt",
    "chars": 7412,
    "preview": "package org.jetbrains.ruby.stateTracker\n\nimport com.google.gson.Gson\nimport java.util.*\nimport kotlin.collections.ArrayL"
  },
  {
    "path": "state-tracker/src/test/java/org/jetbrains/ruby/stateTracker/RubyClassHierarchyLoaderNonStandardModuleTypeTest.kt",
    "chars": 744,
    "preview": "package org.jetbrains.ruby.stateTracker\n\nimport junit.framework.TestCase\nimport org.junit.Test\n\nclass RubyClassHierarchy"
  },
  {
    "path": "state-tracker/src/test/java/org/jetbrains/ruby/stateTracker/RubyClassHierarchyLoaderTest.kt",
    "chars": 3188,
    "preview": "package org.jetbrains.ruby.stateTracker\n\nimport junit.framework.TestCase\nimport org.junit.Test\n\nclass RubyClassHierarchy"
  },
  {
    "path": "state-tracker/src/test/java/testData/classes.json",
    "chars": 38616,
    "preview": "{\n  \"top_level_constants\": [\n    {\n      \"class_name\": \"IO\",\n      \"extended\": [],\n      \"name\": \"STDIN\"\n    }\n  ],\n\n  \""
  },
  {
    "path": "state-tracker/src/test/java/testData/non-standard-module-type.json",
    "chars": 672,
    "preview": "{\n  \"top_level_constants\": [\n    {\n      \"class_name\": \"IO\",\n      \"extended\": [],\n      \"name\": \"STDIN\"\n    }\n  ],\n  \"l"
  },
  {
    "path": "storage-server-api/build.gradle",
    "chars": 503,
    "preview": "buildscript {\n    repositories {\n        jcenter()\n    }\n\n    dependencies {\n        classpath \"org.jetbrains.kotlin:kot"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/signature/serialization/BlobSerialization.kt",
    "chars": 1965,
    "preview": "package org.jetbrains.ruby.codeInsight.types.signature.serialization\n\nimport org.jetbrains.exposed.dao.EntityHook\nimport"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/DatabaseProvider.kt",
    "chars": 3054,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server\n\nimport org.jetbrains.exposed.sql.Database\nimport org.jetbra"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/RSignatureProvider.java",
    "chars": 2372,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbr"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/RSignatureStorage.java",
    "chars": 1805,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbr"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/StorageException.java",
    "chars": 629,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server;\n\n@SuppressWarnings(\"unused\")\npublic class StorageException "
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/impl/IntIdTableWithPossibleDependency.kt",
    "chars": 6026,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.impl\n\nimport org.jetbrains.exposed.dao.EntityID\nimport org.j"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/impl/RSignatureProviderImpl.kt",
    "chars": 4964,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.impl\n\nimport org.jetbrains.exposed.sql.and\nimport org.jetbra"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/impl/RowConversions.kt",
    "chars": 1111,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.impl\n\nimport org.jetbrains.exposed.sql.ResultRow\nimport org."
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/impl/Schema.kt",
    "chars": 12400,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.impl\n\nimport org.jetbrains.exposed.dao.EntityID\nimport org.j"
  },
  {
    "path": "storage-server-api/src/main/java/org/jetbrains/ruby/codeInsight/types/storage/server/testutil/DatabaseTestUtils.kt",
    "chars": 708,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.testutil\n\nimport org.jetbrains.exposed.sql.transactions.tran"
  },
  {
    "path": "storage-server-api/src/test/java/org/jetbrains/ruby/codeInsight/types/storage/server/impl/RSignatureProviderTest.kt",
    "chars": 9587,
    "preview": "package org.jetbrains.ruby.codeInsight.types.storage.server.impl\n\nimport junit.framework.TestCase\nimport org.jetbrains.e"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the JetBrains/ruby-type-inference GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 160 files (406.4 KB), approximately 101.2k tokens, and a symbol index with 380 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!