class Puppet::Pops::Validation::Checker4_0
A Validator validates a model.
Validation is performed on each model element in isolation. Each method should validate the model element's state but not validate its referenced/contained elements except to check their validity in their respective role. The intent is to drive the validation with a tree iterator that visits all elements in a model.
TODO: Add validation of multiplicities - this is a general validation that can be checked for all
Model objects via their metamodel. (I.e an extra call to multiplicity check in polymorph check). This is however mostly valuable when validating model to model transformations, and is therefore T.B.D
Constants
- BAD_MODULE_FILE
- FUTURE_RESERVED_WORDS
- NO_NAMESPACE
- NO_PATH
- RESERVED_PARAMETERS
- RESERVED_TYPE_NAMES
Attributes
Public Class Methods
# File lib/puppet/pops/validation/checker4_0.rb 24 def self.check_visitor 25 # Class instance variable rather than Class variable because methods visited 26 # may be overridden in subclass 27 @check_visitor ||= Visitor.new(nil, 'check', 0, 0) 28 end
Initializes the validator with a diagnostics producer. This object must respond to `:will_accept?` and `:accept`.
Puppet::Pops::Evaluator::LiteralEvaluator::new
# File lib/puppet/pops/validation/checker4_0.rb 33 def initialize(diagnostics_producer) 34 super() 35 @@rvalue_visitor ||= Visitor.new(nil, "rvalue", 0, 0) 36 @@hostname_visitor ||= Visitor.new(nil, "hostname", 1, 2) 37 @@assignment_visitor ||= Visitor.new(nil, "assign", 0, 1) 38 @@query_visitor ||= Visitor.new(nil, "query", 0, 0) 39 @@relation_visitor ||= Visitor.new(nil, "relation", 0, 0) 40 @@idem_visitor ||= Visitor.new(nil, "idem", 0, 0) 41 42 @check_visitor = self.class.check_visitor 43 @acceptor = diagnostics_producer 44 45 # Use null migration checker unless given in context 46 @migration_checker = (Puppet.lookup(:migration_checker) { Migration::MigrationChecker.new() }) 47 end
Public Instance Methods
Checks the LHS of an assignment (is it assignable?). If args is true, assignment via index is checked.
# File lib/puppet/pops/validation/checker4_0.rb 124 def assign(o, via_index = false) 125 @@assignment_visitor.visit_this_1(self, o, via_index) 126 end
# File lib/puppet/pops/validation/checker4_0.rb 165 def assign_AccessExpression(o, via_index) 166 # Are indexed assignments allowed at all ? $x[x] = '...' 167 if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT 168 acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o) 169 else 170 # Then the left expression must be assignable-via-index 171 assign(o.left_expr, true) 172 end 173 end
# File lib/puppet/pops/validation/checker4_0.rb 175 def assign_LiteralList(o, via_index) 176 o.values.each {|x| assign(x) } 177 end
# File lib/puppet/pops/validation/checker4_0.rb 179 def assign_Object(o, via_index) 180 # Can not assign to anything else (differentiate if this is via index or not) 181 # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases) 182 # 183 acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o) 184 end
# File lib/puppet/pops/validation/checker4_0.rb 149 def assign_VariableExpression(o, via_index) 150 varname_string = varname_to_s(o.expr) 151 if varname_string =~ Patterns::NUMERIC_VAR_NAME 152 acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string) 153 end 154 # Can not assign to something in another namespace (i.e. a '::' in the name is not legal) 155 if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT 156 if varname_string =~ /::/ 157 acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string) 158 end 159 end 160 161 # TODO: Could scan for reassignment of the same variable if done earlier in the same container 162 # Or if assigning to a parameter (more work). 163 end
Performs regular validity check
# File lib/puppet/pops/validation/checker4_0.rb 66 def check(o) 67 @check_visitor.visit_this_0(self, o) 68 end
# File lib/puppet/pops/validation/checker4_0.rb 195 def check_AccessExpression(o) 196 # Only min range is checked, all other checks are RT checks as they depend on the resulting type 197 # of the LHS. 198 if o.keys.size < 1 199 acceptor.accept(Issues::MISSING_INDEX, o) 200 end 201 end
# File lib/puppet/pops/validation/checker4_0.rb 203 def check_AssignmentExpression(o) 204 case o.operator 205 when '=' 206 assign(o.left_expr) 207 rvalue(o.right_expr) 208 when '+=', '-=' 209 acceptor.accept(Issues::APPENDS_DELETES_NO_LONGER_SUPPORTED, o, {:operator => o.operator}) 210 else 211 acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) 212 end 213 end
Checks that operation with :+> is contained in a ResourceOverride or Collector.
Parent of an AttributeOperation can be one of:
-
CollectExpression
-
ResourceOverride
-
ResourceBody (ILLEGAL this is a regular resource expression)
-
ResourceDefaults (ILLEGAL)
# File lib/puppet/pops/validation/checker4_0.rb 223 def check_AttributeOperation(o) 224 if o.operator == '+>' 225 # Append operator use is constrained 226 p = container 227 unless p.is_a?(Model::CollectExpression) || p.is_a?(Model::ResourceOverrideExpression) 228 acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, {:name=>o.attribute_name, :parent=>p}) 229 end 230 end 231 rvalue(o.value_expr) 232 end
# File lib/puppet/pops/validation/checker4_0.rb 234 def check_AttributesOperation(o) 235 # Append operator use is constrained 236 p = container 237 case p 238 when Model::AbstractResource 239 when Model::CollectExpression 240 else 241 # protect against just testing a snippet that has no parent, error message will be a bit strange 242 # but it is not for a real program. 243 parent2 = p.nil? ? o : container(-2) 244 unless parent2.is_a?(Model::AbstractResource) 245 acceptor.accept(Issues::UNSUPPORTED_OPERATOR_IN_CONTEXT, parent2, :operator=>'* =>') 246 end 247 end 248 rvalue(o.expr) 249 end
# File lib/puppet/pops/validation/checker4_0.rb 251 def check_BinaryExpression(o) 252 rvalue(o.left_expr) 253 rvalue(o.right_expr) 254 end
# File lib/puppet/pops/validation/checker4_0.rb 265 def check_BlockExpression(o) 266 if resource_without_title?(o) 267 acceptor.accept(Issues::RESOURCE_WITHOUT_TITLE, o, :name => o.statements[0].value) 268 else 269 o.statements[0..-2].each do |statement| 270 if idem(statement) 271 acceptor.accept(Issues::IDEM_EXPRESSION_NOT_LAST, statement) 272 break # only flag the first 273 end 274 end 275 end 276 end
# File lib/puppet/pops/validation/checker4_0.rb 278 def check_CallNamedFunctionExpression(o) 279 functor = o.functor_expr 280 if functor.is_a?(Model::QualifiedReference) || 281 functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference) 282 # ok (a call to a type) 283 return nil 284 end 285 case functor 286 when Model::QualifiedName 287 # ok 288 nil 289 when Model::RenderStringExpression 290 # helpful to point out this easy to make Epp error 291 acceptor.accept(Issues::ILLEGAL_EPP_PARAMETERS, o) 292 else 293 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) 294 end 295 end
# File lib/puppet/pops/validation/checker4_0.rb 319 def check_CaseExpression(o) 320 rvalue(o.test) 321 # There can only be one LiteralDefault case option value 322 found_default = false 323 o.options.each do |option| 324 option.values.each do |value| 325 if value.is_a?(Model::LiteralDefault) 326 # Flag the second default as 'unreachable' 327 acceptor.accept(Issues::DUPLICATE_DEFAULT, value, :container => o) if found_default 328 found_default = true 329 end 330 end 331 end 332 end
# File lib/puppet/pops/validation/checker4_0.rb 334 def check_CaseOption(o) 335 o.values.each { |v| rvalue(v) } 336 end
# File lib/puppet/pops/validation/checker4_0.rb 338 def check_CollectExpression(o) 339 unless o.type_expr.is_a? Model::QualifiedReference 340 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o) 341 end 342 end
# File lib/puppet/pops/validation/checker4_0.rb 297 def check_EppExpression(o) 298 p = container 299 if p.is_a?(Model::LambdaExpression) 300 internal_check_no_capture(p, o) 301 internal_check_parameter_name_uniqueness(p) 302 end 303 end
# File lib/puppet/pops/validation/checker4_0.rb 191 def check_Factory(o) 192 check(o.model) 193 end
# File lib/puppet/pops/validation/checker4_0.rb 460 def check_FunctionDefinition(o) 461 check_NamedDefinition(o) 462 internal_check_return_type(o) 463 internal_check_parameter_name_uniqueness(o) 464 end
# File lib/puppet/pops/validation/checker4_0.rb 305 def check_HeredocExpression(o) 306 # Only syntax check static text in heredoc during validation - dynamic text is validated by the evaluator. 307 expr = o.text_expr 308 if expr.is_a?(Model::LiteralString) 309 assert_external_syntax(nil, expr.value, o.syntax, o.text_expr) 310 end 311 end
# File lib/puppet/pops/validation/checker4_0.rb 466 def check_HostClassDefinition(o) 467 check_NamedDefinition(o) 468 internal_check_no_capture(o) 469 internal_check_parameter_name_uniqueness(o) 470 internal_check_reserved_params(o) 471 internal_check_no_idem_last(o) 472 end
# File lib/puppet/pops/validation/checker4_0.rb 684 def check_IfExpression(o) 685 rvalue(o.test) 686 end
# File lib/puppet/pops/validation/checker4_0.rb 688 def check_KeyedEntry(o) 689 rvalue(o.key) 690 rvalue(o.value) 691 # In case there are additional things to forbid than non-rvalues 692 # acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => container) 693 end
# File lib/puppet/pops/validation/checker4_0.rb 695 def check_LambdaExpression(o) 696 internal_check_capture_last(o) 697 internal_check_return_type(o) 698 end
# File lib/puppet/pops/validation/checker4_0.rb 711 def check_LiteralHash(o) 712 # the keys of a literal hash may be non-literal expressions. They cannot be checked. 713 unique = Set.new 714 o.entries.each do |entry| 715 catch(:not_literal) do 716 literal_key = literal(entry.key) 717 acceptor.accept(Issues::DUPLICATE_KEY, entry, {:key => literal_key}) if unique.add?(literal_key).nil? 718 end 719 end 720 end
# File lib/puppet/pops/validation/checker4_0.rb 704 def check_LiteralInteger(o) 705 v = o.value 706 if v < MIN_INTEGER || v > MAX_INTEGER 707 acceptor.accept(Issues::NUMERIC_OVERFLOW, o, {:value => v}) 708 end 709 end
# File lib/puppet/pops/validation/checker4_0.rb 700 def check_LiteralList(o) 701 o.values.each {|v| rvalue(v) } 702 end
# File lib/puppet/pops/validation/checker4_0.rb 313 def check_MethodCallExpression(o) 314 unless o.functor_expr.is_a? Model::QualifiedName 315 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o) 316 end 317 end
Only used for function names, grammar should not be able to produce something faulty, but check anyway if model is created programmatically (it will fail in transformation to AST for sure).
# File lib/puppet/pops/validation/checker4_0.rb 346 def check_NamedAccessExpression(o) 347 name = o.right_expr 348 unless name.is_a? Model::QualifiedName 349 acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature=> 'function name', :container => container) 350 end 351 end
for 'class', 'define', and function
# File lib/puppet/pops/validation/checker4_0.rb 389 def check_NamedDefinition(o) 390 top(o) 391 if o.name !~ Patterns::CLASSREF_DECL 392 acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, {:name=>o.name}) 393 end 394 395 internal_check_file_namespace(o) 396 internal_check_reserved_type_name(o, o.name) 397 internal_check_future_reserved_word(o, o.name) 398 end
# File lib/puppet/pops/validation/checker4_0.rb 722 def check_NodeDefinition(o) 723 # Check that hostnames are valid hostnames (or regular expressions) 724 hostname(o.host_matches, o) 725 top(o) 726 violator = ends_with_idem(o.body) 727 if violator 728 acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, {:container => o}) unless resource_without_title?(violator) 729 end 730 unless o.parent.nil? 731 acceptor.accept(Issues::ILLEGAL_NODE_INHERITANCE, o.parent) 732 end 733 end
# File lib/puppet/pops/validation/checker4_0.rb 188 def check_Object(o) 189 end
# File lib/puppet/pops/validation/checker4_0.rb 764 def check_Parameter(o) 765 if o.name =~ /^(?:0x)?[0-9]+$/ 766 acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name) 767 end 768 769 unless o.name =~ Patterns::PARAM_NAME 770 acceptor.accept(Issues::ILLEGAL_PARAM_NAME, o, :name => o.name) 771 end 772 return unless o.value 773 774 internal_check_illegal_assignment(o.value) 775 end
No checking takes place - all expressions using a QualifiedName need to check. This because the rules are slightly different depending on the container (A variable allows a numeric start, but not other names). This means that (if the lexer/parser so chooses) a QualifiedName can be anything when it represents a Bare Word and evaluates to a String.
# File lib/puppet/pops/validation/checker4_0.rb 740 def check_QualifiedName(o) 741 end
Checks that the value is a valid UpperCaseWord (a CLASSREF), and optionally if it contains a hypen. DOH: QualifiedReferences are created with LOWER CASE NAMES at parse time
# File lib/puppet/pops/validation/checker4_0.rb 745 def check_QualifiedReference(o) 746 # Is this a valid qualified name? 747 if o.cased_value !~ Patterns::CLASSREF_EXT 748 acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.cased_value}) 749 end 750 end
# File lib/puppet/pops/validation/checker4_0.rb 752 def check_QueryExpression(o) 753 query(o.expr) if o.expr # is optional 754 end
relationship_side: resource
| resourceref | collection | variable | quotedtext | selector | casestatement | hasharrayaccesses
# File lib/puppet/pops/validation/checker4_0.rb 796 def check_RelationshipExpression(o) 797 relation(o.left_expr) 798 relation(o.right_expr) 799 end
# File lib/puppet/pops/validation/checker4_0.rb 837 def check_ReservedWord(o) 838 if o.future 839 acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, :word => o.word) 840 else 841 acceptor.accept(Issues::RESERVED_WORD, o, :word => o.word) 842 end 843 end
# File lib/puppet/pops/validation/checker4_0.rb 812 def check_ResourceBody(o) 813 seenUnfolding = false 814 o.operations.each do |ao| 815 if ao.is_a?(Model::AttributesOperation) 816 if seenUnfolding 817 acceptor.accept(Issues::MULTIPLE_ATTRIBUTES_UNFOLD, ao) 818 else 819 seenUnfolding = true 820 end 821 end 822 end 823 end
# File lib/puppet/pops/validation/checker4_0.rb 825 def check_ResourceDefaultsExpression(o) 826 if o.form != 'regular' 827 acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o) 828 end 829 end
# File lib/puppet/pops/validation/checker4_0.rb 801 def check_ResourceExpression(o) 802 # The expression for type name cannot be statically checked - this is instead done at runtime 803 # to enable better error message of the result of the expression rather than the static instruction. 804 # (This can be revised as there are static constructs that are illegal, but require updating many 805 # tests that expect the detailed reporting). 806 type_name_expr = o.type_name 807 if o.form && o.form != 'regular' && type_name_expr.is_a?(Model::QualifiedName) && type_name_expr.value == 'class' 808 acceptor.accept(Issues::CLASS_NOT_VIRTUALIZABLE, o) 809 end 810 end
# File lib/puppet/pops/validation/checker4_0.rb 831 def check_ResourceOverrideExpression(o) 832 if o.form != 'regular' 833 acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o) 834 end 835 end
# File lib/puppet/pops/validation/checker4_0.rb 474 def check_ResourceTypeDefinition(o) 475 check_NamedDefinition(o) 476 internal_check_no_capture(o) 477 internal_check_parameter_name_uniqueness(o) 478 internal_check_reserved_params(o) 479 internal_check_no_idem_last(o) 480 end
# File lib/puppet/pops/validation/checker4_0.rb 855 def check_SelectorEntry(o) 856 rvalue(o.matching_expr) 857 end
# File lib/puppet/pops/validation/checker4_0.rb 845 def check_SelectorExpression(o) 846 rvalue(o.left_expr) 847 # There can only be one LiteralDefault case option value 848 defaults = o.selectors.select {|v| v.matching_expr.is_a?(Model::LiteralDefault) } 849 unless defaults.size <= 1 850 # Flag the second default as 'unreachable' 851 acceptor.accept(Issues::DUPLICATE_DEFAULT, defaults[1].matching_expr, :container => o) 852 end 853 end
# File lib/puppet/pops/validation/checker4_0.rb 400 def check_TypeAlias(o) 401 top(o) 402 if o.name !~ Patterns::CLASSREF_EXT_DECL 403 acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, {:name=>o.name}) 404 end 405 internal_check_reserved_type_name(o, o.name) 406 internal_check_type_ref(o, o.type_expr) 407 end
# File lib/puppet/pops/validation/checker4_0.rb 453 def check_TypeDefinition(o) 454 top(o) 455 internal_check_reserved_type_name(o, o.name) 456 # TODO: Check TypeDefinition body. For now, just error out 457 acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o) 458 end
# File lib/puppet/pops/validation/checker4_0.rb 409 def check_TypeMapping(o) 410 top(o) 411 lhs = o.type_expr 412 lhs_type = 0 # Not Runtime 413 if lhs.is_a?(Model::AccessExpression) 414 left = lhs.left_expr 415 if left.is_a?(Model::QualifiedReference) && left.cased_value == 'Runtime' 416 lhs_type = 1 # Runtime 417 keys = lhs.keys 418 419 # Must be a literal string or pattern replacement 420 lhs_type = 2 if keys.size == 2 && pattern_with_replacement?(keys[1]) 421 end 422 end 423 424 if lhs_type == 0 425 # This is not a TypeMapping. Something other than Runtime[] on LHS 426 acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o) 427 else 428 rhs = o.mapping_expr 429 if pattern_with_replacement?(rhs) 430 acceptor.accept(Issues::ILLEGAL_SINGLE_TYPE_MAPPING, o) if lhs_type == 1 431 elsif type_ref?(rhs) 432 acceptor.accept(Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) if lhs_type == 2 433 else 434 acceptor.accept(lhs_type == 1 ? Issues::ILLEGAL_SINGLE_TYPE_MAPPING : Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) 435 end 436 end 437 end
# File lib/puppet/pops/validation/checker4_0.rb 859 def check_UnaryExpression(o) 860 rvalue(o.expr) 861 end
# File lib/puppet/pops/validation/checker4_0.rb 863 def check_UnlessExpression(o) 864 rvalue(o.test) 865 # TODO: Unless may not have an else part that is an IfExpression (grammar denies this though) 866 end
Checks that variable is either strictly 0, or a non 0 starting decimal number, or a valid VAR_NAME
# File lib/puppet/pops/validation/checker4_0.rb 869 def check_VariableExpression(o) 870 # The expression must be a qualified name or an integer 871 name_expr = o.expr 872 return if name_expr.is_a?(Model::LiteralInteger) 873 if !name_expr.is_a?(Model::QualifiedName) 874 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o) 875 else 876 # name must be either a decimal string value, or a valid NAME 877 name = o.expr.value 878 if name[0,1] =~ /[0-9]/ 879 unless name =~ Patterns::NUMERIC_VAR_NAME 880 acceptor.accept(Issues::ILLEGAL_NUMERIC_VAR_NAME, o, :name => name) 881 end 882 else 883 unless name =~ Patterns::VAR_NAME 884 acceptor.accept(Issues::ILLEGAL_VAR_NAME, o, :name => name) 885 end 886 end 887 end 888 end
# File lib/puppet/pops/validation/checker4_0.rb 61 def container(index = -1) 62 @path[index] 63 end
# File lib/puppet/pops/validation/checker4_0.rb 638 def dir_to_names(relative_path) 639 # Downcasing here because check is case-insensitive 640 path_components = relative_path.to_s.downcase.split(File::SEPARATOR) 641 642 # Example definition dir: manifests in this path: 643 # <module name>/manifests/<module subdir>/<classfile>.pp 644 dir = path_components[1] 645 646 # How can we get this result? 647 # If it is not an initial manifest, it must come from a module, 648 # and from the manifests dir there. This may never get used... 649 return BAD_MODULE_FILE unless dir == 'manifests' || dir == 'functions' || dir == 'types' || dir == 'plans' 650 651 names = path_components[2 .. -2] # Directories inside module 652 names.unshift(path_components[0]) # Name of the module itself 653 654 # Do not include name of module init file at top level of module 655 # e.g. <module name>/manifests/init.pp 656 filename = path_components[-1] 657 if !(path_components.length == 3 && filename == 'init.pp') 658 names.push(filename[0 .. -4]) # Remove .pp from filename 659 end 660 661 names 662 end
Returns the last expression in a block, or the expression, if that expression is idem
# File lib/puppet/pops/validation/checker4_0.rb 138 def ends_with_idem(o) 139 if o.is_a?(Model::BlockExpression) 140 last = o.statements[-1] 141 idem(last) ? last : nil 142 else 143 idem(o) ? o : nil 144 end 145 end
Performs check if this is a vaid hostname expression @param single_feature_name [String, nil] the name of a single valued hostname feature of the value's container. e.g. 'parent'
# File lib/puppet/pops/validation/checker4_0.rb 72 def hostname(o, semantic) 73 @@hostname_visitor.visit_this_1(self, o, semantic) 74 end
Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
# File lib/puppet/pops/validation/checker4_0.rb 893 def hostname_Array(o, semantic) 894 o.each {|x| hostname(x, semantic) } 895 end
# File lib/puppet/pops/validation/checker4_0.rb 911 def hostname_ConcatenatedString(o, semantic) 912 # Puppet 3.1. only accepts a concatenated string without interpolated expressions 913 the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) } 914 if the_expr 915 acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr) 916 elsif o.segments.size() != 1 917 # corner case, bad model, concatenation of several plain strings 918 acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o) 919 else 920 # corner case, may be ok, but lexer may have replaced with plain string, this is 921 # here if it does not 922 hostname_String(o.segments[0], o.segments[0]) 923 end 924 end
# File lib/puppet/pops/validation/checker4_0.rb 938 def hostname_LiteralDefault(o, semantic) 939 # always ok 940 end
# File lib/puppet/pops/validation/checker4_0.rb 934 def hostname_LiteralNumber(o, semantic) 935 # always ok 936 end
# File lib/puppet/pops/validation/checker4_0.rb 942 def hostname_LiteralRegularExpression(o, semantic) 943 # always ok 944 end
# File lib/puppet/pops/validation/checker4_0.rb 907 def hostname_LiteralValue(o, semantic) 908 hostname_String(o.value.to_s, o) 909 end
# File lib/puppet/pops/validation/checker4_0.rb 946 def hostname_Object(o, semantic) 947 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => 'hostname', :container => semantic}) 948 end
# File lib/puppet/pops/validation/checker4_0.rb 926 def hostname_QualifiedName(o, semantic) 927 hostname_String(o.value.to_s, o) 928 end
# File lib/puppet/pops/validation/checker4_0.rb 930 def hostname_QualifiedReference(o, semantic) 931 hostname_String(o.value.to_s, o) 932 end
# File lib/puppet/pops/validation/checker4_0.rb 897 def hostname_String(o, semantic) 898 # The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid, 899 # but this allows pathological names like "a..b......c", "----" 900 # TODO: Investigate if more illegal hostnames should be flagged. 901 # 902 if o =~ Patterns::ILLEGAL_HOSTNAME_CHARS 903 acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o) 904 end 905 end
Checks if the expression has side effect ('idem' is latin for 'the same', here meaning that the evaluation state is known to be unchanged after the expression has been evaluated). The result is not 100% authoritative for negative answers since analysis of function behavior is not possible. @return [Boolean] true if expression is known to have no effect on evaluation state
# File lib/puppet/pops/validation/checker4_0.rb 133 def idem(o) 134 @@idem_visitor.visit_this_0(self, o) 135 end
# File lib/puppet/pops/validation/checker4_0.rb 1035 def idem_AccessExpression(o) 1036 true 1037 end
An apply expression exists purely for the side effect of applying a catalog somewhere, so it always has side effects
# File lib/puppet/pops/validation/checker4_0.rb 1100 def idem_ApplyExpression(o) 1101 false 1102 end
# File lib/puppet/pops/validation/checker4_0.rb 1052 def idem_AssignmentExpression(o) 1053 # Always side effect 1054 false 1055 end
# File lib/puppet/pops/validation/checker4_0.rb 1039 def idem_BinaryExpression(o) 1040 true 1041 end
# File lib/puppet/pops/validation/checker4_0.rb 1075 def idem_BlockExpression(o) 1076 # productive if there is at least one productive expression 1077 ! o.statements.any? {|expr| !idem(expr) } 1078 end
Case expression is idem, if test, and all options are idem
# File lib/puppet/pops/validation/checker4_0.rb 1109 def idem_CaseExpression(o) 1110 return false if !idem(o.test) 1111 ! o.options.any? {|opt| !idem(opt) } 1112 end
An option is idem if values and the then_expression are idem
# File lib/puppet/pops/validation/checker4_0.rb 1115 def idem_CaseOption(o) 1116 return false if o.values.any? { |value| !idem(value) } 1117 idem(o.then_expr) 1118 end
Returns true even though there may be interpolated expressions that have side effect. Report as idem anyway, as it is very bad design to evaluate an interpolated string for its side effect only.
# File lib/puppet/pops/validation/checker4_0.rb 1083 def idem_ConcatenatedString(o) 1084 true 1085 end
# File lib/puppet/pops/validation/checker4_0.rb 1031 def idem_Factory(o) 1032 idem(o.model) 1033 end
Heredoc is just a string, but may contain interpolated string (which may have side effects). This is still bad design and should be reported as idem.
# File lib/puppet/pops/validation/checker4_0.rb 1089 def idem_HeredocExpression(o) 1090 true 1091 end
# File lib/puppet/pops/validation/checker4_0.rb 1104 def idem_IfExpression(o) 1105 [o.test, o.then_expr, o.else_expr].all? {|e| idem(e) } 1106 end
# File lib/puppet/pops/validation/checker4_0.rb 1019 def idem_Literal(o) 1020 true 1021 end
# File lib/puppet/pops/validation/checker4_0.rb 1027 def idem_LiteralHash(o) 1028 true 1029 end
# File lib/puppet/pops/validation/checker4_0.rb 1023 def idem_LiteralList(o) 1024 true 1025 end
# File lib/puppet/pops/validation/checker4_0.rb 1043 def idem_MatchExpression(o) 1044 false # can have side effect of setting $n match variables 1045 end
# File lib/puppet/pops/validation/checker4_0.rb 1015 def idem_NilClass(o) 1016 true 1017 end
# File lib/puppet/pops/validation/checker4_0.rb 1011 def idem_Nop(o) 1012 true 1013 end
# File lib/puppet/pops/validation/checker4_0.rb 1007 def idem_Object(o) 1008 false 1009 end
Allow (no-effect parentheses) to be used around a productive expression
# File lib/puppet/pops/validation/checker4_0.rb 1063 def idem_ParenthesizedExpression(o) 1064 idem(o.expr) 1065 end
# File lib/puppet/pops/validation/checker4_0.rb 1047 def idem_RelationshipExpression(o) 1048 # Always side effect 1049 false 1050 end
# File lib/puppet/pops/validation/checker4_0.rb 1067 def idem_RenderExpression(o) 1068 false 1069 end
# File lib/puppet/pops/validation/checker4_0.rb 1071 def idem_RenderStringExpression(o) 1072 false 1073 end
May technically have side effects inside the Selector, but this is bad design - treat as idem
# File lib/puppet/pops/validation/checker4_0.rb 1094 def idem_SelectorExpression(o) 1095 true 1096 end
Handles UnaryMinusExpression, NotExpression, VariableExpression
# File lib/puppet/pops/validation/checker4_0.rb 1058 def idem_UnaryExpression(o) 1059 true 1060 end
# File lib/puppet/pops/validation/checker4_0.rb 610 def initial_manifest?(path, manifest_setting) 611 return false if manifest_setting.nil? || manifest_setting == :no_manifest 612 613 string_path = path.to_s 614 615 string_path == manifest_setting || string_path.start_with?(manifest_setting) 616 end
# File lib/puppet/pops/validation/checker4_0.rb 503 def internal_check_capture_last(o) 504 accepted_index = o.parameters.size() -1 505 o.parameters.each_with_index do |p, index| 506 if p.captures_rest && index != accepted_index 507 acceptor.accept(Issues::CAPTURES_REST_NOT_LAST, p, {:param_name => p.name}) 508 end 509 end 510 end
# File lib/puppet/pops/validation/checker4_0.rb 536 def internal_check_file_namespace(o) 537 file = o.locator.file 538 return if file.nil? || file == '' #e.g. puppet apply -e '...' 539 540 file_namespace = namespace_for_file(file) 541 return if file_namespace == NO_NAMESPACE 542 543 # Downcasing here because check is case-insensitive 544 if file_namespace == BAD_MODULE_FILE || !o.name.downcase.start_with?(file_namespace) 545 acceptor.accept(Issues::ILLEGAL_DEFINITION_LOCATION, o, {:name => o.name, :file => file}) 546 end 547 end
# File lib/puppet/pops/validation/checker4_0.rb 526 def internal_check_future_reserved_word(o, name) 527 if FUTURE_RESERVED_WORDS[name] 528 acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, {:word => name}) 529 end 530 end
# File lib/puppet/pops/validation/checker4_0.rb 777 def internal_check_illegal_assignment(o) 778 if o.is_a?(Model::AssignmentExpression) 779 acceptor.accept(Issues::ILLEGAL_ASSIGNMENT_CONTEXT, o) 780 else 781 # recursively check all contents unless it's a lambda expression. A lambda may contain 782 # local assignments 783 o._pcore_contents {|model| internal_check_illegal_assignment(model) } unless o.is_a?(Model::LambdaExpression) 784 end 785 end
# File lib/puppet/pops/validation/checker4_0.rb 512 def internal_check_no_capture(o, container = o) 513 o.parameters.each do |p| 514 if p.captures_rest 515 acceptor.accept(Issues::CAPTURES_REST_NOT_SUPPORTED, p, {:container => container, :param_name => p.name}) 516 end 517 end 518 end
# File lib/puppet/pops/validation/checker4_0.rb 496 def internal_check_no_idem_last(o) 497 violator = ends_with_idem(o.body) 498 if violator 499 acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, {:container => o}) unless resource_without_title?(violator) 500 end 501 end
# File lib/puppet/pops/validation/checker4_0.rb 677 def internal_check_parameter_name_uniqueness(o) 678 unique = Set.new 679 o.parameters.each do |p| 680 acceptor.accept(Issues::DUPLICATE_PARAMETER, p, {:param_name => p.name}) unless unique.add?(p.name) 681 end 682 end
# File lib/puppet/pops/validation/checker4_0.rb 669 def internal_check_reserved_params(o) 670 o.parameters.each do |p| 671 if RESERVED_PARAMETERS[p.name] 672 acceptor.accept(Issues::RESERVED_PARAMETER, p, {:container => o, :param_name => p.name}) 673 end 674 end 675 end
# File lib/puppet/pops/validation/checker4_0.rb 520 def internal_check_reserved_type_name(o, name) 521 if RESERVED_TYPE_NAMES[name] 522 acceptor.accept(Issues::RESERVED_TYPE_NAME, o, {:name => name}) 523 end 524 end
# File lib/puppet/pops/validation/checker4_0.rb 482 def internal_check_return_type(o) 483 r = o.return_type 484 internal_check_type_ref(o, r) unless r.nil? 485 end
# File lib/puppet/pops/validation/checker4_0.rb 549 def internal_check_top_construct_in_module(prog) 550 return unless prog.is_a?(Model::Program) && !prog.body.nil? 551 552 #Check that this is a module autoloaded file 553 file = prog.locator.file 554 return if file.nil? 555 return if namespace_for_file(file) == NO_NAMESPACE 556 557 body = prog.body 558 return if prog.body.is_a?(Model::Nop) #Ignore empty or comment-only files 559 560 if(body.is_a?(Model::BlockExpression)) 561 body.statements.each { |s| acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, s) unless valid_top_construct?(s) } 562 else 563 acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, body) unless valid_top_construct?(body) 564 end 565 end
# File lib/puppet/pops/validation/checker4_0.rb 487 def internal_check_type_ref(o, r) 488 n = r.is_a?(Model::AccessExpression) ? r.left_expr : r 489 if n.is_a? Model::QualifiedReference 490 internal_check_future_reserved_word(r, n.value) 491 else 492 acceptor.accept(Issues::ILLEGAL_EXPRESSION, r, :feature => 'a type reference', :container => o) 493 end 494 end
# File lib/puppet/pops/validation/checker4_0.rb 582 def namespace_for_file(file) 583 env = Puppet.lookup(:current_environment) 584 return NO_NAMESPACE if env.nil? 585 586 adapter = Puppet::Util::FileNamespaceAdapter.adapt(env) 587 588 file_namespace = adapter.file_to_namespace[file] 589 return file_namespace unless file_namespace.nil? # No cache entry, so we do the calculation 590 591 path = Pathname.new(file) 592 593 return adapter.file_to_namespace[file] = NO_NAMESPACE if path.extname != ".pp" 594 595 path = path.expand_path 596 597 return adapter.file_to_namespace[file] = NO_NAMESPACE if initial_manifest?(path, env.manifest) 598 599 #All auto-loaded files from modules come from a module search path dir 600 relative_path = get_module_relative_path(path, env.full_modulepath) 601 602 return adapter.file_to_namespace[file] = NO_NAMESPACE if relative_path == NO_PATH 603 604 #If a file comes from a module, but isn't in the right place, always error 605 names = dir_to_names(relative_path) 606 607 return adapter.file_to_namespace[file] = (names == BAD_MODULE_FILE ? BAD_MODULE_FILE : names.join("::").freeze) 608 end
# File lib/puppet/pops/validation/checker4_0.rb 439 def pattern_with_replacement?(o) 440 if o.is_a?(Model::LiteralList) 441 v = o.values 442 v.size == 2 && v[0].is_a?(Model::LiteralRegularExpression) && v[1].is_a?(Model::LiteralString) 443 else 444 false 445 end 446 end
Performs check if this is valid as a query
# File lib/puppet/pops/validation/checker4_0.rb 77 def query(o) 78 @@query_visitor.visit_this_0(self, o) 79 end
Allows AND, OR, and checks if left/right are allowed in query.
# File lib/puppet/pops/validation/checker4_0.rb 964 def query_BooleanExpression(o) 965 query o.left_expr 966 query o.right_expr 967 end
# File lib/puppet/pops/validation/checker4_0.rb 981 def query_LiteralBoolean(o); end
# File lib/puppet/pops/validation/checker4_0.rb 977 def query_LiteralNumber(o); end
# File lib/puppet/pops/validation/checker4_0.rb 979 def query_LiteralString(o); end
Anything not explicitly allowed is flagged as error.
# File lib/puppet/pops/validation/checker4_0.rb 953 def query_Object(o) 954 acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) 955 end
# File lib/puppet/pops/validation/checker4_0.rb 969 def query_ParenthesizedExpression(o) 970 query(o.expr) 971 end
# File lib/puppet/pops/validation/checker4_0.rb 975 def query_QualifiedName(o); end
# File lib/puppet/pops/validation/checker4_0.rb 973 def query_VariableExpression(o); end
Performs check if this is valid as a relationship side
# File lib/puppet/pops/validation/checker4_0.rb 82 def relation(o) 83 @@relation_visitor.visit_this_0(self, o) 84 end
# File lib/puppet/pops/validation/checker4_0.rb 760 def relation_CollectExpression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 756 def relation_Object(o) 757 rvalue(o) 758 end
# File lib/puppet/pops/validation/checker4_0.rb 762 def relation_RelationshipExpression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 256 def resource_without_title?(o) 257 if o.instance_of?(Model::BlockExpression) 258 statements = o.statements 259 statements.length == 2 && statements[0].instance_of?(Model::QualifiedName) && statements[1].instance_of?(Model::LiteralHash) 260 else 261 false 262 end 263 end
Performs check if this is valid as a rvalue
# File lib/puppet/pops/validation/checker4_0.rb 87 def rvalue(o) 88 @@rvalue_visitor.visit_this_0(self, o) 89 end
# File lib/puppet/pops/validation/checker4_0.rb 990 def rvalue_CollectExpression(o) 991 acceptor.accept(Issues::NOT_RVALUE, o) 992 end
# File lib/puppet/pops/validation/checker4_0.rb 994 def rvalue_Definition(o) 995 acceptor.accept(Issues::NOT_RVALUE, o) 996 end
By default, all expressions are reported as being rvalues Implement specific rvalue checks for those that are not.
# File lib/puppet/pops/validation/checker4_0.rb 988 def rvalue_Expression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 998 def rvalue_NodeDefinition(o) 999 acceptor.accept(Issues::NOT_RVALUE, o) 1000 end
# File lib/puppet/pops/validation/checker4_0.rb 1002 def rvalue_UnaryExpression(o) 1003 rvalue o.expr 1004 end
# File lib/puppet/pops/validation/checker4_0.rb 93 def top(definition, idx = -1) 94 o = container(idx) 95 idx -= 1 96 case o 97 when NilClass, Model::ApplyExpression, Model::HostClassDefinition, Model::Program 98 # ok, stop scanning parents 99 when Model::BlockExpression 100 c = container(idx) 101 if !c.is_a?(Model::Program) && 102 (definition.is_a?(Model::FunctionDefinition) || definition.is_a?(Model::TypeAlias) || definition.is_a?(Model::TypeDefinition)) 103 104 # not ok. These can never be nested in a block 105 acceptor.accept(Issues::NOT_ABSOLUTE_TOP_LEVEL, definition) 106 else 107 # ok, if this is a block representing the body of a class, or is top level 108 top(definition, idx) 109 end 110 when Model::LambdaExpression 111 # A LambdaExpression is a BlockExpression, and this check is needed to prevent the polymorph method for BlockExpression 112 # to accept a lambda. 113 # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure. 114 acceptor.accept(Issues::NOT_TOP_LEVEL, definition) 115 else 116 # fail, reached a container that is not top level 117 acceptor.accept(Issues::NOT_TOP_LEVEL, definition) 118 end 119 end
# File lib/puppet/pops/validation/checker4_0.rb 448 def type_ref?(o) 449 o = o.left_expr if o.is_a?(Model::AccessExpression) 450 o.is_a?(Model::QualifiedReference) 451 end
# File lib/puppet/pops/validation/checker4_0.rb 567 def valid_top_construct?(o) 568 o.is_a?(Model::Definition) && !o.is_a?(Model::NodeDefinition) 569 end
Validates the entire model by visiting each model element and calling `check`. The result is collected (or acted on immediately) by the configured diagnostic provider/acceptor given when creating this Checker.
# File lib/puppet/pops/validation/checker4_0.rb 53 def validate(model) 54 # tree iterate the model, and call check for each element 55 @path = [] 56 check(model) 57 internal_check_top_construct_in_module(model) 58 model._pcore_all_contents(@path) { |element| check(element) } 59 end
Produces string part of something named, or nil if not a QualifiedName or QualifiedReference
# File lib/puppet/pops/validation/checker4_0.rb 1124 def varname_to_s(o) 1125 case o 1126 when Model::QualifiedName 1127 o.value 1128 when Model::QualifiedReference 1129 o.value 1130 else 1131 nil 1132 end 1133 end
Private Instance Methods
Get the path of file_path relative to the first directory in modulepath_directories that is an ancestor of file_path. Return NO_PATH if none is found.
# File lib/puppet/pops/validation/checker4_0.rb 621 def get_module_relative_path(file_path, modulepath_directories) 622 clean_file = file_path.cleanpath.to_s 623 parent_path = modulepath_directories.find { |path_dir| is_parent_dir_of(path_dir, clean_file) } 624 return NO_PATH if parent_path.nil? 625 626 file_path.relative_path_from(Pathname.new(parent_path)) 627 end
# File lib/puppet/pops/validation/checker4_0.rb 630 def is_parent_dir_of(parent_dir, child_dir) 631 parent_dir_path = Pathname.new(parent_dir) 632 clean_parent = parent_dir_path.cleanpath.to_s + File::SEPARATOR 633 634 return child_dir.start_with?(clean_parent) 635 end