Skip to content

Commit

Permalink
Merge pull request #9215 from AriaXLi/PUP-11981_7.x
Browse files Browse the repository at this point in the history
[Backport] (PUP-11981) Syntactically incorrect types cause nil types in Puppet::InfoService::ClassInformationService
  • Loading branch information
AriaXLi authored Jan 22, 2024
2 parents 49c8cd8 + 1f6e950 commit a799b82
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 5 deletions.
15 changes: 12 additions & 3 deletions lib/puppet/pops/evaluator/literal_evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ module Evaluator
# QualifiedName
# Default (produced :default)
# Regular Expression (produces ruby regular expression)
#
# Not considered literal
# QualifiedReference # i.e. File, FooBar
# QualifiedReference
# AccessExpresion
#
class LiteralEvaluator

Expand Down Expand Up @@ -66,6 +65,16 @@ def literal_LiteralRegularExpression(o)
o.value
end

def literal_QualifiedReference(o)
o.value
end

def literal_AccessExpression(o)
# to prevent parameters with [[]] like Optional[[String]]
throw :not_literal if o.keys.size == 1 && o.keys[0].is_a?(Model::LiteralList)
o.keys.map { |v| literal(v) }
end

def literal_ConcatenatedString(o)
# use double quoted string value if there is no interpolation
throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Model::LiteralString)
Expand Down
4 changes: 4 additions & 0 deletions lib/puppet/pops/issues.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ def self.hard_issue(issue_code, *args, &block)
_("The numeric parameter name '$%{name}' cannot be used (clashes with numeric match result variables)") % { name: name }
end

ILLEGAL_NONLITERAL_PARAMETER_TYPE = issue :ILLEGAL_NONLITERAL_PARAMETER_TYPE, :name, :type_class do
_("The parameter '$%{name}' must be a literal type, not %{type_class}") % { name: name, type_class: label.a_an(type_class) }
end

# In certain versions of Puppet it may be allowed to assign to a not already assigned key
# in an array or a hash. This is an optional validation that may be turned on to prevent accidental
# mutation.
Expand Down
13 changes: 13 additions & 0 deletions lib/puppet/pops/validation/checker4_0.rb
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ def check_HostClassDefinition(o)
internal_check_parameter_name_uniqueness(o)
internal_check_reserved_params(o)
internal_check_no_idem_last(o)
internal_check_parameter_type_literal(o)
end

def check_ResourceTypeDefinition(o)
Expand All @@ -478,6 +479,18 @@ def check_ResourceTypeDefinition(o)
internal_check_no_idem_last(o)
end

def internal_check_parameter_type_literal(o)
o.parameters.each do |p|
next unless p.type_expr

type = nil
catch :not_literal do
type = literal(p.type_expr)
end
acceptor.accept(Issues::ILLEGAL_NONLITERAL_PARAMETER_TYPE, p, { name: p.name, type_class: p.type_expr.class }) if type.nil?
end
end

def internal_check_return_type(o)
r = o.return_type
internal_check_type_ref(o, r) unless r.nil?
Expand Down
19 changes: 19 additions & 0 deletions spec/unit/info_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,12 @@ class Borked($Herp+$Derp) {}
CODE
'json_unsafe.pp' => <<-CODE,
class json_unsafe($arg1 = /.*/, $arg2 = default, $arg3 = {1 => 1}) {}
CODE
'non_literal.pp' => <<-CODE,
class oops(Integer[1-3] $bad_int) { }
CODE
'non_literal_2.pp' => <<-CODE,
class oops_2(Optional[[String]] $double_brackets) { }
CODE
})
end
Expand Down Expand Up @@ -509,6 +515,19 @@ class json_unsafe($arg1 = /.*/, $arg2 = default, $arg3 = {1 => 1}) {}
})
end

it "errors with a descriptive message if non-literal class parameter is given" do
files = ['non_literal.pp', 'non_literal_2.pp'].map {|f| File.join(code_dir, f) }
result = Puppet::InfoService.classes_per_environment({'production' => files })
expect(result).to eq({
"production"=>{
"#{code_dir}/non_literal.pp" =>
{:error=> "The parameter '$bad_int' must be a literal type, not a Puppet::Pops::Model::AccessExpression (file: #{code_dir}/non_literal.pp, line: 1, column: 37)"},
"#{code_dir}/non_literal_2.pp" =>
{:error=> "The parameter '$double_brackets' must be a literal type, not a Puppet::Pops::Model::AccessExpression (file: #{code_dir}/non_literal_2.pp, line: 1, column: 44)"}
} # end production env
})
end

it "produces no type entry if type is not given" do
files = ['fum.pp'].map {|f| File.join(code_dir, f) }
result = Puppet::InfoService.classes_per_environment({'production' => files })
Expand Down
6 changes: 4 additions & 2 deletions spec/unit/pops/evaluator/literal_evaluator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
'"a"' => 'a',
'a' => 'a',
'a::b' => 'a::b',
'Integer[1]' => [1],
'File' => "file",

# special values
'default' => :default,
Expand All @@ -35,9 +37,9 @@
expect(leval.literal(parser.parse_string('undef'))).to be_nil
end

['1+1', 'File', '[1,2, 1+2]', '{a=>1+1}', 'Integer[1]', '"x$y"', '"x${y}z"'].each do |source|
['1+1', '[1,2, 1+2]', '{a=>1+1}', '"x$y"', '"x${y}z"', 'Integer[1-3]', 'Optional[[String]]'].each do |source|
it "throws :not_literal for non literal expression '#{source}'" do
expect{leval.literal(parser.parse_string('1+1'))}.to throw_symbol(:not_literal)
expect{leval.literal(parser.parse_string(source))}.to throw_symbol(:not_literal)
end
end
end

0 comments on commit a799b82

Please sign in to comment.