puppet 3.7.5-x64-mingw32 → 3.8.1-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/ext/build_defaults.yaml +5 -5
  3. data/lib/hiera/puppet_function.rb +15 -4
  4. data/lib/puppet.rb +5 -2
  5. data/lib/puppet/application/agent.rb +5 -0
  6. data/lib/puppet/application/apply.rb +5 -0
  7. data/lib/puppet/application/device.rb +8 -3
  8. data/lib/puppet/application/master.rb +5 -0
  9. data/lib/puppet/defaults.rb +8 -0
  10. data/lib/puppet/error.rb +27 -1
  11. data/lib/puppet/file_system.rb +13 -0
  12. data/lib/puppet/file_system/file19windows.rb +8 -0
  13. data/lib/puppet/file_system/file_impl.rb +4 -0
  14. data/lib/puppet/file_system/memory_impl.rb +4 -0
  15. data/lib/puppet/functions.rb +25 -3
  16. data/lib/puppet/functions/defined.rb +130 -0
  17. data/lib/puppet/functions/hiera_include.rb +1 -1
  18. data/lib/puppet/node/environment.rb +4 -0
  19. data/lib/puppet/parser/compiler.rb +5 -2
  20. data/lib/puppet/parser/functions/defined.rb +26 -1
  21. data/lib/puppet/parser/functions/file.rb +3 -1
  22. data/lib/puppet/parser/templatewrapper.rb +2 -1
  23. data/lib/puppet/pops.rb +5 -0
  24. data/lib/puppet/pops/evaluator/access_operator.rb +25 -5
  25. data/lib/puppet/pops/evaluator/collector_transformer.rb +1 -11
  26. data/lib/puppet/pops/evaluator/compare_operator.rb +43 -0
  27. data/lib/puppet/pops/evaluator/evaluator_impl.rb +43 -28
  28. data/lib/puppet/pops/evaluator/runtime3_support.rb +9 -5
  29. data/lib/puppet/pops/functions/dispatch.rb +6 -1
  30. data/lib/puppet/pops/issue_reporter.rb +42 -16
  31. data/lib/puppet/pops/issues.rb +96 -0
  32. data/lib/puppet/pops/loader/module_loaders.rb +3 -1
  33. data/lib/puppet/pops/loaders.rb +6 -4
  34. data/lib/puppet/pops/migration/migration_checker.rb +45 -0
  35. data/lib/puppet/pops/model/factory.rb +1 -1
  36. data/lib/puppet/pops/model/model_meta.rb +1 -1
  37. data/lib/puppet/pops/parser/egrammar.ra +1 -1
  38. data/lib/puppet/pops/parser/eparser.rb +1 -1
  39. data/lib/puppet/pops/parser/epp_support.rb +18 -9
  40. data/lib/puppet/pops/parser/evaluating_parser.rb +7 -1
  41. data/lib/puppet/pops/parser/heredoc_support.rb +12 -11
  42. data/lib/puppet/pops/parser/interpolation_support.rb +7 -1
  43. data/lib/puppet/pops/parser/lexer2.rb +8 -8
  44. data/lib/puppet/pops/parser/lexer_support.rb +46 -20
  45. data/lib/puppet/pops/parser/parser_support.rb +11 -14
  46. data/lib/puppet/pops/parser/slurp_support.rb +22 -6
  47. data/lib/puppet/pops/types/type_calculator.rb +156 -55
  48. data/lib/puppet/pops/types/type_factory.rb +67 -14
  49. data/lib/puppet/pops/types/type_parser.rb +22 -13
  50. data/lib/puppet/pops/types/types.rb +21 -3
  51. data/lib/puppet/pops/types/types_meta.rb +13 -2
  52. data/lib/puppet/pops/validation.rb +25 -2
  53. data/lib/puppet/pops/validation/checker4_0.rb +25 -5
  54. data/lib/puppet/provider/group/windows_adsi.rb +18 -6
  55. data/lib/puppet/provider/mount/parsed.rb +145 -2
  56. data/lib/puppet/provider/package/pip.rb +4 -5
  57. data/lib/puppet/provider/package/zypper.rb +17 -7
  58. data/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +35 -10
  59. data/lib/puppet/provider/service/init.rb +7 -0
  60. data/lib/puppet/provider/user/windows_adsi.rb +8 -1
  61. data/lib/puppet/provider/zpool/zpool.rb +7 -2
  62. data/lib/puppet/resource.rb +1 -1
  63. data/lib/puppet/type/group.rb +1 -1
  64. data/lib/puppet/type/mount.rb +14 -3
  65. data/lib/puppet/type/scheduled_task.rb +21 -6
  66. data/lib/puppet/util/log.rb +50 -8
  67. data/lib/puppet/util/log/destinations.rb +23 -2
  68. data/lib/puppet/util/logging.rb +37 -1
  69. data/lib/puppet/util/windows/adsi.rb +36 -11
  70. data/lib/puppet/version.rb +1 -1
  71. data/spec/fixtures/unit/provider/mount/parsed/aix.filesystems +93 -85
  72. data/spec/fixtures/unit/provider/mount/parsed/aix.mount +11 -7
  73. data/spec/integration/parser/collector_spec.rb +7 -0
  74. data/spec/integration/parser/future_compiler_spec.rb +9 -0
  75. data/spec/integration/parser/resource_expressions_spec.rb +3 -0
  76. data/spec/unit/file_system_spec.rb +38 -0
  77. data/spec/unit/functions/defined_spec.rb +291 -0
  78. data/spec/unit/functions/hiera_spec.rb +8 -6
  79. data/spec/unit/functions4_spec.rb +97 -2
  80. data/spec/unit/parser/functions/file_spec.rb +8 -2
  81. data/spec/unit/parser/functions/template_spec.rb +1 -1
  82. data/spec/unit/parser/templatewrapper_spec.rb +1 -1
  83. data/spec/unit/pops/evaluator/access_ops_spec.rb +19 -0
  84. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +61 -8
  85. data/spec/unit/pops/issues_spec.rb +16 -16
  86. data/spec/unit/pops/loaders/module_loaders_spec.rb +5 -0
  87. data/spec/unit/pops/migration_spec.rb +180 -0
  88. data/spec/unit/pops/parser/lexer2_spec.rb +152 -1
  89. data/spec/unit/pops/parser/parse_heredoc_spec.rb +26 -0
  90. data/spec/unit/pops/transformer/transform_calls_spec.rb +1 -1
  91. data/spec/unit/pops/types/type_calculator_spec.rb +204 -11
  92. data/spec/unit/pops/validation_spec.rb +66 -0
  93. data/spec/unit/provider/group/windows_adsi_spec.rb +65 -1
  94. data/spec/unit/provider/mount/parsed_spec.rb +31 -5
  95. data/spec/unit/provider/package/pip_spec.rb +19 -7
  96. data/spec/unit/provider/package/zypper_spec.rb +25 -14
  97. data/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +312 -70
  98. data/spec/unit/provider/service/base_spec.rb +42 -31
  99. data/spec/unit/provider/service/freebsd_spec.rb +1 -0
  100. data/spec/unit/provider/service/gentoo_spec.rb +1 -0
  101. data/spec/unit/provider/service/init_spec.rb +18 -0
  102. data/spec/unit/provider/service/openbsd_spec.rb +1 -0
  103. data/spec/unit/provider/service/redhat_spec.rb +1 -0
  104. data/spec/unit/provider/user/windows_adsi_spec.rb +21 -0
  105. data/spec/unit/provider/zpool/zpool_spec.rb +47 -10
  106. data/spec/unit/util/log_spec.rb +113 -0
  107. data/spec/unit/util/windows/adsi_spec.rb +106 -26
  108. metadata +10 -2
