Skip to content

Commit

Permalink
Definition list for modules.
Browse files Browse the repository at this point in the history
The new version of Rlint::Analyze::Definitions is now capable of building a
list of defined modules and their associated data.

Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
  • Loading branch information
Yorick Peterse committed Nov 1, 2012
1 parent 602fe56 commit c6281e0
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 69 deletions.
35 changes: 0 additions & 35 deletions lib/rlint/analyze/definitions.old.rb
Expand Up @@ -74,41 +74,6 @@ def on_finish
warn_for_unused_variables
end

##
# Called when a module is defined.
#
# @param [Rlint::Token::Token] token Token class containing details about
# the module.
#
def on_module(token)
name = token.name.join('::')
@namespace << name
@call_types << :method

# If a module has already been defined the scope should not be
# overwritten.
return if scope.lookup(:constant, name)

new_scope = Scope.new(scope)

scope.add(:constant, name, new_scope)

@scopes << new_scope
end

##
# Called after a module definition has been processed.
#
# @see Rlint::Analyze::Definitions#on_module
#
def after_module(token)
warn_for_unused_variables

@call_types.pop
@scopes.pop
@namespace.pop
end

##
# Called when a local variable is found.
#
Expand Down
64 changes: 30 additions & 34 deletions lib/rlint/analyze/definitions.rb
Expand Up @@ -27,7 +27,6 @@ def initialize(*args)
@storage[:scope] = Scope.new(nil, true, true)
@scopes = []
@namespace = []
@call_types = []
end

##
Expand Down Expand Up @@ -101,8 +100,7 @@ def on_method_definition(token)

target.add(type, token.name, Definition.new(token, new_scope))

@call_types << type
@scopes << new_scope
@scopes << new_scope
end

##
Expand All @@ -120,8 +118,6 @@ def after_method_definition(token)
last_scope.symbols[key]
)
end

@call_types.pop
end

##
Expand All @@ -130,9 +126,8 @@ def after_method_definition(token)
# @param [Rlint::Token::ClassToken] token
#
def on_class(token)
name = token.name.join('::')
@namespace << name
@call_types << :method
name = token.name.join('::')
@namespace << name

# If a class has already been defined the scoping data should not be
# overwritten.
Expand All @@ -154,48 +149,49 @@ def on_class(token)
#
def after_class(token)
@scopes.pop
@call_types.pop
@namespace.pop
end

private

##
# Returns the current scope. This method is primarily used to make the
# code in this class a bit more pleasant to read.
# Called when a module is defined.
#
# @return [Rlint::Scope]
# @param [Rlint::Token::Token] token
#
def scope
return !@scopes.empty? ? @scopes[-1] : @storage[:scope]
def on_module(token)
name = token.name.join('::')
@namespace << name

# If a module has already been defined the scope should not be
# overwritten.
return if scope.lookup(:constant, name)

new_scope = Scope.new(scope)

scope.add(:constant, name, Definition.new(token, new_scope))

@scopes << new_scope
end

##
# Returns the last method call type to use.
# Called after a module definition has been processed.
#
# @return [Symbol]
# @see Rlint::Analyze::Definitions#on_module
#
def call_type
return !@call_types.empty? ? @call_types[-1] : :instance_method
def after_module(token)
@scopes.pop
@namespace.pop
end

private

##
# Given an Array of constant names (e.g. the names of a constant path)
# this method will return the scope to use for the last segment.
# Returns the current scope. This method is primarily used to make the
# code in this class a bit more pleasant to read.
#
# @param [Array] segments The name segments to use for the lookup.
# @return [Rlint::Scope|NilClass]
# @return [Rlint::Scope]
#
def resolve_scope(segments)
resolved = scope

segments.each do |name|
break unless resolved

resolved = resolved.lookup(:constant, name)
end

return resolved
def scope
return !@scopes.empty? ? @scopes[-1] : @storage[:scope]
end
end # Definitions
end # Analyze
Expand Down
71 changes: 71 additions & 0 deletions spec/rlint/analyze/definitions/modules.rb
@@ -0,0 +1,71 @@
require File.expand_path('../../../../helper', __FILE__)

describe 'Rlint::Analyze::Definitions: modules' do
it 'Define a module in the global scope' do
code = <<-CODE
module Example
def example_method
end
end
CODE

tokens = Rlint::Parser.new(code).parse
iterator = Rlint::Iterator.new

iterator.bind(Rlint::Analyze::Definitions)
iterator.run(tokens)

scope = iterator.storage[:scope]
const = scope.lookup(:constant, 'Example')

const.class.should == Rlint::Definition

const.token.class.should == Rlint::Token::Token
const.token.type.should == :module
const.token.name.should == ['Example']

scope.lookup(:instance_method, 'example_method').nil?.should == true

method = const.scope.lookup(:instance_method, 'example_method')

method.class.should == Rlint::Definition

method.token.class.should == Rlint::Token::MethodDefinitionToken
method.token.name.should == 'example_method'
end

it 'Define a class method in a module' do
code = <<-CODE
module Example
def self.example_method
end
end
CODE

tokens = Rlint::Parser.new(code).parse
iterator = Rlint::Iterator.new

iterator.bind(Rlint::Analyze::Definitions)
iterator.run(tokens)

scope = iterator.storage[:scope]
const = scope.lookup(:constant, 'Example')

const.class.should == Rlint::Definition

const.token.class.should == Rlint::Token::Token
const.token.type.should == :module
const.token.name.should == ['Example']

scope.lookup(:method, 'example_method').nil?.should == true

method = const.scope.lookup(:method, 'example_method')

method.class.should == Rlint::Definition

method.token.class.should == Rlint::Token::MethodDefinitionToken
method.token.name.should == 'example_method'
end
end

0 comments on commit c6281e0

Please sign in to comment.