(PDOC-37) Parse 4x function ast type and params
The previous iteration eagerly grabbed all parameters when any puppet function was created. We did not retrieve type information and would grab parameters from any helper functions! * Parse the Ruby AST for dispatch blocks which specify type information. Parse the commands and arguments in those blocks. * If there are no dispatch blocks, parse the AST for a ruby function with the same name as the puppet function being created.
This commit is contained in:
parent
8b3cdd3a51
commit
081bbfe790
|
@ -23,32 +23,100 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
|
|||
|
||||
handles method_call(:create_function)
|
||||
|
||||
# Given a command node which represents code like this:
|
||||
# param 'Optional[Type]', :value_type
|
||||
# Extract the type name and type signature and return them as a array.
|
||||
def extract_type_from_command command
|
||||
return [] if command.children.length < 2 or command.children[1].children.length < 2
|
||||
type_specifier = command.children[1]
|
||||
# the parameter signature is the first child of the specifier and an
|
||||
# identifier. Convert it to a string.
|
||||
param_signature = type_specifier.children[0].source
|
||||
# The parameter name is the second child of the specifier and a symbol.
|
||||
# convert it to a string.
|
||||
param_name_ident = type_specifier.jump :ident
|
||||
return [] if param_name_ident == type_specifier
|
||||
param_name = param_name_ident.source
|
||||
[param_name, param_signature]
|
||||
end
|
||||
|
||||
process do
|
||||
name = process_parameters
|
||||
|
||||
method_arguments = []
|
||||
|
||||
# To attach the method parameters to the new code object, traverse the
|
||||
# ruby AST until a node is found which defines a list of parameters.
|
||||
# ruby AST until a node is found which defines a array of parameters.
|
||||
# Then, traverse the children of the parameters, storing each identifier
|
||||
# in the list of method arguments.
|
||||
# in the array of method arguments.
|
||||
obj = MethodObject.new(function_namespace, name) do |o|
|
||||
end
|
||||
|
||||
# overload_signatures is a array of arrays of tuples or empty:
|
||||
# overload_signatures = [
|
||||
# [ # First function dispatch arguments
|
||||
# # argument name, argument type
|
||||
# ['arg0', 'Variant[String,Array[String]]'
|
||||
# ['arg1', 'Optional[Type]']
|
||||
# ],
|
||||
# [ # Second function dispatch arguments
|
||||
# ['arg0', 'Variant[String,Array[String]]'
|
||||
# ['arg1', 'Optional[Type]']
|
||||
# ['arg2', 'Any']
|
||||
# ]
|
||||
# ]
|
||||
overload_signatures = []
|
||||
statement.traverse do |node|
|
||||
# Find all of the dispatch methods
|
||||
if node.type == :ident and node.source == 'dispatch'
|
||||
command = node.parent
|
||||
do_block = command.jump :do_block
|
||||
# If the command doesn't have a do_block we can't extract type info
|
||||
if do_block == command
|
||||
next
|
||||
end
|
||||
# Iterate through each of the children of the do block and build
|
||||
# tuples of parameter names and parameter type signatures
|
||||
do_block.children.first.children.each do |child|
|
||||
overload_signatures <<= [extract_type_from_command(child)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# If the overload_signatures array is empty because we couldn't find any
|
||||
# dispatch blocks, then there must be one method named the same as the
|
||||
# name of the function being created.
|
||||
if overload_signatures.length == 0
|
||||
statement.traverse do |node|
|
||||
if node.type == :params
|
||||
node.traverse do |params|
|
||||
if params.type == :ident
|
||||
# FIXME: Replace nil with parameter types
|
||||
method_arguments << [params[0], nil]
|
||||
# Find the function definition with the same name as the puppet
|
||||
# function being created.
|
||||
if (node.type == :def and node.children.first.type == :ident and
|
||||
node.children.first.source == obj.name.to_s)
|
||||
signature = []
|
||||
# Find its parameters. If they don't exist, fine
|
||||
params = node.jump :params
|
||||
break if params == node
|
||||
params.traverse do |param|
|
||||
if param.type == :ident
|
||||
# The parameters of Puppet functions with no defined dispatch are
|
||||
# as though they are Any type.
|
||||
signature << [param[0], 'Any']
|
||||
end
|
||||
end
|
||||
|
||||
overload_signatures <<= signature
|
||||
# Now that the parameters have been found, break out of the traversal
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
obj.parameters = method_arguments
|
||||
# Preserve this type information. We'll need it later when we look
|
||||
# at the docstring.
|
||||
obj.type_info = overload_signatures
|
||||
|
||||
# The yard docstring parser expects a array of arrays, not a array of
|
||||
# arrays of arrays.
|
||||
obj.parameters = overload_signatures.flatten(1)
|
||||
|
||||
obj['puppet_4x_function'] = true
|
||||
|
||||
|
@ -102,7 +170,7 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
|
|||
# @return [(String, Hash{String => String})]
|
||||
def process_parameters
|
||||
# Passing `false` to parameters excludes the block param from the returned
|
||||
# list.
|
||||
# array.
|
||||
name, _ = statement.parameters(false).compact
|
||||
|
||||
name = process_element(name)
|
||||
|
@ -114,7 +182,7 @@ class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
|
|||
# instead of `<<-`.
|
||||
HEREDOC_START = /^<?<-/
|
||||
|
||||
# Turns an entry in the method parameter list into a string.
|
||||
# Turns an entry in the method parameter array into a string.
|
||||
#
|
||||
# @param ele [YARD::Parser::Ruby::AstNode]
|
||||
# @return [String]
|
||||
|
|
|
@ -72,8 +72,7 @@ describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler d
|
|||
}.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stdout_from_any_process
|
||||
end
|
||||
|
||||
|
||||
it "should not issue a warning if the parameter names do match the docstring" do
|
||||
it "should not issue a warning when the parameter names match the docstring" do
|
||||
expect {
|
||||
parse <<-RUBY
|
||||
# @param num_a [Integer] the first number to be compared
|
||||
|
@ -87,10 +86,43 @@ describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler d
|
|||
}.to output("").to_stdout_from_any_process
|
||||
end
|
||||
|
||||
it "should not issue a warning when there are parametarized types and parameter names are the same" do
|
||||
expect {
|
||||
parse <<-RUBY
|
||||
# @param num_a Integer[1,2] the first number to be compared
|
||||
# @param num_b Integer[1,2] the second number to be compared
|
||||
Puppet::Functions.create_function(:max) do
|
||||
def max(num_a, num_b)
|
||||
num_a >= num_b ? num_a : num_b
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
}.to output("").to_stdout_from_any_process
|
||||
end
|
||||
|
||||
it "should issue a warning when there are parametarized types and parameter names differ" do
|
||||
expected_output_not_num_a = "[warn]: @param tag has unknown parameter" +
|
||||
" name: not_num_a \n in file `(stdin)' near line 3"
|
||||
expect {
|
||||
parse <<-RUBY
|
||||
# @param not_num_a Integer[1,2] the first number to be compared
|
||||
# @param num_b Integer[1,2] the second number to be compared
|
||||
Puppet::Functions.create_function(:max) do
|
||||
dispatch max_1 do
|
||||
param 'Integer[1,2]', :num_a
|
||||
param 'Integer[1,2]', :num_b
|
||||
end
|
||||
|
||||
def max_1(num_a, num_b)
|
||||
num_a >= num_b ? num_a : num_b
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
}.to output("#{expected_output_not_num_a}\n").to_stdout_from_any_process
|
||||
end
|
||||
|
||||
|
||||
it "should issue a warning if the parameter names do not match the " +
|
||||
"docstring in dispatch method" do
|
||||
it "should issue a warning if the parameter names do not match the docstring in dispatch method" do
|
||||
expected_output_not_a_param = "[warn]: @param tag has unknown parameter" +
|
||||
" name: not_a_param \n in file `(stdin)' near line 3"
|
||||
expected_output_also_not_a_param = "[warn]: @param tag has unknown " +
|
||||
|
@ -101,13 +133,13 @@ describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler d
|
|||
# @param also_not_a_param Integer the second number to be compared
|
||||
Puppet::Functions.create_function(:max) do
|
||||
dispatch max_1 do
|
||||
param 'Integer', :num_a
|
||||
param 'Integer[1,2]', :num_a
|
||||
param 'Integer', :num_b
|
||||
end
|
||||
|
||||
dispatch max_2 {
|
||||
param 'String', :num_c
|
||||
param 'String', :num_d
|
||||
param 'String[1,2]', :num_d
|
||||
}
|
||||
|
||||
def max_1(num_a, num_b)
|
||||
|
|
Loading…
Reference in New Issue