@@ -20,6 +20,8 @@ class Puppet::Pops::Parser::EvaluatingParser
20
20
  #
21
21
  begin
22
22
  assert_and_report(parser.parse_string(s))
23
+ rescue Puppet::ParseErrorWithIssue => e
24
+ raise e
23
25
  rescue Puppet::ParseError => e
24
26
  # TODO: This is not quite right, why does not the exception have the correct file?
25
27
  e.file = @file_source unless e.file.is_a?(String) && !e.file.empty?
@@ -56,12 +58,16 @@ class Puppet::Pops::Parser::EvaluatingParser
56
58
  end
57
59
 
58
60
  def evaluator
61
+ # Do not use the cached evaluator if this is a migration run
62
+ if (Puppet.lookup(:migration_checker) { nil })
63
+ return Puppet::Pops::Evaluator::EvaluatorImpl.new()
64
+ end
59
65
  @@evaluator ||= Puppet::Pops::Evaluator::EvaluatorImpl.new()
60
66
  @@evaluator
61
67
  end
62
68
 
63
69
  def convert_to_3x(object, scope)
64
- val = @@evaluator.convert(object, scope, nil)
70
+ val = evaluator.convert(object, scope, nil)
65
71
  end
66
72
 
67
73
  def validate(parse_result)
@@ -1,4 +1,5 @@
1
1
  module Puppet::Pops::Parser::HeredocSupport
2
+ include Puppet::Pops::Parser::LexerSupport
2
3
 
3
4
  # Pattern for heredoc `@(endtag[:syntax][/escapes])
4
5
  # Produces groups for endtag (group 1), syntax (group 2), and escapes (group 3)
@@ -14,13 +15,12 @@ module Puppet::Pops::Parser::HeredocSupport
14
15
 
15
16
  # scanner is at position before @(
16
17
  # find end of the heredoc spec
17
- str = scn.scan_until(/\)/) || lexer.lex_error("Unclosed parenthesis after '@(' followed by '#{followed_by}'")
18
+ str = scn.scan_until(/\)/) || lex_error(Puppet::Pops::Issues::HEREDOC_UNCLOSED_PARENTHESIS, :followed_by => followed_by)
18
19
  pos_after_heredoc = scn.pos
19
20
 
20
21
  # Note: allows '+' as separator in syntax, but this needs validation as empty segments are not allowed
21
- unless md = str.match(PATTERN_HEREDOC)
22
- lex_error("Invalid syntax in heredoc expected @(endtag[:syntax][/escapes])")
23
- end
22
+ md = str.match(PATTERN_HEREDOC)
23
+ lex_error(Puppet::Pops::Issues::HEREDOC_INVALID_SYNTAX) unless md
24
24
  endtag = md[1]
25
25
  syntax = md[2] || ''
26
26
  escapes = md[3]
@@ -33,7 +33,7 @@ module Puppet::Pops::Parser::HeredocSupport
33
33
  endtag = $1.strip
34
34
  end
35
35
 
36
- lexer.lex_error("Missing endtag in heredoc") unless endtag.length >= 1
36
+ lex_error(Puppet::Pops::Issues::HEREDOC_MISSING_ENDTAG) unless endtag.length >= 1
37
37
 
38
38
  resulting_escapes = []
39
39
  if escapes
@@ -41,7 +41,7 @@ module Puppet::Pops::Parser::HeredocSupport
41
41
 
42
42
  escapes = escapes.split('')
43
43
  unless escapes.length == escapes.uniq.length
44
- lex_error("An escape char for @() may only appear once. Got '#{escapes.join(', ')}")
44
+ lex_error(Puppet::Pops::Issues::HEREDOC_MULTIPLE_AT_ESCAPES, :escapes => escapes)
45
45
  end
46
46
  resulting_escapes = ["\\"]
47
47
  escapes.each do |e|
