Expose core RBS definition paths as public API - #1029
Conversation
Tools that build their own list of paths and index it with `index_all` instead of `index_workspace` (RuboCop's `AllCops/UseProjectIndex` does this, so that its own `Include`/`Exclude` configuration governs the index) ended up with a graph that has no definitions for Ruby's core classes. The core definitions come from indexing the `rbs` gem's `core` and `stdlib` signature directories, which only `index_workspace` did, through the private `add_core_rbs_definition_paths`. Add `Rubydex::Graph.core_rbs_definition_paths`, which returns those paths, so callers can append them to their own list without reaching for a private method. `add_core_rbs_definition_paths` now delegates to it, keeping a single source of truth as well as the existing semantics: the latest installation of `rbs` in the system wins, `rbs` does not have to be part of the bundle and no installation being found is not an error. Fixes Shopify#1027 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vinistock
left a comment
There was a problem hiding this comment.
Thanks for the contribution. Exposing this method is not a problem, but I want to call attention that performing the analysis in an incomplete graph can have unexpected results and this is not going to fix the problem 100%.
Constants in Ruby are global and can be influenced by any code included in the application, which includes dependencies.
Consider this example:
# rbs definition
class Object; end
# some_gem
class UsefulParentClass
class Object; end
end
# your code
class MyImplementation < UsefulParentClass
# This Object reference points to UsefulParentClass::Object and not Object.
# By not including the full context of the application into the analysis, constants
# may resolve differently and you might be inaccurate results.
Object
# Note that inaccuracies in constant resolution can easily propagate. For example:
#
# This class inherits from UsefulParentClass::Object and not Object. By incorrectly
# understanding its parent class, we resolve every constant reference inside of this
# namespace against the wrong ancestor chain, which produces more inaccuracies in the
# analysis.
class SomeOtherThing < Object
end
endExposing the method you're proposing would make the analysis less incomplete by including the core definitions, but it will still be incomplete and subject to more inaccuracies if you don't take dependencies into account.
This is why in the Rubydex linter included in this gem, we filter diagnostics from dependencies only at the end during presentation and never exclude anything from the analysis as it may impact the understanding of the code.
|
@vinistock I understand. When it comes to rubocop though, it really depends on how they'd like the integration to go forward. indexing the 3rd party gems is an opt-in,in which case |
Tools that build their own list of paths and index it with
index_allinstead ofindex_workspace(RuboCop'sAllCops/UseProjectIndexdoes this, so that its ownInclude/Excludeconfiguration governs the index) ended up with a graph that has no definitions for Ruby's core classes. The core definitions come from indexing therbsgem'scoreandstdlibsignature directories, which onlyindex_workspacedid, through the privateadd_core_rbs_definition_paths.Add
Rubydex::Graph.core_rbs_definition_paths, which returns those paths, so callers can append them to their own list without reaching for a private method.add_core_rbs_definition_pathsnow delegates to it, keeping a single source of truth as well as the existing semantics: the latest installation ofrbsin the system wins,rbsdoes not have to be part of the bundle and no installation being found is not an error.@vinistock this is to address what we discussed in #1027; rubocop can't really use
graph_workspaceas that'd mean indexing the 3rd party gems too; the easiest workaround I got to was to use this method to expose the rbs sigs there, which would be better served as a public API.