-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
mixes_in_class_methods
support (#77)
- Loading branch information
Showing
7 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# typed: strict | ||
# frozen_string_literal: true | ||
|
||
module YARDSorbet | ||
module Handlers | ||
# Extends any modules included via `mixes_in_class_methods` | ||
# @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook | ||
# Sorbet `mixes_in_class_methods` documentation | ||
class IncludeHandler < YARD::Handlers::Ruby::Base | ||
extend T::Sig | ||
|
||
handles method_call(:include) | ||
namespace_only | ||
|
||
sig { void } | ||
def process | ||
return unless extra_state.mix_in_class_methods | ||
|
||
statement.parameters(false).each do |mixin| | ||
obj = YARD::CodeObjects::Proxy.new(namespace, mixin.source) | ||
class_methods_namespace = extra_state.mix_in_class_methods[obj.to_s] | ||
next unless class_methods_namespace | ||
|
||
included_in.mixins(:class) << YARD::CodeObjects::Proxy.new(obj, class_methods_namespace) | ||
end | ||
end | ||
|
||
private | ||
|
||
# @return the namespace object that is including the module | ||
sig { returns(YARD::CodeObjects::NamespaceObject) } | ||
def included_in | ||
statement.namespace ? YARD::CodeObjects::Proxy.new(namespace, statement.namespace.source) : namespace | ||
end | ||
end | ||
end | ||
end |
22 changes: 22 additions & 0 deletions
22
lib/yard-sorbet/handlers/mixes_in_class_methods_handler.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# typed: strict | ||
# frozen_string_literal: true | ||
|
||
module YARDSorbet | ||
module Handlers | ||
# Tracks modules that invoke `mixes_in_class_methods` for use in {IncludeHandler} | ||
# @see https://sorbet.org/docs/abstract#interfaces-and-the-included-hook | ||
# Sorbet `mixes_in_class_methods` documentation | ||
class MixesInClassMethodsHandler < YARD::Handlers::Ruby::Base | ||
extend T::Sig | ||
|
||
handles method_call(:mixes_in_class_methods) | ||
namespace_only | ||
|
||
sig { void } | ||
def process | ||
extra_state.mix_in_class_methods ||= {} | ||
extra_state.mix_in_class_methods[namespace.to_s] = statement.parameters(false)[0].source | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# typed: true | ||
module M | ||
extend T::Helpers | ||
interface! | ||
|
||
module ClassMethods | ||
extend T::Sig | ||
extend T::Helpers | ||
abstract! | ||
|
||
sig {void} | ||
def foo | ||
bar | ||
end | ||
|
||
sig {abstract.void} | ||
def bar; end | ||
end | ||
|
||
mixes_in_class_methods(ClassMethods) | ||
end | ||
|
||
class A # error: Missing definition for abstract method | ||
include M | ||
|
||
extend T::Sig | ||
|
||
sig {override.void} | ||
def self.bar; end | ||
end | ||
|
||
# Sorbet knows that `foo` is a class method on `A` | ||
A.foo | ||
|
||
module Receiver; end | ||
|
||
Receiver.include(M) | ||
|
||
module OuterModule | ||
module InnerModule | ||
extend T::Helpers | ||
module ClassMethods | ||
def foo; end | ||
end | ||
mixes_in_class_methods(ClassMethods) | ||
end | ||
|
||
class InnerClass | ||
include InnerModule | ||
end | ||
end | ||
|
||
module M2; end | ||
module M3; end | ||
|
||
class C | ||
extend T::Sig | ||
|
||
include M2, M, M3 | ||
|
||
sig {override.void} | ||
def self.bar; end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# typed: strict | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe YARDSorbet::Handlers::IncludeHandler do | ||
path = File.join(File.expand_path('../../data', __dir__), 'include_handler.rb') | ||
|
||
before do | ||
YARD::Registry.clear | ||
YARD::Parser::SourceParser.parse(path) | ||
end | ||
|
||
describe 'including a module with `mixes_in_class_methods`' do | ||
it 'adds the class method namespace to `class_mixins`' do | ||
node = YARD::Registry.at('A') | ||
expect(node.class_mixins.map(&:to_s)).to include('M::ClassMethods') | ||
end | ||
|
||
it 'attches class method namespace to explicit receiver' do | ||
node = YARD::Registry.at('Receiver') | ||
expect(node.class_mixins.map(&:to_s)).to include('M::ClassMethods') | ||
end | ||
|
||
it 'resolves full class method namespace' do | ||
node = YARD::Registry.at('OuterModule::InnerClass') | ||
expect(node.class_mixins.map(&:to_s)).to include('OuterModule::InnerModule::ClassMethods') | ||
end | ||
|
||
it 'handles multiple parameters to include' do | ||
node = YARD::Registry.at('C') | ||
expect(node.class_mixins.map(&:to_s)).to include('M::ClassMethods') | ||
end | ||
end | ||
end |