@@ -51,7 +51,7 @@ module Puppet::Pops::Parser::HeredocSupport
51
51
  when "L"
52
52
  resulting_escapes += ["\n", "\r\n"]
53
53
  else
54
- lex_error("Invalid heredoc escape char. Only t, r, n, s, u, L, $ allowed. Got '#{e}'")
54
+ lex_error(Puppet::Pops::Issues::HEREDOC_INVALID_ESCAPE, :actual => e)
55
55
  end
56
56
  end
57
57
  end
@@ -66,14 +66,14 @@ module Puppet::Pops::Parser::HeredocSupport
66
66
  if ctx[:newline_jump]
67
67
  scn.pos = ctx[:newline_jump]
68
68
  else
69
- scn.scan_until(/\n/) || lex_error("Heredoc without any following lines of text")
69
+ scn.scan_until(/\n/) || lex_error(Puppet::Pops::Issues::HEREDOC_WITHOUT_TEXT)
70
70
  end
71
71
  # offset 0 for the heredoc, and its line number
72
72
  heredoc_offset = scn.pos
73
73
  heredoc_line = locator.line_for_offset(heredoc_offset)-1
74
74
 
75
75
  # Compute message to emit if there is no end (to make it refer to the opening heredoc position).
76
- eof_message = positioned_message("Heredoc without end-tagged line")
76
+ eof_error = create_lex_error(Puppet::Pops::Issues::HEREDOC_WITHOUT_END_TAGGED_LINE)
77
77
 
78
78
  # Text from this position (+ lexing contexts offset for any preceding heredoc) is heredoc until a line
79
79
  # that terminates the heredoc is found.
@@ -82,7 +82,8 @@ module Puppet::Pops::Parser::HeredocSupport
82
82
  endline_pattern = /([[:blank:]]*)(?:([|])[[:blank:]]*)?(?:(\-)[[:blank:]]*)?#{Regexp.escape(endtag)}[[:blank:]]*\r?(?:\n|\z)/
83
83
  lines = []
84
84
  while !scn.eos? do
85
- one_line = scn.scan_until(/(?:\n|\z)/) || lexer.lex_error_without_pos(eof_message)
85
+ one_line = scn.scan_until(/(?:\n|\z)/)
86
+ raise eof_error unless one_line
86
87
  if md = one_line.match(endline_pattern)
87
88
  leading = md[1]
88
89
  has_margin = md[2] == '|'
@@ -116,7 +117,7 @@ module Puppet::Pops::Parser::HeredocSupport
116
117
  lines << one_line
117
118
  end
118
119
  end
119
- lex_error_without_pos(eof_message)
120
+ raise eof_error
120
121
  end
121
122
 
122
123
  # Produces the heredoc text string given the individual (unprocessed) lines as an array.
@@ -193,9 +193,15 @@ module Puppet::Pops::Parser::InterpolationSupport
193
193
  token_name = token[0]
194
194
  ctx[:after] = token_name
195
195
  if token_name == :RBRACE && ctx[:brace_count] == brace_count
196
- if queue.size - queue_size == 1
196
+ qlength = queue.size - queue_size
197
+ if qlength == 1
197
198
  # Single token is subject to replacement
198
199
  queue[-1] = transform_to_variable(queue[-1])
200
+ elsif qlength > 1 && [:DOT, :LBRACK].include?(queue[queue_size + 1][0])
201
+ # A first word, number of name token followed by '[' or '.' is subject to replacement
202
+ # But not for other operators such as ?, +, - etc. where user must use a $ before the name
203
+ # to get a variable
204
+ queue[queue_size] = transform_to_variable(queue[queue_size])
199
205
  end
200
206
  return
201
207
  end
@@ -213,7 +213,7 @@ class Puppet::Pops::Parser::Lexer2
213
213
  @scanner = StringScanner.new(string)
214
214
  @locator = locator || Puppet::Pops::Parser::Locator.locator(string, '')
215
215
  @lexing_context[:escapes] = escapes || UQ_ESCAPES
216
- @lexing_context[:uq_slurp_pattern] = (interpolate || !escapes.empty?) ? SLURP_UQ_PATTERN : SLURP_ALL_PATTERN
216
+ @lexing_context[:uq_slurp_pattern] = interpolate ? (escapes.include?('$') ? SLURP_UQ_PATTERN : SLURP_UQNE_PATTERN) : SLURP_ALL_PATTERN
217
217
  end
218
218
 
219
219
  # Convenience method, and for compatibility with older lexer. Use the lex_file instead.
@@ -274,7 +274,7 @@ class Puppet::Pops::Parser::Lexer2
274
274
  ctx = @lexing_context
275
275
  queue = @token_queue
276
276
 
277
- lex_error_without_pos("Internal Error: No string or file given to lexer to process.") unless scn
277
+ lex_error_without_pos(Puppet::Pops::Issues::NO_INPUT_TO_LEXER) unless scn
278
278
 
279
279
  scn.skip(PATTERN_WS)
280
280
 
@@ -533,7 +533,7 @@ class Puppet::Pops::Parser::Lexer2
533
533
  else
534
534
  # move to faulty position ('::<uc-letter>' was ok)
535
535
  scn.pos = scn.pos + 3
536
- lex_error("Illegal fully qualified class reference")
536
+ lex_error(Puppet::Pops::Issues::ILLEGAL_FULLY_QUALIFIED_CLASS_REFERENCE)
537
537
  end
538
538
  else
539
539
  value = scn.scan(PATTERN_BARE_WORD)
@@ -546,7 +546,7 @@ class Puppet::Pops::Parser::Lexer2
546
546
  else
547
547
  # move to faulty position ('::' was ok)
548
548
  scn.pos = scn.pos + 2
549
- lex_error("Illegal fully qualified name")
549
+ lex_error(Puppet::Pops::Issues::ILLEGAL_FULLY_QUALIFIED_NAME)
550
550
  end
