summaryrefslogtreecommitdiff
path: root/lib/puppet/pops/validation/checker3_1.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/pops/validation/checker3_1.rb')
-rw-r--r--lib/puppet/pops/validation/checker3_1.rb558
1 files changed, 0 insertions, 558 deletions
diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb
deleted file mode 100644
index 77f643fb1..000000000
--- a/lib/puppet/pops/validation/checker3_1.rb
+++ /dev/null
@@ -1,558 +0,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
-#
-class Puppet::Pops::Validation::Checker3_1
- Issues = Puppet::Pops::Issues
- Model = Puppet::Pops::Model
-
- attr_reader :acceptor
- # Initializes the validator with a diagnostics producer. This object must respond to
- # `:will_accept?` and `:accept`.
- #
- def initialize(diagnostics_producer)
- @@check_visitor ||= Puppet::Pops::Visitor.new(nil, "check", 0, 0)
- @@rvalue_visitor ||= Puppet::Pops::Visitor.new(nil, "rvalue", 0, 0)
- @@hostname_visitor ||= Puppet::Pops::Visitor.new(nil, "hostname", 1, 2)
- @@assignment_visitor ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1)
- @@query_visitor ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0)
- @@top_visitor ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1)
- @@relation_visitor ||= Puppet::Pops::Visitor.new(nil, "relation", 1, 1)
-
- @acceptor = diagnostics_producer
- 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.
- #
- def validate(model)
- # tree iterate the model, and call check for each element
- check(model)
- model.eAllContents.each {|m| check(m) }
- end
-
- # Performs regular validity check
- def check(o)
- @@check_visitor.visit_this_0(self, o)
- 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'
- def hostname(o, semantic, single_feature_name = nil)
- @@hostname_visitor.visit_this_2(self, o, semantic, single_feature_name)
- end
-
- # Performs check if this is valid as a query
- def query(o)
- @@query_visitor.visit_this_0(self, o)
- end
-
- # Performs check if this is valid as a relationship side
- def relation(o, container)
- @@relation_visitor.visit_this_1(self, o, container)
- end
-
- # Performs check if this is valid as a rvalue
- def rvalue(o)
- @@rvalue_visitor.visit_this_0(self, o)
- end
-
- # Performs check if this is valid as a container of a definition (class, define, node)
- def top(o, definition)
- @@top_visitor.visit_this_1(self, o, definition)
- end
-
- # Checks the LHS of an assignment (is it assignable?).
- # If args[0] is true, assignment via index is checked.
- #
- def assign(o, via_index = false)
- @@assignment_visitor.visit_this_1(self, o, via_index)
- end
-
- #---ASSIGNMENT CHECKS
-
- def assign_VariableExpression(o, via_index)
- varname_string = varname_to_s(o.expr)
- if varname_string =~ /^[0-9]+$/
- acceptor.accept(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o, :varname => varname_string)
- end
- # Can not assign to something in another namespace (i.e. a '::' in the name is not legal)
- if acceptor.will_accept? Issues::CROSS_SCOPE_ASSIGNMENT
- if varname_string =~ /::/
- acceptor.accept(Issues::CROSS_SCOPE_ASSIGNMENT, o, :name => varname_string)
- end
- end
- # TODO: Could scan for reassignment of the same variable if done earlier in the same container
- # Or if assigning to a parameter (more work).
- # TODO: Investigate if there are invalid cases for += assignment
- end
-
- def assign_AccessExpression(o, via_index)
- # Are indexed assignments allowed at all ? $x[x] = '...'
- if acceptor.will_accept? Issues::ILLEGAL_INDEXED_ASSIGNMENT
- acceptor.accept(Issues::ILLEGAL_INDEXED_ASSIGNMENT, o)
- else
- # Then the left expression must be assignable-via-index
- assign(o.left_expr, true)
- end
- end
-
- def assign_Object(o, via_index)
- # Can not assign to anything else (differentiate if this is via index or not)
- # i.e. 10 = 'hello' vs. 10['x'] = 'hello' (the root is reported as being in error in both cases)
- #
- acceptor.accept(via_index ? Issues::ILLEGAL_ASSIGNMENT_VIA_INDEX : Issues::ILLEGAL_ASSIGNMENT, o)
- end
-
- #---CHECKS
-
- def check_Object(o)
- end
-
- def check_Factory(o)
- check(o.current)
- end
-
- def check_AccessExpression(o)
- # Check multiplicity of keys
- case o.left_expr
- when Model::QualifiedName
- # allows many keys, but the name should really be a QualifiedReference
- acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.left_expr.value)
- when Model::QualifiedReference
- # ok, allows many - this is a resource reference
-
- else
- # i.e. for any other expression that may produce an array or hash
- if o.keys.size > 1
- acceptor.accept(Issues::UNSUPPORTED_RANGE, o, :count => o.keys.size)
- end
- if o.keys.size < 1
- acceptor.accept(Issues::MISSING_INDEX, o)
- end
- end
- end
-
- def check_AssignmentExpression(o)
- acceptor.accept(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) unless [:'=', :'+='].include? o.operator
- assign(o.left_expr)
- rvalue(o.right_expr)
- 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)
- #
- def check_AttributeOperation(o)
- if o.operator == :'+>'
- # Append operator use is constrained
- parent = o.eContainer
- unless parent.is_a?(Model::CollectExpression) || parent.is_a?(Model::ResourceOverrideExpression)
- acceptor.accept(Issues::ILLEGAL_ATTRIBUTE_APPEND, o, {:name=>o.attribute_name, :parent=>parent})
- end
- end
- rvalue(o.value_expr)
- end
-
- def check_BinaryExpression(o)
- rvalue(o.left_expr)
- rvalue(o.right_expr)
- end
-
- def check_CallNamedFunctionExpression(o)
- unless o.functor_expr.is_a? Model::QualifiedName
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o)
- end
- end
-
- def check_MethodCallExpression(o)
- unless o.functor_expr.is_a? Model::QualifiedName
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.functor_expr, :feature => 'function name', :container => o)
- end
- end
-
- def check_CaseExpression(o)
- # There should only be one LiteralDefault case option value
- # TODO: Implement this check
- end
-
- def check_CollectExpression(o)
- unless o.type_expr.is_a? Model::QualifiedReference
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_expr, :feature=> 'type name', :container => o)
- end
-
- # If a collect expression tries to collect exported resources and storeconfigs is not on
- # then it will not work... This was checked in the parser previously. This is a runtime checking
- # thing as opposed to a language thing.
- if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.query.is_a?(Model::ExportedQuery)
- acceptor.accept(Issues::RT_NO_STORECONFIGS, o)
- end
- end
-
- # Only used for function names, grammar should not be able to produce something faulty, but
- # check anyway if model is created programatically (it will fail in transformation to AST for sure).
- def check_NamedAccessExpression(o)
- name = o.right_expr
- unless name.is_a? Model::QualifiedName
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, name, :feature=> 'function name', :container => o.eContainer)
- end
- end
-
- # for 'class' and 'define'
- def check_NamedDefinition(o)
- top(o.eContainer, o)
- if (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.name.include?('-')
- acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.name})
- end
- end
-
- def check_ImportExpression(o)
- o.files.each do |f|
- unless f.is_a? Model::LiteralString
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, f, :feature => 'file name', :container => o)
- end
- end
- end
-
- def check_InstanceReference(o)
- # TODO: Original warning is :
- # Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
- # This model element is not used in the egrammar.
- # Either implement checks or deprecate the use of InstanceReference (the same is acheived by
- # transformation of AccessExpression when used where an Instance/Resource reference is allowed.
- #
- end
-
- # Restrictions on hash key are because of the strange key comparisons/and merge rules in the AST evaluation
- # (Even the allowed ones are handled in a strange way).
- #
- def transform_KeyedEntry(o)
- case o.key
- when Model::QualifiedName
- when Model::LiteralString
- when Model::LiteralNumber
- when Model::ConcatenatedString
- else
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.key, :feature => 'hash key', :container => o.eContainer)
- end
- end
-
- # A Lambda is a Definition, but it may appear in other scopes that top scope (Which check_Definition asserts).
- #
- def check_LambdaExpression(o)
- end
-
- def check_NodeDefinition(o)
- # Check that hostnames are valid hostnames (or regular expressons)
- hostname(o.host_matches, o)
- hostname(o.parent, o, 'parent') unless o.parent.nil?
- top(o.eContainer, o)
- 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.
- #
- def check_QualifiedName(o)
- 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
- def check_QualifiedReference(o)
- # Is this a valid qualified name?
- if o.value !~ Puppet::Pops::Patterns::CLASSREF
- acceptor.accept(Issues::ILLEGAL_CLASSREF, o, {:name=>o.value})
- elsif (acceptor.will_accept? Issues::NAME_WITH_HYPHEN) && o.value.include?('-')
- acceptor.accept(Issues::NAME_WITH_HYPHEN, o, {:name => o.value})
- end
- end
-
- def check_QueryExpression(o)
- query(o.expr) if o.expr # is optional
- end
-
- def relation_Object(o, rel_expr)
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature => o.eContainingFeature, :container => rel_expr})
- end
-
- def relation_AccessExpression(o, rel_expr); end
-
- def relation_CollectExpression(o, rel_expr); end
-
- def relation_VariableExpression(o, rel_expr); end
-
- def relation_LiteralString(o, rel_expr); end
-
- def relation_ConcatenatedStringExpression(o, rel_expr); end
-
- def relation_SelectorExpression(o, rel_expr); end
-
- def relation_CaseExpression(o, rel_expr); end
-
- def relation_ResourceExpression(o, rel_expr); end
-
- def relation_RelationshipExpression(o, rel_expr); end
-
- def check_Parameter(o)
- if o.name =~ /^[0-9]+$/
- acceptor.accept(Issues::ILLEGAL_NUMERIC_PARAMETER, o, :name => o.name)
- end
- end
-
- #relationship_side: resource
- # | resourceref
- # | collection
- # | variable
- # | quotedtext
- # | selector
- # | casestatement
- # | hasharrayaccesses
-
- def check_RelationshipExpression(o)
- relation(o.left_expr, o)
- relation(o.right_expr, o)
- end
-
- def check_ResourceExpression(o)
- # A resource expression must have a lower case NAME as its type e.g. 'file { ... }'
- unless o.type_name.is_a? Model::QualifiedName
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.type_name, :feature => 'resource type', :container => o)
- end
-
- # This is a runtime check - the model is valid, but will have runtime issues when evaluated
- # and storeconfigs is not set.
- if acceptor.will_accept?(Issues::RT_NO_STORECONFIGS) && o.exported
- acceptor.accept(Issues::RT_NO_STORECONFIGS_EXPORT, o)
- end
- end
-
- def check_ResourceDefaultsExpression(o)
- if o.form && o.form != :regular
- acceptor.accept(Issues::NOT_VIRTUALIZEABLE, o)
- end
- end
-
- # Transformation of SelectorExpression is limited to certain types of expressions.
- # This is probably due to constraints in the old grammar rather than any real concerns.
- def select_SelectorExpression(o)
- case o.left_expr
- when Model::CallNamedFunctionExpression
- when Model::AccessExpression
- when Model::VariableExpression
- when Model::ConcatenatedString
- else
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o.left_expr, :feature => 'left operand', :container => o)
- end
- end
-
- def check_UnaryExpression(o)
- rvalue(o.expr)
- end
-
- def check_UnlessExpression(o)
- # TODO: Unless may not have an elsif
- # TODO: 3.x unless may not have an else
- end
-
- def check_VariableExpression(o)
- # The expression must be a qualified name
- if !o.expr.is_a? Model::QualifiedName
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, :feature => 'name', :container => o)
- else
- # Note, that if it later becomes illegal with hyphen in any name, this special check
- # can be skipped in favor of the check in QualifiedName, which is now not done if contained in
- # a VariableExpression
- name = o.expr.value
- if (acceptor.will_accept? Issues::VAR_WITH_HYPHEN) && name.include?('-')
- acceptor.accept(Issues::VAR_WITH_HYPHEN, o, {:name => name})
- end
- end
- end
-
- #--- HOSTNAME CHECKS
-
- # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
- def hostname_Array(o, semantic, single_feature_name)
- if single_feature_name
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=>single_feature_name, :container=>semantic})
- end
- o.each {|x| hostname(x, semantic, false) }
- end
-
- def hostname_String(o, semantic, single_feature_name)
- # The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid,
- # but this allows pathological names like "a..b......c", "----"
- # TODO: Investigate if more illegal hostnames should be flagged.
- #
- if o =~ Puppet::Pops::Patterns::ILLEGAL_HOSTNAME_CHARS
- acceptor.accept(Issues::ILLEGAL_HOSTNAME_CHARS, semantic, :hostname => o)
- end
- end
-
- def hostname_LiteralValue(o, semantic, single_feature_name)
- hostname_String(o.value.to_s, o, single_feature_name)
- end
-
- def hostname_ConcatenatedString(o, semantic, single_feature_name)
- # Puppet 3.1. only accepts a concatenated string without interpolated expressions
- if the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) }
- acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr)
- elsif o.segments.size() != 1
- # corner case, bad model, concatenation of several plain strings
- acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o)
- else
- # corner case, may be ok, but lexer may have replaced with plain string, this is
- # here if it does not
- hostname_String(o.segments[0], o.segments[0], false)
- end
- end
-
- def hostname_QualifiedName(o, semantic, single_feature_name)
- hostname_String(o.value.to_s, o, single_feature_name)
- end
-
- def hostname_QualifiedReference(o, semantic, single_feature_name)
- hostname_String(o.value.to_s, o, single_feature_name)
- end
-
- def hostname_LiteralNumber(o, semantic, single_feature_name)
- # always ok
- end
-
- def hostname_LiteralDefault(o, semantic, single_feature_name)
- # always ok
- end
-
- def hostname_LiteralRegularExpression(o, semantic, single_feature_name)
- # always ok
- end
-
- def hostname_Object(o, semantic, single_feature_name)
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=> single_feature_name || 'hostname', :container=>semantic})
- end
-
- #---QUERY CHECKS
-
- # Anything not explicitly allowed is flagged as error.
- def query_Object(o)
- acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o)
- end
-
- # Puppet AST only allows == and !=
- #
- def query_ComparisonExpression(o)
- acceptor.accept(Issues::ILLEGAL_QUERY_EXPRESSION, o) unless [:'==', :'!='].include? o.operator
- end
-
- # Allows AND, OR, and checks if left/right are allowed in query.
- def query_BooleanExpression(o)
- query o.left_expr
- query o.right_expr
- end
-
- def query_ParenthesizedExpression(o)
- query(o.expr)
- end
-
- def query_VariableExpression(o); end
-
- def query_QualifiedName(o); end
-
- def query_LiteralNumber(o); end
-
- def query_LiteralString(o); end
-
- def query_LiteralBoolean(o); end
-
- #---RVALUE CHECKS
-
- # By default, all expressions are reported as being rvalues
- # Implement specific rvalue checks for those that are not.
- #
- def rvalue_Expression(o); end
-
- def rvalue_ImportExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_BlockExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_CaseExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_IfExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_UnlessExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_ResourceExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_ResourceDefaultsExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_ResourceOverrideExpression(o); acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_CollectExpression(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_Definition(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_NodeDefinition(o) ; acceptor.accept(Issues::NOT_RVALUE, o) ; end
-
- def rvalue_UnaryExpression(o) ; rvalue o.expr ; end
-
- #---TOP CHECK
-
- def top_NilClass(o, definition)
- # ok, reached the top, no more parents
- end
-
- def top_Object(o, definition)
- # fail, reached a container that is not top level
- acceptor.accept(Issues::NOT_TOP_LEVEL, definition)
- end
-
- def top_BlockExpression(o, definition)
- # ok, if this is a block representing the body of a class, or is top level
- top o.eContainer, definition
- end
-
- def top_HostClassDefinition(o, definition)
- # ok, stop scanning parents
- end
-
- def top_Program(o, definition)
- # ok
- end
-
- # A LambdaExpression is a BlockExpression, and this method is needed to prevent the polymorph method for BlockExpression
- # to accept a lambda.
- # A lambda can not iteratively create classes, nodes or defines as the lambda does not have a closure.
- #
- def top_LambdaExpression(o, definition)
- # fail, stop scanning parents
- acceptor.accept(Issues::NOT_TOP_LEVEL, definition)
- end
-
- #--- NON POLYMORPH, NON CHECKING CODE
-
- # Produces string part of something named, or nil if not a QualifiedName or QualifiedReference
- #
- def varname_to_s(o)
- case o
- when Model::QualifiedName
- o.value
- when Model::QualifiedReference
- o.value
- else
- nil
- end
- end
-end