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 23 def self.check_visitor 24 # Class instance variable rather than Class variable because methods visited 25 # may be overridden in subclass 26 @check_visitor ||= Visitor.new(nil, 'check', 0, 0) 27 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 32 def initialize(diagnostics_producer) 33 super() 34 @@rvalue_visitor ||= Visitor.new(nil, "rvalue", 0, 0) 35 @@hostname_visitor ||= Visitor.new(nil, "hostname", 1, 2) 36 @@assignment_visitor ||= Visitor.new(nil, "assign", 0, 1) 37 @@query_visitor ||= Visitor.new(nil, "query", 0, 0) 38 @@relation_visitor ||= Visitor.new(nil, "relation", 0, 0) 39 @@idem_visitor ||= Visitor.new(nil, "idem", 0, 0) 40 41 @check_visitor = self.class.check_visitor 42 @acceptor = diagnostics_producer 43 44 # Use null migration checker unless given in context 45 @migration_checker = (Puppet.lookup(:migration_checker) { Migration::MigrationChecker.new() }) 46 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 123 def assign(o, via_index = false) 124 @@assignment_visitor.visit_this_1(self, o, via_index) 125 end
# File lib/puppet/pops/validation/checker4_0.rb 164 def assign_AccessExpression(o, via_index) 165 # Are indexed assignments allowed at all ? $x[x] = '...' 166 if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT 167 acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o) 168 else 169 # Then the left expression must be assignable-via-index 170 assign(o.left_expr, true) 171 end 172 end
# File lib/puppet/pops/validation/checker4_0.rb 174 def assign_LiteralList(o, via_index) 175 o.values.each {|x| assign(x) } 176 end
# File lib/puppet/pops/validation/checker4_0.rb 178 def assign_Object(o, via_index) 179 # Can not assign to anything else (differentiate if this is via index or not) 180 # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases) 181 # 182 acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o) 183 end
# File lib/puppet/pops/validation/checker4_0.rb 148 def assign_VariableExpression(o, via_index) 149 varname_string = varname_to_s(o.expr) 150 if varname_string =~ Patterns::NUMERIC_VAR_NAME 151 acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string) 152 end 153 # Can not assign to something in another namespace (i.e. a '::' in the name is not legal) 154 if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT 155 if varname_string =~ /::/ 156 acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string) 157 end 158 end 159 160 # TODO: Could scan for reassignment of the same variable if done earlier in the same container 161 # Or if assigning to a parameter (more work). 162 end
Performs regular validity check
# File lib/puppet/pops/validation/checker4_0.rb 65 def check(o) 66 @check_visitor.visit_this_0(self, o) 67 end
# File lib/puppet/pops/validation/checker4_0.rb 194 def check_AccessExpression(o) 195 # Only min range is checked, all other checks are RT checks as they depend on the resulting type 196 # of the LHS. 197 if o.keys.size < 1 198 acceptor.accept(Issues::MISSING_INDEX, o) 199 end 200 end
# File lib/puppet/pops/validation/checker4_0.rb 202 def check_AssignmentExpression(o) 203 case o.operator 204 when '=' 205 assign(o.left_expr) 206 rvalue(o.right_expr) 207 when '+=', '-=' 208 acceptor.accept(Issues::APPENDS_DELETES_NO_LONGER_SUPPORTED, o, {:operator => o.operator}) 209 else 210 acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) 211 end 212 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 222 def check_AttributeOperation(o) 223 if o.operator == '+>' 224 # Append operator use is constrained 225 p = container 226 unless p.is_a?(Model::CollectExpression) || p.is_a?(Model::ResourceOverrideExpression) 227 acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, {:name=>o.attribute_name, :parent=>p}) 228 end 229 end 230 rvalue(o.value_expr) 231 end
# File lib/puppet/pops/validation/checker4_0.rb 233 def check_AttributesOperation(o) 234 # Append operator use is constrained 235 p = container 236 case p 237 when Model::AbstractResource 238 when Model::CollectExpression 239 else 240 # protect against just testing a snippet that has no parent, error message will be a bit strange 241 # but it is not for a real program. 242 parent2 = p.nil? ? o : container(-2) 243 unless parent2.is_a?(Model::AbstractResource) 244 acceptor.accept(Issues::UNSUPPORTED_OPERATOR_IN_CONTEXT, parent2, :operator=>'* =>') 245 end 246 end 247 rvalue(o.expr) 248 end
# File lib/puppet/pops/validation/checker4_0.rb 250 def check_BinaryExpression(o) 251 rvalue(o.left_expr) 252 rvalue(o.right_expr) 253 end
# File lib/puppet/pops/validation/checker4_0.rb 264 def check_BlockExpression(o) 265 if resource_without_title?(o) 266 acceptor.accept(Issues::RESOURCE_WITHOUT_TITLE, o, :name => o.statements[0].value) 267 else 268 o.statements[0..-2].each do |statement| 269 if idem(statement) 270 acceptor.accept(Issues::IDEM_EXPRESSION_NOT_LAST, statement) 271 break # only flag the first 272 end 273 end 274 end 275 end
# File lib/puppet/pops/validation/checker4_0.rb 277 def check_CallNamedFunctionExpression(o) 278 functor = o.functor_expr 279 if functor.is_a?(Model::QualifiedReference) || 280 functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference) 281 # ok (a call to a type) 282 return nil 283 end 284 case functor 285 when Model::QualifiedName 286 # ok 287 nil 288 when Model::RenderStringExpression 289 # helpful to point out this easy to make Epp error 290 acceptor.accept(Issues::ILLEGAL_EPP_PARAMETERS, o) 291 else 292 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) 293 end 294 end
# File lib/puppet/pops/validation/checker4_0.rb 318 def check_CaseExpression(o) 319 rvalue(o.test) 320 # There can only be one LiteralDefault case option value 321 found_default = false 322 o.options.each do |option| 323 option.values.each do |value| 324 if value.is_a?(Model::LiteralDefault) 325 # Flag the second default as 'unreachable' 326 acceptor.accept(Issues::DUPLICATE_DEFAULT, value, :container => o) if found_default 327 found_default = true 328 end 329 end 330 end 331 end
# File lib/puppet/pops/validation/checker4_0.rb 333 def check_CaseOption(o) 334 o.values.each { |v| rvalue(v) } 335 end
# File lib/puppet/pops/validation/checker4_0.rb 337 def check_CollectExpression(o) 338 unless o.type_expr.is_a? Model::QualifiedReference 339 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o) 340 end 341 end
# File lib/puppet/pops/validation/checker4_0.rb 296 def check_EppExpression(o) 297 p = container 298 if p.is_a?(Model::LambdaExpression) 299 internal_check_no_capture(p, o) 300 internal_check_parameter_name_uniqueness(p) 301 end 302 end
# File lib/puppet/pops/validation/checker4_0.rb 190 def check_Factory(o) 191 check(o.model) 192 end
# File lib/puppet/pops/validation/checker4_0.rb 459 def check_FunctionDefinition(o) 460 check_NamedDefinition(o) 461 internal_check_return_type(o) 462 internal_check_parameter_name_uniqueness(o) 463 end
# File lib/puppet/pops/validation/checker4_0.rb 304 def check_HeredocExpression(o) 305 # Only syntax check static text in heredoc during validation - dynamic text is validated by the evaluator. 306 expr = o.text_expr 307 if expr.is_a?(Model::LiteralString) 308 assert_external_syntax(nil, expr.value, o.syntax, o.text_expr) 309 end 310 end
# File lib/puppet/pops/validation/checker4_0.rb 465 def check_HostClassDefinition(o) 466 check_NamedDefinition(o) 467 internal_check_no_capture(o) 468 internal_check_parameter_name_uniqueness(o) 469 internal_check_reserved_params(o) 470 internal_check_no_idem_last(o) 471 end
# File lib/puppet/pops/validation/checker4_0.rb 678 def check_IfExpression(o) 679 rvalue(o.test) 680 end
# File lib/puppet/pops/validation/checker4_0.rb 682 def check_KeyedEntry(o) 683 rvalue(o.key) 684 rvalue(o.value) 685 # In case there are additional things to forbid than non-rvalues 686 # acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => container) 687 end
# File lib/puppet/pops/validation/checker4_0.rb 689 def check_LambdaExpression(o) 690 internal_check_capture_last(o) 691 internal_check_return_type(o) 692 end
# File lib/puppet/pops/validation/checker4_0.rb 705 def check_LiteralHash(o) 706 # the keys of a literal hash may be non-literal expressions. They cannot be checked. 707 unique = Set.new 708 o.entries.each do |entry| 709 catch(:not_literal) do 710 literal_key = literal(entry.key) 711 acceptor.accept(Issues::DUPLICATE_KEY, entry, {:key => literal_key}) if unique.add?(literal_key).nil? 712 end 713 end 714 end
# File lib/puppet/pops/validation/checker4_0.rb 698 def check_LiteralInteger(o) 699 v = o.value 700 if v < MIN_INTEGER || v > MAX_INTEGER 701 acceptor.accept(Issues::NUMERIC_OVERFLOW, o, {:value => v}) 702 end 703 end
# File lib/puppet/pops/validation/checker4_0.rb 694 def check_LiteralList(o) 695 o.values.each {|v| rvalue(v) } 696 end
# File lib/puppet/pops/validation/checker4_0.rb 312 def check_MethodCallExpression(o) 313 unless o.functor_expr.is_a? Model::QualifiedName 314 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o) 315 end 316 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 345 def check_NamedAccessExpression(o) 346 name = o.right_expr 347 unless name.is_a? Model::QualifiedName 348 acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature=> 'function name', :container => container) 349 end 350 end
for 'class', 'define', and function
# File lib/puppet/pops/validation/checker4_0.rb 388 def check_NamedDefinition(o) 389 top(o) 390 if o.name !~ Patterns::CLASSREF_DECL 391 acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, {:name=>o.name}) 392 end 393 394 internal_check_file_namespace(o) 395 internal_check_reserved_type_name(o, o.name) 396 internal_check_future_reserved_word(o, o.name) 397 end
# File lib/puppet/pops/validation/checker4_0.rb 716 def check_NodeDefinition(o) 717 # Check that hostnames are valid hostnames (or regular expressions) 718 hostname(o.host_matches, o) 719 top(o) 720 violator = ends_with_idem(o.body) 721 if violator 722 acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, {:container => o}) unless resource_without_title?(violator) 723 end 724 unless o.parent.nil? 725 acceptor.accept(Issues::ILLEGAL_NODE_INHERITANCE, o.parent) 726 end 727 end
# File lib/puppet/pops/validation/checker4_0.rb 187 def check_Object(o) 188 end
# File lib/puppet/pops/validation/checker4_0.rb 758 def check_Parameter(o) 759 if o.name =~ /^(?:0x)?[0-9]+$/ 760 acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name) 761 end 762 763 unless o.name =~ Patterns::PARAM_NAME 764 acceptor.accept(Issues::ILLEGAL_PARAM_NAME, o, :name => o.name) 765 end 766 return unless o.value 767 768 internal_check_illegal_assignment(o.value) 769 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 734 def check_QualifiedName(o) 735 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 739 def check_QualifiedReference(o) 740 # Is this a valid qualified name? 741 if o.cased_value !~ Patterns::CLASSREF_EXT 742 acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.cased_value}) 743 end 744 end
# File lib/puppet/pops/validation/checker4_0.rb 746 def check_QueryExpression(o) 747 query(o.expr) if o.expr # is optional 748 end
relationship_side: resource
| resourceref | collection | variable | quotedtext | selector | casestatement | hasharrayaccesses
# File lib/puppet/pops/validation/checker4_0.rb 790 def check_RelationshipExpression(o) 791 relation(o.left_expr) 792 relation(o.right_expr) 793 end
# File lib/puppet/pops/validation/checker4_0.rb 831 def check_ReservedWord(o) 832 if o.future 833 acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, :word => o.word) 834 else 835 acceptor.accept(Issues::RESERVED_WORD, o, :word => o.word) 836 end 837 end
# File lib/puppet/pops/validation/checker4_0.rb 806 def check_ResourceBody(o) 807 seenUnfolding = false 808 o.operations.each do |ao| 809 if ao.is_a?(Model::AttributesOperation) 810 if seenUnfolding 811 acceptor.accept(Issues::MULTIPLE_ATTRIBUTES_UNFOLD, ao) 812 else 813 seenUnfolding = true 814 end 815 end 816 end 817 end
# File lib/puppet/pops/validation/checker4_0.rb 819 def check_ResourceDefaultsExpression(o) 820 if o.form != 'regular' 821 acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o) 822 end 823 end
# File lib/puppet/pops/validation/checker4_0.rb 795 def check_ResourceExpression(o) 796 # The expression for type name cannot be statically checked - this is instead done at runtime 797 # to enable better error message of the result of the expression rather than the static instruction. 798 # (This can be revised as there are static constructs that are illegal, but require updating many 799 # tests that expect the detailed reporting). 800 type_name_expr = o.type_name 801 if o.form && o.form != 'regular' && type_name_expr.is_a?(Model::QualifiedName) && type_name_expr.value == 'class' 802 acceptor.accept(Issues::CLASS_NOT_VIRTUALIZABLE, o) 803 end 804 end
# File lib/puppet/pops/validation/checker4_0.rb 825 def check_ResourceOverrideExpression(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 473 def check_ResourceTypeDefinition(o) 474 check_NamedDefinition(o) 475 internal_check_no_capture(o) 476 internal_check_parameter_name_uniqueness(o) 477 internal_check_reserved_params(o) 478 internal_check_no_idem_last(o) 479 end
# File lib/puppet/pops/validation/checker4_0.rb 849 def check_SelectorEntry(o) 850 rvalue(o.matching_expr) 851 end
# File lib/puppet/pops/validation/checker4_0.rb 839 def check_SelectorExpression(o) 840 rvalue(o.left_expr) 841 # There can only be one LiteralDefault case option value 842 defaults = o.selectors.select {|v| v.matching_expr.is_a?(Model::LiteralDefault) } 843 unless defaults.size <= 1 844 # Flag the second default as 'unreachable' 845 acceptor.accept(Issues::DUPLICATE_DEFAULT, defaults[1].matching_expr, :container => o) 846 end 847 end
# File lib/puppet/pops/validation/checker4_0.rb 399 def check_TypeAlias(o) 400 top(o) 401 if o.name !~ Patterns::CLASSREF_EXT_DECL 402 acceptor.accept(Issues::ILLEGAL_DEFINITION_NAME, o, {:name=>o.name}) 403 end 404 internal_check_reserved_type_name(o, o.name) 405 internal_check_type_ref(o, o.type_expr) 406 end
# File lib/puppet/pops/validation/checker4_0.rb 452 def check_TypeDefinition(o) 453 top(o) 454 internal_check_reserved_type_name(o, o.name) 455 # TODO: Check TypeDefinition body. For now, just error out 456 acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o) 457 end
# File lib/puppet/pops/validation/checker4_0.rb 408 def check_TypeMapping(o) 409 top(o) 410 lhs = o.type_expr 411 lhs_type = 0 # Not Runtime 412 if lhs.is_a?(Model::AccessExpression) 413 left = lhs.left_expr 414 if left.is_a?(Model::QualifiedReference) && left.cased_value == 'Runtime' 415 lhs_type = 1 # Runtime 416 keys = lhs.keys 417 418 # Must be a literal string or pattern replacement 419 lhs_type = 2 if keys.size == 2 && pattern_with_replacement?(keys[1]) 420 end 421 end 422 423 if lhs_type == 0 424 # This is not a TypeMapping. Something other than Runtime[] on LHS 425 acceptor.accept(Issues::UNSUPPORTED_EXPRESSION, o) 426 else 427 rhs = o.mapping_expr 428 if pattern_with_replacement?(rhs) 429 acceptor.accept(Issues::ILLEGAL_SINGLE_TYPE_MAPPING, o) if lhs_type == 1 430 elsif type_ref?(rhs) 431 acceptor.accept(Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) if lhs_type == 2 432 else 433 acceptor.accept(lhs_type == 1 ? Issues::ILLEGAL_SINGLE_TYPE_MAPPING : Issues::ILLEGAL_REGEXP_TYPE_MAPPING, o) 434 end 435 end 436 end
# File lib/puppet/pops/validation/checker4_0.rb 853 def check_UnaryExpression(o) 854 rvalue(o.expr) 855 end
# File lib/puppet/pops/validation/checker4_0.rb 857 def check_UnlessExpression(o) 858 rvalue(o.test) 859 # TODO: Unless may not have an else part that is an IfExpression (grammar denies this though) 860 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 863 def check_VariableExpression(o) 864 # The expression must be a qualified name or an integer 865 name_expr = o.expr 866 return if name_expr.is_a?(Model::LiteralInteger) 867 if !name_expr.is_a?(Model::QualifiedName) 868 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o) 869 else 870 # name must be either a decimal string value, or a valid NAME 871 name = o.expr.value 872 if name[0,1] =~ /[0-9]/ 873 unless name =~ Patterns::NUMERIC_VAR_NAME 874 acceptor.accept(Issues::ILLEGAL_NUMERIC_VAR_NAME, o, :name => name) 875 end 876 else 877 unless name =~ Patterns::VAR_NAME 878 acceptor.accept(Issues::ILLEGAL_VAR_NAME, o, :name => name) 879 end 880 end 881 end 882 end
# File lib/puppet/pops/validation/checker4_0.rb 60 def container(index = -1) 61 @path[index] 62 end
# File lib/puppet/pops/validation/checker4_0.rb 632 def dir_to_names(relative_path) 633 # Downcasing here because check is case-insensitive 634 path_components = relative_path.to_s.downcase.split(File::SEPARATOR) 635 636 # Example definition dir: manifests in this path: 637 # <module name>/manifests/<module subdir>/<classfile>.pp 638 dir = path_components[1] 639 640 # How can we get this result? 641 # If it is not an initial manifest, it must come from a module, 642 # and from the manifests dir there. This may never get used... 643 return BAD_MODULE_FILE unless dir == 'manifests' || dir == 'functions' || dir == 'types' || dir == 'plans' 644 645 names = path_components[2 .. -2] # Directories inside module 646 names.unshift(path_components[0]) # Name of the module itself 647 648 # Do not include name of module init file at top level of module 649 # e.g. <module name>/manifests/init.pp 650 filename = path_components[-1] 651 if !(path_components.length == 3 && filename == 'init.pp') 652 names.push(filename[0 .. -4]) # Remove .pp from filename 653 end 654 655 names 656 end
Returns the last expression in a block, or the expression, if that expression is idem
# File lib/puppet/pops/validation/checker4_0.rb 137 def ends_with_idem(o) 138 if o.is_a?(Model::BlockExpression) 139 last = o.statements[-1] 140 idem(last) ? last : nil 141 else 142 idem(o) ? o : nil 143 end 144 end
# File lib/puppet/pops/validation/checker4_0.rb 617 def get_module_relative_path(file_path, modulepath_directories) 618 clean_file = file_path.cleanpath 619 parent_path = modulepath_directories.find { |path_dir| is_parent_dir_of(path_dir, clean_file) } 620 return NO_PATH if parent_path.nil? 621 622 file_path.relative_path_from(Pathname.new(parent_path)) 623 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 71 def hostname(o, semantic) 72 @@hostname_visitor.visit_this_1(self, o, semantic) 73 end
Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
# File lib/puppet/pops/validation/checker4_0.rb 887 def hostname_Array(o, semantic) 888 o.each {|x| hostname(x, semantic) } 889 end
# File lib/puppet/pops/validation/checker4_0.rb 905 def hostname_ConcatenatedString(o, semantic) 906 # Puppet 3.1. only accepts a concatenated string without interpolated expressions 907 the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) } 908 if the_expr 909 acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr) 910 elsif o.segments.size() != 1 911 # corner case, bad model, concatenation of several plain strings 912 acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o) 913 else 914 # corner case, may be ok, but lexer may have replaced with plain string, this is 915 # here if it does not 916 hostname_String(o.segments[0], o.segments[0]) 917 end 918 end
# File lib/puppet/pops/validation/checker4_0.rb 932 def hostname_LiteralDefault(o, semantic) 933 # always ok 934 end
# File lib/puppet/pops/validation/checker4_0.rb 928 def hostname_LiteralNumber(o, semantic) 929 # always ok 930 end
# File lib/puppet/pops/validation/checker4_0.rb 936 def hostname_LiteralRegularExpression(o, semantic) 937 # always ok 938 end
# File lib/puppet/pops/validation/checker4_0.rb 901 def hostname_LiteralValue(o, semantic) 902 hostname_String(o.value.to_s, o) 903 end
# File lib/puppet/pops/validation/checker4_0.rb 940 def hostname_Object(o, semantic) 941 acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => 'hostname', :container => semantic}) 942 end
# File lib/puppet/pops/validation/checker4_0.rb 920 def hostname_QualifiedName(o, semantic) 921 hostname_String(o.value.to_s, o) 922 end
# File lib/puppet/pops/validation/checker4_0.rb 924 def hostname_QualifiedReference(o, semantic) 925 hostname_String(o.value.to_s, o) 926 end
# File lib/puppet/pops/validation/checker4_0.rb 891 def hostname_String(o, semantic) 892 # The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid, 893 # but this allows pathological names like "a..b......c", "----" 894 # TODO: Investigate if more illegal hostnames should be flagged. 895 # 896 if o =~ Patterns::ILLEGAL_HOSTNAME_CHARS 897 acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o) 898 end 899 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 132 def idem(o) 133 @@idem_visitor.visit_this_0(self, o) 134 end
# File lib/puppet/pops/validation/checker4_0.rb 1029 def idem_AccessExpression(o) 1030 true 1031 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 1094 def idem_ApplyExpression(o) 1095 false 1096 end
# File lib/puppet/pops/validation/checker4_0.rb 1046 def idem_AssignmentExpression(o) 1047 # Always side effect 1048 false 1049 end
# File lib/puppet/pops/validation/checker4_0.rb 1033 def idem_BinaryExpression(o) 1034 true 1035 end
# File lib/puppet/pops/validation/checker4_0.rb 1069 def idem_BlockExpression(o) 1070 # productive if there is at least one productive expression 1071 ! o.statements.any? {|expr| !idem(expr) } 1072 end
Case expression is idem, if test, and all options are idem
# File lib/puppet/pops/validation/checker4_0.rb 1103 def idem_CaseExpression(o) 1104 return false if !idem(o.test) 1105 ! o.options.any? {|opt| !idem(opt) } 1106 end
An option is idem if values and the then_expression are idem
# File lib/puppet/pops/validation/checker4_0.rb 1109 def idem_CaseOption(o) 1110 return false if o.values.any? { |value| !idem(value) } 1111 idem(o.then_expr) 1112 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 1077 def idem_ConcatenatedString(o) 1078 true 1079 end
# File lib/puppet/pops/validation/checker4_0.rb 1025 def idem_Factory(o) 1026 idem(o.model) 1027 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 1083 def idem_HeredocExpression(o) 1084 true 1085 end
# File lib/puppet/pops/validation/checker4_0.rb 1098 def idem_IfExpression(o) 1099 [o.test, o.then_expr, o.else_expr].all? {|e| idem(e) } 1100 end
# File lib/puppet/pops/validation/checker4_0.rb 1013 def idem_Literal(o) 1014 true 1015 end
# File lib/puppet/pops/validation/checker4_0.rb 1021 def idem_LiteralHash(o) 1022 true 1023 end
# File lib/puppet/pops/validation/checker4_0.rb 1017 def idem_LiteralList(o) 1018 true 1019 end
# File lib/puppet/pops/validation/checker4_0.rb 1037 def idem_MatchExpression(o) 1038 false # can have side effect of setting $n match variables 1039 end
# File lib/puppet/pops/validation/checker4_0.rb 1009 def idem_NilClass(o) 1010 true 1011 end
# File lib/puppet/pops/validation/checker4_0.rb 1005 def idem_Nop(o) 1006 true 1007 end
# File lib/puppet/pops/validation/checker4_0.rb 1001 def idem_Object(o) 1002 false 1003 end
Allow (no-effect parentheses) to be used around a productive expression
# File lib/puppet/pops/validation/checker4_0.rb 1057 def idem_ParenthesizedExpression(o) 1058 idem(o.expr) 1059 end
# File lib/puppet/pops/validation/checker4_0.rb 1041 def idem_RelationshipExpression(o) 1042 # Always side effect 1043 false 1044 end
# File lib/puppet/pops/validation/checker4_0.rb 1061 def idem_RenderExpression(o) 1062 false 1063 end
# File lib/puppet/pops/validation/checker4_0.rb 1065 def idem_RenderStringExpression(o) 1066 false 1067 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 1088 def idem_SelectorExpression(o) 1089 true 1090 end
Handles UnaryMinusExpression, NotExpression, VariableExpression
# File lib/puppet/pops/validation/checker4_0.rb 1052 def idem_UnaryExpression(o) 1053 true 1054 end
# File lib/puppet/pops/validation/checker4_0.rb 609 def initial_manifest?(path, manifest_setting) 610 return false if manifest_setting.nil? || manifest_setting == :no_manifest 611 612 string_path = path.to_s 613 614 string_path == manifest_setting || string_path.start_with?(manifest_setting) 615 end
# File lib/puppet/pops/validation/checker4_0.rb 502 def internal_check_capture_last(o) 503 accepted_index = o.parameters.size() -1 504 o.parameters.each_with_index do |p, index| 505 if p.captures_rest && index != accepted_index 506 acceptor.accept(Issues::CAPTURES_REST_NOT_LAST, p, {:param_name => p.name}) 507 end 508 end 509 end
# File lib/puppet/pops/validation/checker4_0.rb 535 def internal_check_file_namespace(o) 536 file = o.locator.file 537 return if file.nil? || file == '' #e.g. puppet apply -e '...' 538 539 file_namespace = namespace_for_file(file) 540 return if file_namespace == NO_NAMESPACE 541 542 # Downcasing here because check is case-insensitive 543 if file_namespace == BAD_MODULE_FILE || !o.name.downcase.start_with?(file_namespace) 544 acceptor.accept(Issues::ILLEGAL_DEFINITION_LOCATION, o, {:name => o.name, :file => file}) 545 end 546 end
# File lib/puppet/pops/validation/checker4_0.rb 525 def internal_check_future_reserved_word(o, name) 526 if FUTURE_RESERVED_WORDS[name] 527 acceptor.accept(Issues::FUTURE_RESERVED_WORD, o, {:word => name}) 528 end 529 end
# File lib/puppet/pops/validation/checker4_0.rb 771 def internal_check_illegal_assignment(o) 772 if o.is_a?(Model::AssignmentExpression) 773 acceptor.accept(Issues::ILLEGAL_ASSIGNMENT_CONTEXT, o) 774 else 775 # recursively check all contents unless it's a lambda expression. A lambda may contain 776 # local assignments 777 o._pcore_contents {|model| internal_check_illegal_assignment(model) } unless o.is_a?(Model::LambdaExpression) 778 end 779 end
# File lib/puppet/pops/validation/checker4_0.rb 511 def internal_check_no_capture(o, container = o) 512 o.parameters.each do |p| 513 if p.captures_rest 514 acceptor.accept(Issues::CAPTURES_REST_NOT_SUPPORTED, p, {:container => container, :param_name => p.name}) 515 end 516 end 517 end
# File lib/puppet/pops/validation/checker4_0.rb 495 def internal_check_no_idem_last(o) 496 violator = ends_with_idem(o.body) 497 if violator 498 acceptor.accept(Issues::IDEM_NOT_ALLOWED_LAST, violator, {:container => o}) unless resource_without_title?(violator) 499 end 500 end
# File lib/puppet/pops/validation/checker4_0.rb 671 def internal_check_parameter_name_uniqueness(o) 672 unique = Set.new 673 o.parameters.each do |p| 674 acceptor.accept(Issues::DUPLICATE_PARAMETER, p, {:param_name => p.name}) unless unique.add?(p.name) 675 end 676 end
# File lib/puppet/pops/validation/checker4_0.rb 663 def internal_check_reserved_params(o) 664 o.parameters.each do |p| 665 if RESERVED_PARAMETERS[p.name] 666 acceptor.accept(Issues::RESERVED_PARAMETER, p, {:container => o, :param_name => p.name}) 667 end 668 end 669 end
# File lib/puppet/pops/validation/checker4_0.rb 519 def internal_check_reserved_type_name(o, name) 520 if RESERVED_TYPE_NAMES[name] 521 acceptor.accept(Issues::RESERVED_TYPE_NAME, o, {:name => name}) 522 end 523 end
# File lib/puppet/pops/validation/checker4_0.rb 481 def internal_check_return_type(o) 482 r = o.return_type 483 internal_check_type_ref(o, r) unless r.nil? 484 end
# File lib/puppet/pops/validation/checker4_0.rb 548 def internal_check_top_construct_in_module(prog) 549 return unless prog.is_a?(Model::Program) && !prog.body.nil? 550 551 #Check that this is a module autoloaded file 552 file = prog.locator.file 553 return if file.nil? 554 return if namespace_for_file(file) == NO_NAMESPACE 555 556 body = prog.body 557 return if prog.body.is_a?(Model::Nop) #Ignore empty or comment-only files 558 559 if(body.is_a?(Model::BlockExpression)) 560 body.statements.each { |s| acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, s) unless valid_top_construct?(s) } 561 else 562 acceptor.accept(Issues::ILLEGAL_TOP_CONSTRUCT_LOCATION, body) unless valid_top_construct?(body) 563 end 564 end
# File lib/puppet/pops/validation/checker4_0.rb 486 def internal_check_type_ref(o, r) 487 n = r.is_a?(Model::AccessExpression) ? r.left_expr : r 488 if n.is_a? Model::QualifiedReference 489 internal_check_future_reserved_word(r, n.value) 490 else 491 acceptor.accept(Issues::ILLEGAL_EXPRESSION, r, :feature => 'a type reference', :container => o) 492 end 493 end
# File lib/puppet/pops/validation/checker4_0.rb 625 def is_parent_dir_of(parent_dir, child_dir) 626 parent_dir_path = Pathname.new(parent_dir) 627 clean_parent = parent_dir_path.cleanpath.to_s + File::SEPARATOR 628 629 return child_dir.to_s.start_with?(clean_parent) 630 end
# File lib/puppet/pops/validation/checker4_0.rb 581 def namespace_for_file(file) 582 env = Puppet.lookup(:current_environment) 583 return NO_NAMESPACE if env.nil? 584 585 adapter = Puppet::Util::FileNamespaceAdapter.adapt(env) 586 587 file_namespace = adapter.file_to_namespace[file] 588 return file_namespace unless file_namespace.nil? # No cache entry, so we do the calculation 589 590 path = Pathname.new(file) 591 592 return adapter.file_to_namespace[file] = NO_NAMESPACE if path.extname != ".pp" 593 594 path = path.expand_path 595 596 return adapter.file_to_namespace[file] = NO_NAMESPACE if initial_manifest?(path, env.manifest) 597 598 #All auto-loaded files from modules come from a module search path dir 599 relative_path = get_module_relative_path(path, env.full_modulepath) 600 601 return adapter.file_to_namespace[file] = NO_NAMESPACE if relative_path == NO_PATH 602 603 #If a file comes from a module, but isn't in the right place, always error 604 names = dir_to_names(relative_path) 605 606 return adapter.file_to_namespace[file] = (names == BAD_MODULE_FILE ? BAD_MODULE_FILE : names.join("::").freeze) 607 end
# File lib/puppet/pops/validation/checker4_0.rb 438 def pattern_with_replacement?(o) 439 if o.is_a?(Model::LiteralList) 440 v = o.values 441 v.size == 2 && v[0].is_a?(Model::LiteralRegularExpression) && v[1].is_a?(Model::LiteralString) 442 else 443 false 444 end 445 end
Performs check if this is valid as a query
# File lib/puppet/pops/validation/checker4_0.rb 76 def query(o) 77 @@query_visitor.visit_this_0(self, o) 78 end
Allows AND, OR, and checks if left/right are allowed in query.
# File lib/puppet/pops/validation/checker4_0.rb 958 def query_BooleanExpression(o) 959 query o.left_expr 960 query o.right_expr 961 end
# File lib/puppet/pops/validation/checker4_0.rb 975 def query_LiteralBoolean(o); end
# File lib/puppet/pops/validation/checker4_0.rb 971 def query_LiteralNumber(o); end
# File lib/puppet/pops/validation/checker4_0.rb 973 def query_LiteralString(o); end
Anything not explicitly allowed is flagged as error.
# File lib/puppet/pops/validation/checker4_0.rb 947 def query_Object(o) 948 acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) 949 end
# File lib/puppet/pops/validation/checker4_0.rb 963 def query_ParenthesizedExpression(o) 964 query(o.expr) 965 end
# File lib/puppet/pops/validation/checker4_0.rb 969 def query_QualifiedName(o); end
# File lib/puppet/pops/validation/checker4_0.rb 967 def query_VariableExpression(o); end
Performs check if this is valid as a relationship side
# File lib/puppet/pops/validation/checker4_0.rb 81 def relation(o) 82 @@relation_visitor.visit_this_0(self, o) 83 end
# File lib/puppet/pops/validation/checker4_0.rb 754 def relation_CollectExpression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 750 def relation_Object(o) 751 rvalue(o) 752 end
# File lib/puppet/pops/validation/checker4_0.rb 756 def relation_RelationshipExpression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 255 def resource_without_title?(o) 256 if o.instance_of?(Model::BlockExpression) 257 statements = o.statements 258 statements.length == 2 && statements[0].instance_of?(Model::QualifiedName) && statements[1].instance_of?(Model::LiteralHash) 259 else 260 false 261 end 262 end
Performs check if this is valid as a rvalue
# File lib/puppet/pops/validation/checker4_0.rb 86 def rvalue(o) 87 @@rvalue_visitor.visit_this_0(self, o) 88 end
# File lib/puppet/pops/validation/checker4_0.rb 984 def rvalue_CollectExpression(o) 985 acceptor.accept(Issues::NOT_RVALUE, o) 986 end
# File lib/puppet/pops/validation/checker4_0.rb 988 def rvalue_Definition(o) 989 acceptor.accept(Issues::NOT_RVALUE, o) 990 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 982 def rvalue_Expression(o); end
# File lib/puppet/pops/validation/checker4_0.rb 992 def rvalue_NodeDefinition(o) 993 acceptor.accept(Issues::NOT_RVALUE, o) 994 end
# File lib/puppet/pops/validation/checker4_0.rb 996 def rvalue_UnaryExpression(o) 997 rvalue o.expr 998 end
# File lib/puppet/pops/validation/checker4_0.rb 92 def top(definition, idx = -1) 93 o = container(idx) 94 idx -= 1 95 case o 96 when NilClass, Model::ApplyExpression, Model::HostClassDefinition, Model::Program 97 # ok, stop scanning parents 98 when Model::BlockExpression 99 c = container(idx) 100 if !c.is_a?(Model::Program) && 101 (definition.is_a?(Model::FunctionDefinition) || definition.is_a?(Model::TypeAlias) || definition.is_a?(Model::TypeDefinition)) 102 103 # not ok. These can never be nested in a block 104 acceptor.accept(Issues::NOT_ABSOLUTE_TOP_LEVEL, definition) 105 else 106 # ok, if this is a block representing the body of a class, or is top level 107 top(definition, idx) 108 end 109 when Model::LambdaExpression 110 # A LambdaExpression is a BlockExpression, and this check is needed to prevent the polymorph method for BlockExpression 111 # to accept a lambda. 112 # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure. 113 acceptor.accept(Issues::NOT_TOP_LEVEL, definition) 114 else 115 # fail, reached a container that is not top level 116 acceptor.accept(Issues::NOT_TOP_LEVEL, definition) 117 end 118 end
# File lib/puppet/pops/validation/checker4_0.rb 447 def type_ref?(o) 448 o = o.left_expr if o.is_a?(Model::AccessExpression) 449 o.is_a?(Model::QualifiedReference) 450 end
# File lib/puppet/pops/validation/checker4_0.rb 566 def valid_top_construct?(o) 567 o.is_a?(Model::Definition) && !o.is_a?(Model::NodeDefinition) 568 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 52 def validate(model) 53 # tree iterate the model, and call check for each element 54 @path = [] 55 check(model) 56 internal_check_top_construct_in_module(model) 57 model._pcore_all_contents(@path) { |element| check(element) } 58 end
Produces string part of something named, or nil if not a QualifiedName or QualifiedReference
# File lib/puppet/pops/validation/checker4_0.rb 1118 def varname_to_s(o) 1119 case o 1120 when Model::QualifiedName 1121 o.value 1122 when Model::QualifiedReference 1123 o.value 1124 else 1125 nil 1126 end 1127 end