551
551
  end
552
552
  else
@@ -578,7 +578,7 @@ class Puppet::Pops::Parser::Lexer2
578
578
  else
579
579
  # move to faulty position ([0-9] was ok)
580
580
  scn.pos = scn.pos + 1
581
- lex_error("Illegal number")
581
+ lex_error(Puppet::Pops::Issues::ILLEGAL_NUMBER)
582
582
  end
583
583
 
584
584
  when 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
@@ -594,9 +594,9 @@ class Puppet::Pops::Parser::Lexer2
594
594
  scn.pos = scn.pos + 1
595
595
  fully_qualified = scn.match?(/::/)
596
596
  if fully_qualified
597
- lex_error("Illegal fully qualified name")
597
+ lex_error(Puppet::Pops::Issues::ILLEGAL_FULLY_QUALIFIED_NAME)
598
598
  else
599
- lex_error("Illegal name or bare word")
599
+ lex_error(Puppet::Pops::Issues::ILLEGAL_NAME_OR_BARE_WORD)
600
600
  end
601
601
  end
602
602
 
@@ -608,7 +608,7 @@ class Puppet::Pops::Parser::Lexer2
608
608
  else
609
609
  # move to faulty position ([A-Z] was ok)
610
610
  scn.pos = scn.pos + 1
611
- lex_error("Illegal class reference")
611
+ lex_error(Puppet::Pops::Issues::ILLEGAL_CLASS_REFERENCE)
612
612
  end
613
613
 
614
614
  when "\n"
@@ -3,18 +3,6 @@
3
3
  #
4
4
  module Puppet::Pops::Parser::LexerSupport
5
5
 
6
- # Formats given message by appending file, line and position if available.
7
- def positioned_message(msg, pos = nil)
8
- result = [msg]
9
- file = @locator.file
10
- line = @locator.line_for_offset(pos || @scanner.pos)
11
- pos = @locator.pos_on_line(pos || @scanner.pos)
12
-
13
- result << "in file #{file}" if file && file.is_a?(String) && !file.empty?
14
- result << "at line #{line}:#{pos}"
15
- result.join(" ")
16
- end
17
-
18
6
  # Returns "<eof>" if at end of input, else the following 5 characters with \n \r \t escaped
19
7
  def followed_by
20
8
  return "<eof>" if @scanner.eos?
@@ -35,13 +23,51 @@ module Puppet::Pops::Parser::LexerSupport
35
23
  end
36
24
 
37
25
  # Raises a Puppet::LexError with the given message
38
- def lex_error_without_pos msg
39
- raise Puppet::LexError.new(msg)
26
+ def lex_error_without_pos(issue, args = {})
27
+ raise Puppet::ParseErrorWithIssue.new(issue.format(args), nil, nil, nil, nil, issue.issue_code)
40
28
  end
41
29
 
42
- # Raises a Puppet::LexError with the given message
43
- def lex_error(msg, pos=nil)
44
- raise Puppet::LexError.new(positioned_message(msg, pos))
30
+ # Raises a Puppet::ParserErrorWithIssue with the given issue and arguments
31
+ def lex_error(issue, args = {}, pos=nil)
32
+ raise create_lex_error(issue, args, pos)
33
+ end
34
+
35
+ def filename
36
+ file = @locator.file
37
+ file.is_a?(String) && !file.empty? ? file : nil
38
+ end
39
+
40
+ def line(pos)
41
+ @locator.line_for_offset(pos || @scanner.pos)
42
+ end
43
+
44
+ def position(pos)
45
+ @locator.pos_on_line(pos || @scanner.pos)
46
+ end
47
+
48
+ def lex_warning(issue, args = {}, pos=nil)
49
+ Puppet::Util::Log.create({
50
+ :level => :warning,
51
+ :message => issue.format(args),
52
+ :issue_code => issue.issue_code,
53
+ :file => filename,
54
+ :line => line(pos),
55
+ :pos => position(pos),
56
+ })
57
+ end
58
+
59
+ # @param issue [Puppet::Pops::Issues::Issue] the issue
60
+ # @param args [Hash<Symbol,String>] Issue arguments
61
+ # @param pos [Integer]
62
+ # @return [Puppet::ParseErrorWithIssue] the created error
63
+ def create_lex_error(issue, args = {}, pos = nil)
64
+ Puppet::ParseErrorWithIssue.new(
65
+ issue.format(args),
66
+ filename,
67
+ line(pos),
68
+ position(pos),
69
+ nil,
70
+ issue.issue_code)
45
71
  end
46
72
 
47
73
  # Asserts that the given string value is a float, or an integer in decimal, octal or hex form.
@@ -49,13 +75,13 @@ module Puppet::Pops::Parser::LexerSupport
49
75
  #
50
76
  def assert_numeric(value, length)
51
77
  if value =~ /^0[xX].*$/
52
- lex_error("Not a valid hex number #{value}", length) unless value =~ /^0[xX][0-9A-Fa-f]+$/
78
+ lex_error(Puppet::Pops::Issues::INVALID_HEX_NUMBER, {:value => value}, length) unless value =~ /^0[xX][0-9A-Fa-f]+$/
53
79
 
54
80
  elsif value =~ /^0[^.].*$/
55
- lex_error("Not a valid octal number #{value}", length) unless value =~ /^0[0-7]+$/
81
+ lex_error(Puppet::Pops::Issues::INVALID_OCTAL_NUMBER, {:value => value}, length) unless value =~ /^0[0-7]+$/
56
82
 
57
83
  else
58
- lex_error("Not a valid decimal number #{value}", length) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/
84
+ lex_error(Puppet::Pops::Issues::INVALID_DECIMAL_NUMBER, {:value => value}, length) unless value =~ /0?\d+(?:\.\d+)?(?:[eE]-?\d+)?/
59
85
  end
