Skip to content

Commit 1f6e950

Browse files
committed
(PUP-11981) Syntactically incorrect types cause nil
This commit adds logic to validate class parameters are literal and provides a descriptive warning if parameter(s) are not literal, adds QualifiedReference and AccessExpression as a valid literal values & updates spec tests to check for more cases of invalid class parameters and any that broke as a result of the changes. The logic to validate class parameters is done in internal_check_parameter_type_literal method where the appropriate literal method is called on each parameter. There are different literal methods for each literal object/type [1]. For example if the parameter is String type, then literal_LiteralString is called. If a parameter is found to be not literal, then a :not_literal will be thrown & warning message with the invalid class parameter and its type will be printed. QualifiedReference is considered literal since it is a literal reference, however, there is a possibility it could be a faulty dangling reference that doesn't reference anything. AccessExpresions are anything with the Access Operator ([ ]) such as Optional[String] or Variant[Boolean, Float] [2]. Since these are valid class parameters, AccessExpressions are also considered literal. The logic to add QualifiedReference & AccessExpression as valid literal values is done in the literal_QualifiedReference & literal_AccessExpression methods. The literal_AccessExpression works by validating each key in the expression is literal. The literal_QualifiedReference method looks at o.value, similar to other existing literal methods like literal_LiteralString. [1] http://puppet-on-the-edge.blogspot.com/2014/02/puppet-internals-polymorphic-dispatch.html [2] https://github.com/puppetlabs/puppet-specifications/blob/master/language/expressions.md#assignment-operator For more information on this issue, see #9140 (cherry picked from commit adbb02c) (PUP-11981) Add test for non-literal class params in ClassInfoService spec (cherry picked from commit 8992763)
1 parent 49c8cd8 commit 1f6e950

File tree

5 files changed

+52
-5
lines changed

5 files changed

+52
-5
lines changed

lib/puppet/pops/evaluator/literal_evaluator.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ module Evaluator
1010
# QualifiedName
1111
# Default (produced :default)
1212
# Regular Expression (produces ruby regular expression)
13-
#
14-
# Not considered literal
15-
# QualifiedReference # i.e. File, FooBar
13+
# QualifiedReference
14+
# AccessExpresion
1615
#
1716
class LiteralEvaluator
1817

@@ -66,6 +65,16 @@ def literal_LiteralRegularExpression(o)
6665
o.value
6766
end
6867

68+
def literal_QualifiedReference(o)
69+
o.value
70+
end
71+
72+
def literal_AccessExpression(o)
73+
# to prevent parameters with [[]] like Optional[[String]]
74+
throw :not_literal if o.keys.size == 1 && o.keys[0].is_a?(Model::LiteralList)
75+
o.keys.map { |v| literal(v) }
76+
end
77+
6978
def literal_ConcatenatedString(o)
7079
# use double quoted string value if there is no interpolation
7180
throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Model::LiteralString)

lib/puppet/pops/issues.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ def self.hard_issue(issue_code, *args, &block)
206206
_("The numeric parameter name '$%{name}' cannot be used (clashes with numeric match result variables)") % { name: name }
207207
end
208208

209+
ILLEGAL_NONLITERAL_PARAMETER_TYPE = issue :ILLEGAL_NONLITERAL_PARAMETER_TYPE, :name, :type_class do
210+
_("The parameter '$%{name}' must be a literal type, not %{type_class}") % { name: name, type_class: label.a_an(type_class) }
211+
end
212+
209213
# In certain versions of Puppet it may be allowed to assign to a not already assigned key
210214
# in an array or a hash. This is an optional validation that may be turned on to prevent accidental
211215
# mutation.

lib/puppet/pops/validation/checker4_0.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ def check_HostClassDefinition(o)
468468
internal_check_parameter_name_uniqueness(o)
469469
internal_check_reserved_params(o)
470470
internal_check_no_idem_last(o)
471+
internal_check_parameter_type_literal(o)
471472
end
472473

473474
def check_ResourceTypeDefinition(o)
@@ -478,6 +479,18 @@ def check_ResourceTypeDefinition(o)
478479
internal_check_no_idem_last(o)
479480
end
480481

482+
def internal_check_parameter_type_literal(o)
483+
o.parameters.each do |p|
484+
next unless p.type_expr
485+
486+
type = nil
487+
catch :not_literal do
488+
type = literal(p.type_expr)
489+
end
490+
acceptor.accept(Issues::ILLEGAL_NONLITERAL_PARAMETER_TYPE, p, { name: p.name, type_class: p.type_expr.class }) if type.nil?
491+
end
492+
end
493+
481494
def internal_check_return_type(o)
482495
r = o.return_type
483496
internal_check_type_ref(o, r) unless r.nil?

spec/unit/info_service_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ class Borked($Herp+$Derp) {}
313313
CODE
314314
'json_unsafe.pp' => <<-CODE,
315315
class json_unsafe($arg1 = /.*/, $arg2 = default, $arg3 = {1 => 1}) {}
316+
CODE
317+
'non_literal.pp' => <<-CODE,
318+
class oops(Integer[1-3] $bad_int) { }
319+
CODE
320+
'non_literal_2.pp' => <<-CODE,
321+
class oops_2(Optional[[String]] $double_brackets) { }
316322
CODE
317323
})
318324
end
@@ -509,6 +515,19 @@ class json_unsafe($arg1 = /.*/, $arg2 = default, $arg3 = {1 => 1}) {}
509515
})
510516
end
511517

518+
it "errors with a descriptive message if non-literal class parameter is given" do
519+
files = ['non_literal.pp', 'non_literal_2.pp'].map {|f| File.join(code_dir, f) }
520+
result = Puppet::InfoService.classes_per_environment({'production' => files })
521+
expect(result).to eq({
522+
"production"=>{
523+
"#{code_dir}/non_literal.pp" =>
524+
{: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)"},
525+
"#{code_dir}/non_literal_2.pp" =>
526+
{: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)"}
527+
} # end production env
528+
})
529+
end
530+
512531
it "produces no type entry if type is not given" do
513532
files = ['fum.pp'].map {|f| File.join(code_dir, f) }
514533
result = Puppet::InfoService.classes_per_environment({'production' => files })

spec/unit/pops/evaluator/literal_evaluator_spec.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
'"a"' => 'a',
1717
'a' => 'a',
1818
'a::b' => 'a::b',
19+
'Integer[1]' => [1],
20+
'File' => "file",
1921

2022
# special values
2123
'default' => :default,
@@ -35,9 +37,9 @@
3537
expect(leval.literal(parser.parse_string('undef'))).to be_nil
3638
end
3739

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

0 commit comments

Comments
 (0)