60
86
  end
61
87
 
@@ -93,11 +93,8 @@ class Puppet::Pops::Parser::Parser
93
93
  else
94
94
  value_at = "'#{value[:value]}'"
95
95
  end
96
- if @yydebug
97
- error = "Syntax error at #{value_at}, token: #{token}"
98
- else
99
- error = "Syntax error at #{value_at}"
100
- end
96
+ error = Puppet::Pops::Issues::SYNTAX_ERROR.format(:where => value_at)
97
+ error = "#{error}, token: #{token}" if @yydebug
101
98
 
102
99
  # Note, old parser had processing of "expected token here" - do not try to reinstate:
103
100
  # The 'expected' is only of value at end of input, otherwise any parse error involving a
@@ -110,19 +107,19 @@ class Puppet::Pops::Parser::Parser
110
107
  # must be handled by the grammar. The lexer may have enqueued tokens far ahead - the lexer's opinion about this
111
108
  # is not trustworthy.
112
109
  #
113
-
114
- except = Puppet::ParseError.new(error)
110
+ file = nil
111
+ line = nil
112
+ pos = nil
115
113
  if token != 0
116
- path = value[:file]
117
- except.line = value[:line]
118
- except.pos = value[:pos]
114
+ file = value[:file]
115
+ line = value[:line]
116
+ pos = value[:pos]
119
117
  else
120
118
  # At end of input, use what the lexer thinks is the source file
121
- path = lexer.file
119
+ file = lexer.file
122
120
  end
123
- except.file = path if path.is_a?(String) && !path.empty?
124
-
125
- raise except
121
+ file = nil unless file.is_a?(String) && !file.empty?
122
+ raise Puppet::ParseErrorWithIssue.new(error, file, line, pos, nil, issue_code = Puppet::Pops::Issues::SYNTAX_ERROR.issue_code)
126
123
  end
127
124
 
128
125
  # Parses a String of pp DSL code.
@@ -7,10 +7,13 @@
7
7
  # TODO: More detailed performance analysis of excessive character escaping and interpolation.
8
8
  #
9
9
  module Puppet::Pops::Parser::SlurpSupport
10
+ include Puppet::Pops::Parser::LexerSupport
10
11
 
11
12
  SLURP_SQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*[']/
12
13
  SLURP_DQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*(["]|[$]\{?)/
13
14
  SLURP_UQ_PATTERN = /(?:[^\\]|^|[^\\])(?:[\\]{2})*([$]\{?|\z)/
15
+ # unquoted, no escapes
16
+ SLURP_UQNE_PATTERN = /(\$\{?|\z)/m
14
17
  SLURP_ALL_PATTERN = /.*(\z)/m
15
18
  SQ_ESCAPES = %w{ \\ ' }
16
19
  DQ_ESCAPES = %w{ \\ $ ' " r n t s u}+["\r\n", "\n"]
@@ -19,7 +22,8 @@ module Puppet::Pops::Parser::SlurpSupport
19
22
  def slurp_sqstring
20
23
  # skip the leading '
21
24
  @scanner.pos += 1
22
- str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes) || lex_error("Unclosed quote after \"'\" followed by '#{followed_by}'")
25
+ str = slurp(@scanner, SLURP_SQ_PATTERN, SQ_ESCAPES, :ignore_invalid_escapes)
26
+ lex_error(Puppet::Pops::Issues::UNCLOSED_QUOTE, :after => "\"'\"", :followed_by => followed_by) unless str
23
27
  str[0..-2] # strip closing "'" from result
24
28
  end
25
29
 
@@ -28,7 +32,7 @@ module Puppet::Pops::Parser::SlurpSupport
28
32
  last = scn.matched
29
33
  str = slurp(scn, SLURP_DQ_PATTERN, DQ_ESCAPES, false)
30
34
  unless str
31
- lex_error("Unclosed quote after #{format_quote(last)} followed by '#{followed_by}'")
35
+ lex_error(Puppet::Pops::Issues::UNCLOSED_QUOTE, :after => format_quote(last), :followed_by => followed_by)
32
36
  end
33
37
 
34
38
  # Terminator may be a single char '"', '$', or two characters '${' group match 1 (scn[1]) from the last slurp holds this
@@ -41,6 +45,7 @@ module Puppet::Pops::Parser::SlurpSupport
41
45
  scn = @scanner
42
46
  last = scn.matched
43
47
  ignore = true
48
+
44
49
  str = slurp(scn, @lexing_context[:uq_slurp_pattern], @lexing_context[:escapes], :ignore_invalid_escapes)
45
50
 
46
51
  # Terminator may be a single char '$', two characters '${', or empty string '' at the end of intput.
@@ -65,11 +70,12 @@ module Puppet::Pops::Parser::SlurpSupport
65
70
  # Process unicode escapes first as they require getting 4 hex digits
66
71
  # If later a \u is found it is warned not to be a unicode escape
67
72
  if escapes.include?('u')
68
- str.gsub!(/\\u([\da-fA-F]{4})/m) {
69
- [$1.hex].pack("U")
73
+ str.gsub!(/\\u(?:([\da-fA-F]{4})|\{([\da-fA-F]{1,6})\})/m) {
74
+ [($1 || $2).hex].pack("U")
70
75
  }
71
76
  end
72
77
 
78
+ begin
73
79
  str.gsub!(/\\([^\r\n]|(?:\r?\n))/m) {
74
80
  ch = $1
75
81
  if escapes.include? ch
@@ -79,17 +85,27 @@ module Puppet::Pops::Parser::SlurpSupport
79
85
  when 't' ; "\t"
80
86
  when 's' ; " "
81
87
  when 'u'
82
- Puppet.warning(positioned_message("Unicode escape '\\u' was not followed by 4 hex digits"))
88
+ lex_warning(Puppet::Pops::Issues::ILLEGAL_UNICODE_ESCAPE)
83
89
  "\\u"
84
90
  when "\n" ; ''
85
91
  when "\r\n"; ''
86
92
  else ch
87
93
  end
88
94
  else
89
- Puppet.warning(positioned_message("Unrecognized escape sequence '\\#{ch}'")) unless ignore_invalid_escapes
95
+ lex_warning(Puppet::Pops::Issues::UNRECOGNIZED_ESCAPE, :ch => ch) unless ignore_invalid_escapes
90
96
  "\\#{ch}"
91
97
  end
92
98
  }
99
+ rescue ArgumentError => e
100
+ # A invalid byte sequence may be the result of faulty input as well, but that could not possibly
101
+ # have reached this far... Unfortunately there is no more specific error and a match on message is
102
+ # required to differentiate from other internal problems.
103
+ if e.message =~ /invalid byte sequence/
104
+ lex_error(Puppet::Pops::Issues::ILLEGAL_UNICODE_ESCAPE)
105
+ else
106
+ raise e
107
+ end
108
+ end
93
109
  str
94
110
  end
95
111
  end
@@ -269,14 +269,21 @@ class Puppet::Pops::Types::TypeCalculator
269
269
  if t2.is_a?(Class)
270
270
  t2 = type(t2)
271
271
  end
272
+ t2_class = t2.class
273
+
272
274
  # Unit can be assigned to anything
273
- return true if t2.class == Types::PUnitType
275
+ return true if t2_class == Types::PUnitType
274
276
 
275
- if t2.class == Types::PVariantType
277
+ if t2_class == Types::PVariantType
276
278
  # Assignable if all contained types are assignable
277
279
  t2.types.all? { |vt| @@assignable_visitor.visit_this_1(self, t, vt) }
278
280
  else
279
- @@assignable_visitor.visit_this_1(self, t, t2)
281
+ # Turn NotUndef[T] into T when T is not assignable from Undef
282
+ if t2_class == Types::PNotUndefType && !(t2.type.nil? || assignable?(t2.type, @nil_t))
283
+ assignable?(t, t2.type)
284
+ else
285
+ @@assignable_visitor.visit_this_1(self, t, t2)
286
+ end
280
287
  end
281
288
  end
282
289
 
@@ -354,10 +361,23 @@ class Puppet::Pops::Types::TypeCalculator
354
361
  # do nothing, there is nothing to change for most types
355
362
  end
356
363
 
364
+ # @return [Boolean] true if the given argument is contained in a struct element key
365
+ def is_struct_element_key?(o)
366
+ c = o.eContainer
367
+ if c.is_a?(Types::POptionalType)
368
+ o = c
369
+ c = c.eContainer
370
+ end
371
+ c.is_a?(Types::PStructElement) && c.key_type.equal?(o)
372
+ end
373
+ private :is_struct_element_key?
374
+
357
375
  def generalize_PStringType(o)
358
- o.values = []
359
- o.size_type = nil
360
- []
376
+ # Skip generalization if the string is contained in a PStructElement key.
377
+ unless is_struct_element_key?(o)
378
+ o.values = []
379
+ o.size_type = nil
380
+ end
361
381
  end
362
382
 
363
383
  def generalize_PCollectionType(o)
@@ -434,8 +454,13 @@ class Puppet::Pops::Types::TypeCalculator
434
454
  def instance_of_PStringType(t, o)
435
455
  return false unless o.is_a?(String)
436
456
  # true if size compliant
437
- size_t = t.size_type || @collection_default_size_t
438
- instance_of_PIntegerType(size_t, o.size)
457
+ size_t = t.size_type
458
+ if size_t.nil? || instance_of_PIntegerType(size_t, o.size)
459
+ values = t.values
460
+ values.empty? || values.include?(o)
461
+ else
462
+ false
463
+ end
439
464
  end
440
465
 
441
466
  def instance_of_PTupleType(t, o)
@@ -452,9 +477,18 @@ class Puppet::Pops::Types::TypeCalculator
452
477
 
453
478
  def instance_of_PStructType(t, o)
454
479
  return false unless o.is_a?(Hash)
455
- h = t.hashed_elements
456
- # all keys must be present and have a value (even if nil/undef)
457
- (o.keys - h.keys).empty? && h.all? { |k,v| instance_of(v, o[k]) }
480
+ matched = 0
481
+ t.elements.all? do |e|
482
+ key = e.name
483
+ v = o[key]
484
+ if v.nil? && !o.include?(key)
485
+ # Entry is missing. Only OK when key is optional
486
+ assignable?(e.key_type, @nil_t)
487
+ else
488
+ matched += 1
489
+ instance_of(e.value_type, v)
490
+ end
491
+ end && matched == o.size
458
492
  end
459
493
 
460
494
  def instance_of_PHashType(t, o)
@@ -471,6 +505,10 @@ class Puppet::Pops::Types::TypeCalculator
471
505
  instance_of(@data_variant_t, o)
472
506
  end
473
507
 
508
+ def instance_of_PNotUndefType(t, o)
509
+ !(o.nil? || o == :undef) && (t.type.nil? || instance_of(t.type, o))
510
+ end
511
+
474
512
  def instance_of_PUndefType(t, o)
475
513
  o.nil? || o == :undef
476
514
  end
@@ -598,7 +636,8 @@ class Puppet::Pops::Types::TypeCalculator
598
636
 
599
637
  if t1.is_a?(Types::PStringType) && t2.is_a?(Types::PStringType)
600
638
  t = Types::PStringType.new()
601
- t.values = t1.values | t2.values
639
+ t.values = t1.values | t2.values unless t1.values.empty? || t2.values.empty?
640
+ t.size_type = common_type(t1.size_type, t2.size_type) unless t1.size_type.nil? || t2.size_type.nil?
602
641
  return t
603
642
  end
604
643
 
@@ -937,27 +976,23 @@ class Puppet::Pops::Types::TypeCalculator
937
976
  type.key_type = Types::PUndefType.new
938
977
  type.element_type = Types::PUndefType.new
939
978
  type.size_type = size_as_type(o)
940
- else
941
- if o.keys.find {|k| !instance_of_PStringType(@non_empty_string_t, k) }
942
- type = Types::PHashType.new
943
- ktype = Types::PVariantType.new
944
- ktype.types = o.keys.map {|k| infer_set(k) }
945
- etype = Types::PVariantType.new
946
- etype.types = o.values.map {|e| infer_set(e) }
947
- type.key_type = unwrap_single_variant(ktype)
948
- type.element_type = unwrap_single_variant(etype)
949
- type.size_type = size_as_type(o)
950
- else
951
- elements = []
952
- o.each_pair do |k,v|
953
- element = Types::PStructElement.new
954
- element.name = k
955
- element.type = infer_set(v)
956
- elements << element
957
- end
958
- type = Types::PStructType.new
959
- type.elements = elements
979
+ elsif o.keys.all? {|k| instance_of_PStringType(@non_empty_string_t, k) }
980
+ type = Types::PStructType.new
981
+ type.elements = o.map do |k,v|
982
+ element = Types::PStructElement.new
983
+ element.key_type = infer_String(k)
984
+ element.value_type = infer_set(v)
985
+ element
960
986
  end
987
+ else
988
+ type = Types::PHashType.new
989
+ ktype = Types::PVariantType.new
990
+ ktype.types = o.keys.map {|k| infer_set(k) }
991
+ etype = Types::PVariantType.new
992
+ etype.types = o.values.map {|e| infer_set(e) }
993
+ type.key_type = unwrap_single_variant(ktype)
994
+ type.element_type = unwrap_single_variant(etype)
995
+ type.size_type = size_as_type(o)
961
996
  end
962
997
  type
963
998
  end
@@ -981,6 +1016,11 @@ class Puppet::Pops::Types::TypeCalculator
981
1016
  t2.is_a?(Types::PAnyType)
982
1017
  end
983
1018
 
1019
+ # @api private
1020
+ def assignable_PNotUndefType(t, t2)
1021
+ !assignable?(t2, @nil_t) && (t.type.nil? || assignable?(t.type, t2))
1022
+ end
1023
+
984
1024
  # @api private
985
1025
  def assignable_PUndefType(t, t2)
986
1026
  # Only undef/nil is assignable to nil type
@@ -1110,6 +1150,17 @@ class Puppet::Pops::Types::TypeCalculator
1110
1150
  end
1111
1151
  end
1112
1152
 
1153
+ # @api private
1154
+ def self.is_kind_of_optional?(t, optional = true)
1155
+ case t
1156
+ when Types::POptionalType
1157
+ true
1158
+ when Types::PVariantType
1159
+ t.types.all? {|t2| is_kind_of_optional?(t2, optional) }
1160
+ else
1161
+ false
1162
+ end
1163
+ end
1113
1164
 
1114
1165
  def callable_PArrayType(args_array, callable_t)
1115
1166
  return false unless assignable?(callable_t.param_types, args_array)
@@ -1193,22 +1244,34 @@ class Puppet::Pops::Types::TypeCalculator
1193
1244
  #
1194
1245
  def assignable_PStructType(t, t2)
1195
1246
  if t2.is_a?(Types::PStructType)
1196
- h = t.hashed_elements
1197
1247
  h2 = t2.hashed_elements
1198
- (h2.keys - h.keys).empty? && h.all? {|k, v| v2 = h2[k]; assignable?(v, v2.nil? ? @nil_t : v2) }
1248
+ matched = 0
1249
+ t.elements.all? do |e1|
1250
+ e2 = h2[e1.name]
1251
+ if e2.nil?
1252
+ assignable?(e1.key_type, @nil_t)
1253
+ else
1254
+ matched += 1
1255
+ assignable?(e1.key_type, e2.key_type) && assignable?(e1.value_type, e2.value_type)
1256
+ end
1257
+ end && matched == h2.size
1199
1258
  elsif t2.is_a?(Types::PHashType)
1200
- size_t2 = t2.size_type || @collection_default_size_t
1201
- size_t = Types::PIntegerType.new
1202
- elements = t.elements
1203
- size_t.from = elements.count {|e| !assignable?(e.type, @nil_t) }
1204
- size_t.to = elements.size
1205
- # compatible size
1206
- # hash key type must be string of min 1 size
1207
- # hash value t must be assignable to each key
1208
- element_type = t2.element_type
1209
- assignable_PIntegerType(size_t, size_t2) &&
1210
- (size_t2.to == 0 || assignable?(@non_empty_string_t, t2.key_type)) &&
1211
- elements.all? {|e| assignable?(e.type, element_type) }
1259
+ required = 0
1260
+ required_elements_assignable = t.elements.all? do |e|
1261
+ if assignable?(e.key_type, @nil_t)
1262
+ true
1263
+ else
1264
+ required += 1
1265
+ assignable?(e.value_type, t2.element_type)
1266
+ end
1267
+ end
1268
+ if required_elements_assignable
1269
+ size_t2 = t2.size_type || @collection_default_size_t
1270
+ size_t = Types::PIntegerType.new
1271
+ size_t.from = required
1272
+ size_t.to = t.elements.size
1273
+ assignable_PIntegerType(size_t, size_t2)
1274
+ end
1212
1275
  else
1213
1276
  false
1214
1277
  end
@@ -1217,8 +1280,9 @@ class Puppet::Pops::Types::TypeCalculator
1217
1280
  # @api private
1218
1281
  def assignable_POptionalType(t, t2)
1219
1282
  return true if t2.is_a?(Types::PUndefType)
1283
+ return true if t.optional_type.nil?
1220
1284
  if t2.is_a?(Types::POptionalType)
1221
- assignable?(t.optional_type, t2.optional_type)
1285
+ assignable?(t.optional_type, t2.optional_type || @t)
1222
1286
  else
1223
1287
  assignable?(t.optional_type, t2)
1224
1288
  end
@@ -1389,11 +1453,11 @@ class Puppet::Pops::Types::TypeCalculator
1389
1453
  # @api private
1390
1454
  def assignable_PArrayType(t, t2)
1391
1455
  if t2.is_a?(Types::PArrayType)
1392
- return false unless assignable?(t.element_type, t2.element_type)
1456
+ return false unless t.element_type.nil? || assignable?(t.element_type, t2.element_type || @t)
1393
1457
  assignable_PCollectionType(t, t2)
1394
1458
 
1395
1459
  elsif t2.is_a?(Types::PTupleType)
1396
- return false unless t2.types.all? {|t2_element| assignable?(t.element_type, t2_element) }
1460
+ return false unless t.element_type.nil? || t2.types.all? {|t2_element| assignable?(t.element_type, t2_element) }
1397
1461
  t2_regular = t2.types[0..-2]
1398
1462
  t2_ranged = t2.types[-1]
1399
1463
  t2_from, t2_to = size_range(t2.size_type)
@@ -1431,7 +1495,8 @@ class Puppet::Pops::Types::TypeCalculator
1431
1495
  case t2
1432
1496
  when Types::PHashType
1433
1497
  return true if (t.size_type.nil? || t.size_type.from == 0) && t2.is_the_empty_hash?
1434
- return false unless assignable?(t.key_type, t2.key_type) && assignable?(t.element_type, t2.element_type)
1498
+ return false unless t.key_type.nil? || assignable?(t.key_type, t2.key_type || @t)
1499
+ return false unless t.element_type.nil? || assignable?(t.element_type, t2.element_type || @t)
1435
1500
  assignable_PCollectionType(t, t2)
1436
1501
  when Types::PStructType
1437
1502
  # hash must accept String as key type
@@ -1443,7 +1508,7 @@ class Puppet::Pops::Types::TypeCalculator
1443
1508
  key_type = t.key_type
1444
1509
  element_type = t.element_type
1445
1510
  ( struct_size >= min && struct_size <= max &&
1446
- t2.elements.all? {|e| instance_of(key_type, e.name) && assignable?(element_type, e.type) })
1511
+ t2.elements.all? {|e| (key_type.nil? || instance_of(key_type, e.name)) && (element_type.nil? || assignable?(element_type, e.value_type)) })
1447
1512
  else
1448
1513
  false
1449
1514
  end
@@ -1475,7 +1540,15 @@ class Puppet::Pops::Types::TypeCalculator
1475
1540
  # Data is assignable by other Data and by Array[Data] and Hash[Scalar, Data]
1476
1541
  # @api private
1477
1542
  def assignable_PDataType(t, t2)
1478
- t2.is_a?(Types::PDataType) || assignable?(@data_variant_t, t2)
1543
+ # We cannot put the NotUndefType[Data] in the @data_variant_t since that causes an endless recursion
1544
+ case t2
1545
+ when Types::PDataType
1546
+ true
1547
+ when Types::PNotUndefType
1548
+ assignable?(t, t2.type || @t)
1549
+ else
1550
+ assignable?(@data_variant_t, t2)
1551
+ end
1479
1552
  end
1480
1553
 
1481
1554
  # Assignable if t2's has the same runtime and the runtime name resolves to
@@ -1646,7 +1719,16 @@ class Puppet::Pops::Types::TypeCalculator
1646
1719
  end
1647
1720
 
1648
1721
  def string_PStructElement(t)
1649
- "'#{t.name}'=>#{string(t.type)}"
1722
+ k = t.key_type
1723
+ value_optional = assignable?(t.value_type, @nil_t)
1724
+ key_string =
1725
+ if k.is_a?(Types::POptionalType)
1726
+ # Output as literal String
1727
+ value_optional ? "'#{t.name}'" : string(k)
1728
+ else
1729
+ value_optional ? "NotUndef['#{t.name}']" : "'#{t.name}'"
1730
+ end
1731
+ "#{key_string}=>#{string(t.value_type)}"
1650
1732
  end
1651
1733
 
1652
1734
  # @api private
@@ -1712,11 +1794,30 @@ class Puppet::Pops::Types::TypeCalculator
1712
1794
  end
1713
1795
  end
1714
1796
 
1797
+ # @api private
1798
+ def string_PNotUndefType(t)
1799
+ contained_type = t.type
1800
+ if contained_type.nil? || contained_type.class == Puppet::Pops::Types::PAnyType
1801
+ 'NotUndef'
1802
+ else
1803
+ if contained_type.is_a?(Puppet::Pops::Types::PStringType) && contained_type.values.size == 1
1804
+ "NotUndef['#{contained_type.values[0]}']"
1805
+ else
1806
+ "NotUndef[#{string(contained_type)}]"
1807
+ end
1808
+ end
1809
+ end
1810
+
1715
1811
  def string_POptionalType(t)
1716
- if t.optional_type.nil?
1812
+ optional_type = t.optional_type
1813
+ if optional_type.nil?
1717
1814
  "Optional"
1718
1815
  else
1719
- "Optional[#{string(t.optional_type)}]"
1816
+ if optional_type.is_a?(Puppet::Pops::Types::PStringType) && optional_type.values.size == 1
1817
+ "Optional['#{optional_type.values[0]}']"
1818
+ else
1819
+ "Optional[#{string(optional_type)}]"
1820
+ end
1720
1821
  end
1721
1822
  end
1722
1823