mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: bump the golang-dependencies group with 1 update
Bumps the golang-dependencies group with 1 update: [golang.org/x/crypto](https://github.com/golang/crypto). Updates `golang.org/x/crypto` from 0.16.0 to 0.17.0 - [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
mergify[bot]
parent
1ad79314f9
commit
e5d9b68d36
8
vendor/github.com/google/cel-go/cel/BUILD.bazel
generated
vendored
8
vendor/github.com/google/cel-go/cel/BUILD.bazel
generated
vendored
@ -15,6 +15,7 @@ go_library(
|
||||
"macro.go",
|
||||
"options.go",
|
||||
"program.go",
|
||||
"validator.go",
|
||||
],
|
||||
importpath = "github.com/google/cel-go/cel",
|
||||
visibility = ["//visibility:public"],
|
||||
@ -22,15 +23,18 @@ go_library(
|
||||
"//checker:go_default_library",
|
||||
"//checker/decls:go_default_library",
|
||||
"//common:go_default_library",
|
||||
"//common/ast:go_default_library",
|
||||
"//common/containers:go_default_library",
|
||||
"//common/decls:go_default_library",
|
||||
"//common/functions:go_default_library",
|
||||
"//common/operators:go_default_library",
|
||||
"//common/overloads:go_default_library",
|
||||
"//common/stdlib:go_default_library",
|
||||
"//common/types:go_default_library",
|
||||
"//common/types/pb:go_default_library",
|
||||
"//common/types/ref:go_default_library",
|
||||
"//common/types/traits:go_default_library",
|
||||
"//interpreter:go_default_library",
|
||||
"//interpreter/functions:go_default_library",
|
||||
"//parser:go_default_library",
|
||||
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
@ -72,6 +76,8 @@ go_test(
|
||||
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
|
||||
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
"@org_golang_google_protobuf//encoding/prototext:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/structpb:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
|
||||
],
|
||||
)
|
||||
|
1055
vendor/github.com/google/cel-go/cel/decls.go
generated
vendored
1055
vendor/github.com/google/cel-go/cel/decls.go
generated
vendored
File diff suppressed because it is too large
Load Diff
368
vendor/github.com/google/cel-go/cel/env.go
generated
vendored
368
vendor/github.com/google/cel-go/cel/env.go
generated
vendored
@ -16,13 +16,14 @@ package cel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/checker/decls"
|
||||
chkdecls "github.com/google/cel-go/checker/decls"
|
||||
"github.com/google/cel-go/common"
|
||||
celast "github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/decls"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
@ -40,8 +41,8 @@ type Ast struct {
|
||||
expr *exprpb.Expr
|
||||
info *exprpb.SourceInfo
|
||||
source Source
|
||||
refMap map[int64]*exprpb.Reference
|
||||
typeMap map[int64]*exprpb.Type
|
||||
refMap map[int64]*celast.ReferenceInfo
|
||||
typeMap map[int64]*types.Type
|
||||
}
|
||||
|
||||
// Expr returns the proto serializable instance of the parsed/checked expression.
|
||||
@ -60,21 +61,26 @@ func (ast *Ast) SourceInfo() *exprpb.SourceInfo {
|
||||
}
|
||||
|
||||
// ResultType returns the output type of the expression if the Ast has been type-checked, else
|
||||
// returns decls.Dyn as the parse step cannot infer the type.
|
||||
// returns chkdecls.Dyn as the parse step cannot infer the type.
|
||||
//
|
||||
// Deprecated: use OutputType
|
||||
func (ast *Ast) ResultType() *exprpb.Type {
|
||||
if !ast.IsChecked() {
|
||||
return decls.Dyn
|
||||
return chkdecls.Dyn
|
||||
}
|
||||
return ast.typeMap[ast.expr.GetId()]
|
||||
out := ast.OutputType()
|
||||
t, err := TypeToExprType(out)
|
||||
if err != nil {
|
||||
return chkdecls.Dyn
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// OutputType returns the output type of the expression if the Ast has been type-checked, else
|
||||
// returns cel.DynType as the parse step cannot infer types.
|
||||
func (ast *Ast) OutputType() *Type {
|
||||
t, err := ExprTypeToType(ast.ResultType())
|
||||
if err != nil {
|
||||
t, found := ast.typeMap[ast.expr.GetId()]
|
||||
if !found {
|
||||
return DynType
|
||||
}
|
||||
return t
|
||||
@ -87,22 +93,33 @@ func (ast *Ast) Source() Source {
|
||||
}
|
||||
|
||||
// FormatType converts a type message into a string representation.
|
||||
//
|
||||
// Deprecated: prefer FormatCELType
|
||||
func FormatType(t *exprpb.Type) string {
|
||||
return checker.FormatCheckedType(t)
|
||||
}
|
||||
|
||||
// FormatCELType formats a cel.Type value to a string representation.
|
||||
//
|
||||
// The type formatting is identical to FormatType.
|
||||
func FormatCELType(t *Type) string {
|
||||
return checker.FormatCELType(t)
|
||||
}
|
||||
|
||||
// Env encapsulates the context necessary to perform parsing, type checking, or generation of
|
||||
// evaluable programs for different expressions.
|
||||
type Env struct {
|
||||
Container *containers.Container
|
||||
functions map[string]*functionDecl
|
||||
declarations []*exprpb.Decl
|
||||
variables []*decls.VariableDecl
|
||||
functions map[string]*decls.FunctionDecl
|
||||
macros []parser.Macro
|
||||
adapter ref.TypeAdapter
|
||||
provider ref.TypeProvider
|
||||
adapter types.Adapter
|
||||
provider types.Provider
|
||||
features map[int]bool
|
||||
appliedFeatures map[int]bool
|
||||
libraries map[string]bool
|
||||
validators []ASTValidator
|
||||
costOptions []checker.CostOption
|
||||
|
||||
// Internal parser representation
|
||||
prsr *parser.Parser
|
||||
@ -154,8 +171,8 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) {
|
||||
return nil, err
|
||||
}
|
||||
return (&Env{
|
||||
declarations: []*exprpb.Decl{},
|
||||
functions: map[string]*functionDecl{},
|
||||
variables: []*decls.VariableDecl{},
|
||||
functions: map[string]*decls.FunctionDecl{},
|
||||
macros: []parser.Macro{},
|
||||
Container: containers.DefaultContainer,
|
||||
adapter: registry,
|
||||
@ -163,14 +180,20 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) {
|
||||
features: map[int]bool{},
|
||||
appliedFeatures: map[int]bool{},
|
||||
libraries: map[string]bool{},
|
||||
validators: []ASTValidator{},
|
||||
progOpts: []ProgramOption{},
|
||||
costOptions: []checker.CostOption{},
|
||||
}).configure(opts)
|
||||
}
|
||||
|
||||
// Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues.
|
||||
// If any `ASTValidators` are configured on the environment, they will be applied after a valid
|
||||
// type-check result. If any issues are detected, the validators will provide them on the
|
||||
// output Issues object.
|
||||
//
|
||||
// Checking has failed if the returned Issues value and its Issues.Err() value are non-nil.
|
||||
// Issues should be inspected if they are non-nil, but may not represent a fatal error.
|
||||
// Either checking or validation has failed if the returned Issues value and its Issues.Err()
|
||||
// value are non-nil. Issues should be inspected if they are non-nil, but may not represent a
|
||||
// fatal error.
|
||||
//
|
||||
// It is possible to have both non-nil Ast and Issues values returned from this call: however,
|
||||
// the mere presence of an Ast does not imply that it is valid for use.
|
||||
@ -183,21 +206,38 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
|
||||
if err != nil {
|
||||
errs := common.NewErrors(ast.Source())
|
||||
errs.ReportError(common.NoLocation, err.Error())
|
||||
return nil, NewIssues(errs)
|
||||
return nil, NewIssuesWithSourceInfo(errs, ast.SourceInfo())
|
||||
}
|
||||
|
||||
res, errs := checker.Check(pe, ast.Source(), chk)
|
||||
if len(errs.GetErrors()) > 0 {
|
||||
return nil, NewIssues(errs)
|
||||
return nil, NewIssuesWithSourceInfo(errs, ast.SourceInfo())
|
||||
}
|
||||
// Manually create the Ast to ensure that the Ast source information (which may be more
|
||||
// detailed than the information provided by Check), is returned to the caller.
|
||||
return &Ast{
|
||||
ast = &Ast{
|
||||
source: ast.Source(),
|
||||
expr: res.GetExpr(),
|
||||
info: res.GetSourceInfo(),
|
||||
refMap: res.GetReferenceMap(),
|
||||
typeMap: res.GetTypeMap()}, nil
|
||||
expr: res.Expr,
|
||||
info: res.SourceInfo,
|
||||
refMap: res.ReferenceMap,
|
||||
typeMap: res.TypeMap}
|
||||
|
||||
// Generate a validator configuration from the set of configured validators.
|
||||
vConfig := newValidatorConfig()
|
||||
for _, v := range e.validators {
|
||||
if cv, ok := v.(ASTValidatorConfigurer); ok {
|
||||
cv.Configure(vConfig)
|
||||
}
|
||||
}
|
||||
// Apply additional validators on the type-checked result.
|
||||
iss := NewIssuesWithSourceInfo(errs, ast.SourceInfo())
|
||||
for _, v := range e.validators {
|
||||
v.Validate(e, vConfig, res, iss)
|
||||
}
|
||||
if iss.Err() != nil {
|
||||
return nil, iss
|
||||
}
|
||||
return ast, nil
|
||||
}
|
||||
|
||||
// Compile combines the Parse and Check phases CEL program compilation to produce an Ast and
|
||||
@ -255,7 +295,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
copy(chkOptsCopy, e.chkOpts)
|
||||
|
||||
// Copy the declarations if needed.
|
||||
decsCopy := []*exprpb.Decl{}
|
||||
varsCopy := []*decls.VariableDecl{}
|
||||
if chk != nil {
|
||||
// If the type-checker has already been instantiated, then the e.declarations have been
|
||||
// validated within the chk instance.
|
||||
@ -263,8 +303,8 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
} else {
|
||||
// If the type-checker has not been instantiated, ensure the unvalidated declarations are
|
||||
// provided to the extended Env instance.
|
||||
decsCopy = make([]*exprpb.Decl, len(e.declarations))
|
||||
copy(decsCopy, e.declarations)
|
||||
varsCopy = make([]*decls.VariableDecl, len(e.variables))
|
||||
copy(varsCopy, e.variables)
|
||||
}
|
||||
|
||||
// Copy macros and program options
|
||||
@ -276,8 +316,8 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
// Copy the adapter / provider if they appear to be mutable.
|
||||
adapter := e.adapter
|
||||
provider := e.provider
|
||||
adapterReg, isAdapterReg := e.adapter.(ref.TypeRegistry)
|
||||
providerReg, isProviderReg := e.provider.(ref.TypeRegistry)
|
||||
adapterReg, isAdapterReg := e.adapter.(*types.Registry)
|
||||
providerReg, isProviderReg := e.provider.(*types.Registry)
|
||||
// In most cases the provider and adapter will be a ref.TypeRegistry;
|
||||
// however, in the rare cases where they are not, they are assumed to
|
||||
// be immutable. Since it is possible to set the TypeProvider separately
|
||||
@ -308,7 +348,7 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
for k, v := range e.appliedFeatures {
|
||||
appliedFeaturesCopy[k] = v
|
||||
}
|
||||
funcsCopy := make(map[string]*functionDecl, len(e.functions))
|
||||
funcsCopy := make(map[string]*decls.FunctionDecl, len(e.functions))
|
||||
for k, v := range e.functions {
|
||||
funcsCopy[k] = v
|
||||
}
|
||||
@ -316,10 +356,14 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
for k, v := range e.libraries {
|
||||
libsCopy[k] = v
|
||||
}
|
||||
validatorsCopy := make([]ASTValidator, len(e.validators))
|
||||
copy(validatorsCopy, e.validators)
|
||||
costOptsCopy := make([]checker.CostOption, len(e.costOptions))
|
||||
copy(costOptsCopy, e.costOptions)
|
||||
|
||||
ext := &Env{
|
||||
Container: e.Container,
|
||||
declarations: decsCopy,
|
||||
variables: varsCopy,
|
||||
functions: funcsCopy,
|
||||
macros: macsCopy,
|
||||
progOpts: progOptsCopy,
|
||||
@ -327,9 +371,11 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
|
||||
features: featuresCopy,
|
||||
appliedFeatures: appliedFeaturesCopy,
|
||||
libraries: libsCopy,
|
||||
validators: validatorsCopy,
|
||||
provider: provider,
|
||||
chkOpts: chkOptsCopy,
|
||||
prsrOpts: prsrOptsCopy,
|
||||
costOptions: costOptsCopy,
|
||||
}
|
||||
return ext.configure(opts)
|
||||
}
|
||||
@ -347,6 +393,25 @@ func (e *Env) HasLibrary(libName string) bool {
|
||||
return exists && configured
|
||||
}
|
||||
|
||||
// Libraries returns a list of SingletonLibrary that have been configured in the environment.
|
||||
func (e *Env) Libraries() []string {
|
||||
libraries := make([]string, 0, len(e.libraries))
|
||||
for libName := range e.libraries {
|
||||
libraries = append(libraries, libName)
|
||||
}
|
||||
return libraries
|
||||
}
|
||||
|
||||
// HasValidator returns whether a specific ASTValidator has been configured in the environment.
|
||||
func (e *Env) HasValidator(name string) bool {
|
||||
for _, v := range e.validators {
|
||||
if v.Name() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse parses the input expression value `txt` to a Ast and/or a set of Issues.
|
||||
//
|
||||
// This form of Parse creates a Source value for the input `txt` and forwards to the
|
||||
@ -388,36 +453,64 @@ func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error) {
|
||||
return newProgram(e, ast, optSet)
|
||||
}
|
||||
|
||||
// CELTypeAdapter returns the `types.Adapter` configured for the environment.
|
||||
func (e *Env) CELTypeAdapter() types.Adapter {
|
||||
return e.adapter
|
||||
}
|
||||
|
||||
// CELTypeProvider returns the `types.Provider` configured for the environment.
|
||||
func (e *Env) CELTypeProvider() types.Provider {
|
||||
return e.provider
|
||||
}
|
||||
|
||||
// TypeAdapter returns the `ref.TypeAdapter` configured for the environment.
|
||||
//
|
||||
// Deprecated: use CELTypeAdapter()
|
||||
func (e *Env) TypeAdapter() ref.TypeAdapter {
|
||||
return e.adapter
|
||||
}
|
||||
|
||||
// TypeProvider returns the `ref.TypeProvider` configured for the environment.
|
||||
//
|
||||
// Deprecated: use CELTypeProvider()
|
||||
func (e *Env) TypeProvider() ref.TypeProvider {
|
||||
return e.provider
|
||||
if legacyProvider, ok := e.provider.(ref.TypeProvider); ok {
|
||||
return legacyProvider
|
||||
}
|
||||
return &interopLegacyTypeProvider{Provider: e.provider}
|
||||
}
|
||||
|
||||
// UnknownVars returns an interpreter.PartialActivation which marks all variables
|
||||
// declared in the Env as unknown AttributePattern values.
|
||||
// UnknownVars returns an interpreter.PartialActivation which marks all variables declared in the
|
||||
// Env as unknown AttributePattern values.
|
||||
//
|
||||
// Note, the UnknownVars will behave the same as an interpreter.EmptyActivation
|
||||
// unless the PartialAttributes option is provided as a ProgramOption.
|
||||
// Note, the UnknownVars will behave the same as an interpreter.EmptyActivation unless the
|
||||
// PartialAttributes option is provided as a ProgramOption.
|
||||
func (e *Env) UnknownVars() interpreter.PartialActivation {
|
||||
var unknownPatterns []*interpreter.AttributePattern
|
||||
for _, d := range e.declarations {
|
||||
switch d.GetDeclKind().(type) {
|
||||
case *exprpb.Decl_Ident:
|
||||
unknownPatterns = append(unknownPatterns,
|
||||
interpreter.NewAttributePattern(d.GetName()))
|
||||
}
|
||||
}
|
||||
part, _ := PartialVars(
|
||||
interpreter.EmptyActivation(),
|
||||
unknownPatterns...)
|
||||
act := interpreter.EmptyActivation()
|
||||
part, _ := PartialVars(act, e.computeUnknownVars(act)...)
|
||||
return part
|
||||
}
|
||||
|
||||
// PartialVars returns an interpreter.PartialActivation where all variables not in the input variable
|
||||
// set, but which have been configured in the environment, are marked as unknown.
|
||||
//
|
||||
// The `vars` value may either be an interpreter.Activation or any valid input to the
|
||||
// interpreter.NewActivation call.
|
||||
//
|
||||
// Note, this is equivalent to calling cel.PartialVars and manually configuring the set of unknown
|
||||
// variables. For more advanced use cases of partial state where portions of an object graph, rather
|
||||
// than top-level variables, are missing the PartialVars() method may be a more suitable choice.
|
||||
//
|
||||
// Note, the PartialVars will behave the same as an interpreter.EmptyActivation unless the
|
||||
// PartialAttributes option is provided as a ProgramOption.
|
||||
func (e *Env) PartialVars(vars any) (interpreter.PartialActivation, error) {
|
||||
act, err := interpreter.NewActivation(vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return PartialVars(act, e.computeUnknownVars(act)...)
|
||||
}
|
||||
|
||||
// ResidualAst takes an Ast and its EvalDetails to produce a new Ast which only contains the
|
||||
// attribute references which are unknown.
|
||||
//
|
||||
@ -463,11 +556,16 @@ func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) {
|
||||
// EstimateCost estimates the cost of a type checked CEL expression using the length estimates of input data and
|
||||
// extension functions provided by estimator.
|
||||
func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator, opts ...checker.CostOption) (checker.CostEstimate, error) {
|
||||
checked, err := AstToCheckedExpr(ast)
|
||||
if err != nil {
|
||||
return checker.CostEstimate{}, fmt.Errorf("EsimateCost could not inspect Ast: %v", err)
|
||||
checked := &celast.CheckedAST{
|
||||
Expr: ast.Expr(),
|
||||
SourceInfo: ast.SourceInfo(),
|
||||
TypeMap: ast.typeMap,
|
||||
ReferenceMap: ast.refMap,
|
||||
}
|
||||
return checker.Cost(checked, estimator, opts...)
|
||||
extendedOpts := make([]checker.CostOption, 0, len(e.costOptions))
|
||||
extendedOpts = append(extendedOpts, opts...)
|
||||
extendedOpts = append(extendedOpts, e.costOptions...)
|
||||
return checker.Cost(checked, estimator, extendedOpts...)
|
||||
}
|
||||
|
||||
// configure applies a series of EnvOptions to the current environment.
|
||||
@ -488,14 +586,6 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize all of the functions configured within the environment.
|
||||
for _, fn := range e.functions {
|
||||
err = fn.init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the parser.
|
||||
prsrOpts := []parser.Option{}
|
||||
prsrOpts = append(prsrOpts, e.prsrOpts...)
|
||||
@ -504,6 +594,9 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
|
||||
if e.HasFeature(featureEnableMacroCallTracking) {
|
||||
prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true))
|
||||
}
|
||||
if e.HasFeature(featureVariadicLogicalASTs) {
|
||||
prsrOpts = append(prsrOpts, parser.EnableVariadicOperatorASTs(true))
|
||||
}
|
||||
e.prsr, err = parser.NewParser(prsrOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -525,8 +618,6 @@ func (e *Env) initChecker() (*checker.Env, error) {
|
||||
chkOpts := []checker.Option{}
|
||||
chkOpts = append(chkOpts, e.chkOpts...)
|
||||
chkOpts = append(chkOpts,
|
||||
checker.HomogeneousAggregateLiterals(
|
||||
e.HasFeature(featureDisableDynamicAggregateLiterals)),
|
||||
checker.CrossTypeNumericComparisons(
|
||||
e.HasFeature(featureCrossTypeNumericComparisons)))
|
||||
|
||||
@ -536,19 +627,17 @@ func (e *Env) initChecker() (*checker.Env, error) {
|
||||
return
|
||||
}
|
||||
// Add the statically configured declarations.
|
||||
err = ce.Add(e.declarations...)
|
||||
err = ce.AddIdents(e.variables...)
|
||||
if err != nil {
|
||||
e.setCheckerOrError(nil, err)
|
||||
return
|
||||
}
|
||||
// Add the function declarations which are derived from the FunctionDecl instances.
|
||||
for _, fn := range e.functions {
|
||||
fnDecl, err := functionDeclToExprDecl(fn)
|
||||
if err != nil {
|
||||
e.setCheckerOrError(nil, err)
|
||||
return
|
||||
if fn.IsDeclarationDisabled() {
|
||||
continue
|
||||
}
|
||||
err = ce.Add(fnDecl)
|
||||
err = ce.AddFunctions(fn)
|
||||
if err != nil {
|
||||
e.setCheckerOrError(nil, err)
|
||||
return
|
||||
@ -596,17 +685,43 @@ func (e *Env) maybeApplyFeature(feature int, option EnvOption) (*Env, error) {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// computeUnknownVars determines a set of missing variables based on the input activation and the
|
||||
// environment's configured declaration set.
|
||||
func (e *Env) computeUnknownVars(vars interpreter.Activation) []*interpreter.AttributePattern {
|
||||
var unknownPatterns []*interpreter.AttributePattern
|
||||
for _, v := range e.variables {
|
||||
varName := v.Name()
|
||||
if _, found := vars.ResolveName(varName); found {
|
||||
continue
|
||||
}
|
||||
unknownPatterns = append(unknownPatterns, interpreter.NewAttributePattern(varName))
|
||||
}
|
||||
return unknownPatterns
|
||||
}
|
||||
|
||||
// Error type which references an expression id, a location within source, and a message.
|
||||
type Error = common.Error
|
||||
|
||||
// Issues defines methods for inspecting the error details of parse and check calls.
|
||||
//
|
||||
// Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct.
|
||||
type Issues struct {
|
||||
errs *common.Errors
|
||||
info *exprpb.SourceInfo
|
||||
}
|
||||
|
||||
// NewIssues returns an Issues struct from a common.Errors object.
|
||||
func NewIssues(errs *common.Errors) *Issues {
|
||||
return NewIssuesWithSourceInfo(errs, nil)
|
||||
}
|
||||
|
||||
// NewIssuesWithSourceInfo returns an Issues struct from a common.Errors object with SourceInfo metatata
|
||||
// which can be used with the `ReportErrorAtID` method for additional error reports within the context
|
||||
// information that's inferred from an expression id.
|
||||
func NewIssuesWithSourceInfo(errs *common.Errors, info *exprpb.SourceInfo) *Issues {
|
||||
return &Issues{
|
||||
errs: errs,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,9 +737,9 @@ func (i *Issues) Err() error {
|
||||
}
|
||||
|
||||
// Errors returns the collection of errors encountered in more granular detail.
|
||||
func (i *Issues) Errors() []common.Error {
|
||||
func (i *Issues) Errors() []*Error {
|
||||
if i == nil {
|
||||
return []common.Error{}
|
||||
return []*Error{}
|
||||
}
|
||||
return i.errs.GetErrors()
|
||||
}
|
||||
@ -648,6 +763,37 @@ func (i *Issues) String() string {
|
||||
return i.errs.ToDisplayString()
|
||||
}
|
||||
|
||||
// ReportErrorAtID reports an error message with an optional set of formatting arguments.
|
||||
//
|
||||
// The source metadata for the expression at `id`, if present, is attached to the error report.
|
||||
// To ensure that source metadata is attached to error reports, use NewIssuesWithSourceInfo.
|
||||
func (i *Issues) ReportErrorAtID(id int64, message string, args ...any) {
|
||||
i.errs.ReportErrorAtID(id, locationByID(id, i.info), message, args...)
|
||||
}
|
||||
|
||||
// locationByID returns a common.Location given an expression id.
|
||||
//
|
||||
// TODO: move this functionality into the native SourceInfo and an overhaul of the common.Source
|
||||
// as this implementation relies on the abstractions present in the protobuf SourceInfo object,
|
||||
// and is replicated in the checker.
|
||||
func locationByID(id int64, sourceInfo *exprpb.SourceInfo) common.Location {
|
||||
positions := sourceInfo.GetPositions()
|
||||
var line = 1
|
||||
if offset, found := positions[id]; found {
|
||||
col := int(offset)
|
||||
for _, lineOffset := range sourceInfo.GetLineOffsets() {
|
||||
if lineOffset < offset {
|
||||
line++
|
||||
col = int(offset - lineOffset)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return common.NewLocation(line, col)
|
||||
}
|
||||
return common.NoLocation
|
||||
}
|
||||
|
||||
// getStdEnv lazy initializes the CEL standard environment.
|
||||
func getStdEnv() (*Env, error) {
|
||||
stdEnvInit.Do(func() {
|
||||
@ -656,6 +802,90 @@ func getStdEnv() (*Env, error) {
|
||||
return stdEnv, stdEnvErr
|
||||
}
|
||||
|
||||
// interopCELTypeProvider layers support for the types.Provider interface on top of a ref.TypeProvider.
|
||||
type interopCELTypeProvider struct {
|
||||
ref.TypeProvider
|
||||
}
|
||||
|
||||
// FindStructType returns a types.Type instance for the given fully-qualified typeName if one exists.
|
||||
//
|
||||
// This method proxies to the underyling ref.TypeProvider's FindType method and converts protobuf type
|
||||
// into a native type representation. If the conversion fails, the type is listed as not found.
|
||||
func (p *interopCELTypeProvider) FindStructType(typeName string) (*types.Type, bool) {
|
||||
if et, found := p.FindType(typeName); found {
|
||||
t, err := types.ExprTypeToType(et)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return t, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindStructFieldType returns a types.FieldType instance for the given fully-qualified typeName and field
|
||||
// name, if one exists.
|
||||
//
|
||||
// This method proxies to the underyling ref.TypeProvider's FindFieldType method and converts protobuf type
|
||||
// into a native type representation. If the conversion fails, the type is listed as not found.
|
||||
func (p *interopCELTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) {
|
||||
if ft, found := p.FindFieldType(structType, fieldName); found {
|
||||
t, err := types.ExprTypeToType(ft.Type)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return &types.FieldType{
|
||||
Type: t,
|
||||
IsSet: ft.IsSet,
|
||||
GetFrom: ft.GetFrom,
|
||||
}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// interopLegacyTypeProvider layers support for the ref.TypeProvider interface on top of a types.Provider.
|
||||
type interopLegacyTypeProvider struct {
|
||||
types.Provider
|
||||
}
|
||||
|
||||
// FindType retruns the protobuf Type representation for the input type name if one exists.
|
||||
//
|
||||
// This method proxies to the underlying types.Provider FindStructType method and converts the types.Type
|
||||
// value to a protobuf Type representation.
|
||||
//
|
||||
// Failure to convert the type will result in the type not being found.
|
||||
func (p *interopLegacyTypeProvider) FindType(typeName string) (*exprpb.Type, bool) {
|
||||
if t, found := p.FindStructType(typeName); found {
|
||||
et, err := types.TypeToExprType(t)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return et, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindFieldType returns the protobuf-based FieldType representation for the input type name and field,
|
||||
// if one exists.
|
||||
//
|
||||
// This call proxies to the types.Provider FindStructFieldType method and converts the types.FIeldType
|
||||
// value to a protobuf-based ref.FieldType representation if found.
|
||||
//
|
||||
// Failure to convert the FieldType will result in the field not being found.
|
||||
func (p *interopLegacyTypeProvider) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) {
|
||||
if cft, found := p.FindStructFieldType(structType, fieldName); found {
|
||||
et, err := types.TypeToExprType(cft.Type)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return &ref.FieldType{
|
||||
Type: et,
|
||||
IsSet: cft.IsSet,
|
||||
GetFrom: cft.GetFrom,
|
||||
}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var (
|
||||
stdEnvInit sync.Once
|
||||
stdEnv *Env
|
||||
|
46
vendor/github.com/google/cel-go/cel/io.go
generated
vendored
46
vendor/github.com/google/cel-go/cel/io.go
generated
vendored
@ -22,6 +22,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
@ -33,7 +34,8 @@ import (
|
||||
|
||||
// CheckedExprToAst converts a checked expression proto message to an Ast.
|
||||
func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast {
|
||||
return CheckedExprToAstWithSource(checkedExpr, nil)
|
||||
checked, _ := CheckedExprToAstWithSource(checkedExpr, nil)
|
||||
return checked
|
||||
}
|
||||
|
||||
// CheckedExprToAstWithSource converts a checked expression proto message to an Ast,
|
||||
@ -44,29 +46,18 @@ func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast {
|
||||
// through future calls.
|
||||
//
|
||||
// Prefer CheckedExprToAst if loading expressions from storage.
|
||||
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) *Ast {
|
||||
refMap := checkedExpr.GetReferenceMap()
|
||||
if refMap == nil {
|
||||
refMap = map[int64]*exprpb.Reference{}
|
||||
}
|
||||
typeMap := checkedExpr.GetTypeMap()
|
||||
if typeMap == nil {
|
||||
typeMap = map[int64]*exprpb.Type{}
|
||||
}
|
||||
si := checkedExpr.GetSourceInfo()
|
||||
if si == nil {
|
||||
si = &exprpb.SourceInfo{}
|
||||
}
|
||||
if src == nil {
|
||||
src = common.NewInfoSource(si)
|
||||
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) (*Ast, error) {
|
||||
checkedAST, err := ast.CheckedExprToCheckedAST(checkedExpr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Ast{
|
||||
expr: checkedExpr.GetExpr(),
|
||||
info: si,
|
||||
expr: checkedAST.Expr,
|
||||
info: checkedAST.SourceInfo,
|
||||
source: src,
|
||||
refMap: refMap,
|
||||
typeMap: typeMap,
|
||||
}
|
||||
refMap: checkedAST.ReferenceMap,
|
||||
typeMap: checkedAST.TypeMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AstToCheckedExpr converts an Ast to an protobuf CheckedExpr value.
|
||||
@ -76,12 +67,13 @@ func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error) {
|
||||
if !a.IsChecked() {
|
||||
return nil, fmt.Errorf("cannot convert unchecked ast")
|
||||
}
|
||||
return &exprpb.CheckedExpr{
|
||||
Expr: a.Expr(),
|
||||
SourceInfo: a.SourceInfo(),
|
||||
cAst := &ast.CheckedAST{
|
||||
Expr: a.expr,
|
||||
SourceInfo: a.info,
|
||||
ReferenceMap: a.refMap,
|
||||
TypeMap: a.typeMap,
|
||||
}, nil
|
||||
}
|
||||
return ast.CheckedASTToCheckedExpr(cAst)
|
||||
}
|
||||
|
||||
// ParsedExprToAst converts a parsed expression proto message to an Ast.
|
||||
@ -202,7 +194,7 @@ func RefValueToValue(res ref.Val) (*exprpb.Value, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
typeNameToTypeValue = map[string]*types.TypeValue{
|
||||
typeNameToTypeValue = map[string]ref.Val{
|
||||
"bool": types.BoolType,
|
||||
"bytes": types.BytesType,
|
||||
"double": types.DoubleType,
|
||||
@ -219,7 +211,7 @@ var (
|
||||
)
|
||||
|
||||
// ValueToRefValue converts between exprpb.Value and ref.Val.
|
||||
func ValueToRefValue(adapter ref.TypeAdapter, v *exprpb.Value) (ref.Val, error) {
|
||||
func ValueToRefValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error) {
|
||||
switch v.Kind.(type) {
|
||||
case *exprpb.Value_NullValue:
|
||||
return types.NullValue, nil
|
||||
|
262
vendor/github.com/google/cel-go/cel/library.go
generated
vendored
262
vendor/github.com/google/cel-go/cel/library.go
generated
vendored
@ -15,19 +15,18 @@
|
||||
package cel
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/common/operators"
|
||||
"github.com/google/cel-go/common/overloads"
|
||||
"github.com/google/cel-go/common/stdlib"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/common/types/traits"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
"github.com/google/cel-go/parser"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
@ -35,6 +34,7 @@ import (
|
||||
|
||||
const (
|
||||
optMapMacro = "optMap"
|
||||
optFlatMapMacro = "optFlatMap"
|
||||
hasValueFunc = "hasValue"
|
||||
optionalNoneFunc = "optional.none"
|
||||
optionalOfFunc = "optional.of"
|
||||
@ -106,44 +106,213 @@ func (stdLibrary) LibraryName() string {
|
||||
return "cel.lib.std"
|
||||
}
|
||||
|
||||
// EnvOptions returns options for the standard CEL function declarations and macros.
|
||||
// CompileOptions returns options for the standard CEL function declarations and macros.
|
||||
func (stdLibrary) CompileOptions() []EnvOption {
|
||||
return []EnvOption{
|
||||
Declarations(checker.StandardDeclarations()...),
|
||||
func(e *Env) (*Env, error) {
|
||||
var err error
|
||||
for _, fn := range stdlib.Functions() {
|
||||
existing, found := e.functions[fn.Name()]
|
||||
if found {
|
||||
fn, err = existing.Merge(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
e.functions[fn.Name()] = fn
|
||||
}
|
||||
return e, nil
|
||||
},
|
||||
func(e *Env) (*Env, error) {
|
||||
e.variables = append(e.variables, stdlib.Types()...)
|
||||
return e, nil
|
||||
},
|
||||
Macros(StandardMacros...),
|
||||
}
|
||||
}
|
||||
|
||||
// ProgramOptions returns function implementations for the standard CEL functions.
|
||||
func (stdLibrary) ProgramOptions() []ProgramOption {
|
||||
return []ProgramOption{
|
||||
Functions(functions.StandardOverloads()...),
|
||||
return []ProgramOption{}
|
||||
}
|
||||
|
||||
// OptionalTypes enable support for optional syntax and types in CEL.
|
||||
//
|
||||
// The optional value type makes it possible to express whether variables have
|
||||
// been provided, whether a result has been computed, and in the future whether
|
||||
// an object field path, map key value, or list index has a value.
|
||||
//
|
||||
// # Syntax Changes
|
||||
//
|
||||
// OptionalTypes are unlike other CEL extensions because they modify the CEL
|
||||
// syntax itself, notably through the use of a `?` preceding a field name or
|
||||
// index value.
|
||||
//
|
||||
// ## Field Selection
|
||||
//
|
||||
// The optional syntax in field selection is denoted as `obj.?field`. In other
|
||||
// words, if a field is set, return `optional.of(obj.field)“, else
|
||||
// `optional.none()`. The optional field selection is viral in the sense that
|
||||
// after the first optional selection all subsequent selections or indices
|
||||
// are treated as optional, i.e. the following expressions are equivalent:
|
||||
//
|
||||
// obj.?field.subfield
|
||||
// obj.?field.?subfield
|
||||
//
|
||||
// ## Indexing
|
||||
//
|
||||
// Similar to field selection, the optional syntax can be used in index
|
||||
// expressions on maps and lists:
|
||||
//
|
||||
// list[?0]
|
||||
// map[?key]
|
||||
//
|
||||
// ## Optional Field Setting
|
||||
//
|
||||
// When creating map or message literals, if a field may be optionally set
|
||||
// based on its presence, then placing a `?` before the field name or key
|
||||
// will ensure the type on the right-hand side must be optional(T) where T
|
||||
// is the type of the field or key-value.
|
||||
//
|
||||
// The following returns a map with the key expression set only if the
|
||||
// subfield is present, otherwise an empty map is created:
|
||||
//
|
||||
// {?key: obj.?field.subfield}
|
||||
//
|
||||
// ## Optional Element Setting
|
||||
//
|
||||
// When creating list literals, an element in the list may be optionally added
|
||||
// when the element expression is preceded by a `?`:
|
||||
//
|
||||
// [a, ?b, ?c] // return a list with either [a], [a, b], [a, b, c], or [a, c]
|
||||
//
|
||||
// # Optional.Of
|
||||
//
|
||||
// Create an optional(T) value of a given value with type T.
|
||||
//
|
||||
// optional.of(10)
|
||||
//
|
||||
// # Optional.OfNonZeroValue
|
||||
//
|
||||
// Create an optional(T) value of a given value with type T if it is not a
|
||||
// zero-value. A zero-value the default empty value for any given CEL type,
|
||||
// including empty protobuf message types. If the value is empty, the result
|
||||
// of this call will be optional.none().
|
||||
//
|
||||
// optional.ofNonZeroValue([1, 2, 3]) // optional(list(int))
|
||||
// optional.ofNonZeroValue([]) // optional.none()
|
||||
// optional.ofNonZeroValue(0) // optional.none()
|
||||
// optional.ofNonZeroValue("") // optional.none()
|
||||
//
|
||||
// # Optional.None
|
||||
//
|
||||
// Create an empty optional value.
|
||||
//
|
||||
// # HasValue
|
||||
//
|
||||
// Determine whether the optional contains a value.
|
||||
//
|
||||
// optional.of(b'hello').hasValue() // true
|
||||
// optional.ofNonZeroValue({}).hasValue() // false
|
||||
//
|
||||
// # Value
|
||||
//
|
||||
// Get the value contained by the optional. If the optional does not have a
|
||||
// value, the result will be a CEL error.
|
||||
//
|
||||
// optional.of(b'hello').value() // b'hello'
|
||||
// optional.ofNonZeroValue({}).value() // error
|
||||
//
|
||||
// # Or
|
||||
//
|
||||
// If the value on the left-hand side is optional.none(), the optional value
|
||||
// on the right hand side is returned. If the value on the left-hand set is
|
||||
// valued, then it is returned. This operation is short-circuiting and will
|
||||
// only evaluate as many links in the `or` chain as are needed to return a
|
||||
// non-empty optional value.
|
||||
//
|
||||
// obj.?field.or(m[?key])
|
||||
// l[?index].or(obj.?field.subfield).or(obj.?other)
|
||||
//
|
||||
// # OrValue
|
||||
//
|
||||
// Either return the value contained within the optional on the left-hand side
|
||||
// or return the alternative value on the right hand side.
|
||||
//
|
||||
// m[?key].orValue("none")
|
||||
//
|
||||
// # OptMap
|
||||
//
|
||||
// Apply a transformation to the optional's underlying value if it is not empty
|
||||
// and return an optional typed result based on the transformation. The
|
||||
// transformation expression type must return a type T which is wrapped into
|
||||
// an optional.
|
||||
//
|
||||
// msg.?elements.optMap(e, e.size()).orValue(0)
|
||||
//
|
||||
// # OptFlatMap
|
||||
//
|
||||
// Introduced in version: 1
|
||||
//
|
||||
// Apply a transformation to the optional's underlying value if it is not empty
|
||||
// and return the result. The transform expression must return an optional(T)
|
||||
// rather than type T. This can be useful when dealing with zero values and
|
||||
// conditionally generating an empty or non-empty result in ways which cannot
|
||||
// be expressed with `optMap`.
|
||||
//
|
||||
// msg.?elements.optFlatMap(e, e[?0]) // return the first element if present.
|
||||
func OptionalTypes(opts ...OptionalTypesOption) EnvOption {
|
||||
lib := &optionalLib{version: math.MaxUint32}
|
||||
for _, opt := range opts {
|
||||
lib = opt(lib)
|
||||
}
|
||||
return Lib(lib)
|
||||
}
|
||||
|
||||
type optionalLib struct {
|
||||
version uint32
|
||||
}
|
||||
|
||||
// OptionalTypesOption is a functional interface for configuring the strings library.
|
||||
type OptionalTypesOption func(*optionalLib) *optionalLib
|
||||
|
||||
// OptionalTypesVersion configures the version of the optional type library.
|
||||
//
|
||||
// The version limits which functions are available. Only functions introduced
|
||||
// below or equal to the given version included in the library. If this option
|
||||
// is not set, all functions are available.
|
||||
//
|
||||
// See the library documentation to determine which version a function was introduced.
|
||||
// If the documentation does not state which version a function was introduced, it can
|
||||
// be assumed to be introduced at version 0, when the library was first created.
|
||||
func OptionalTypesVersion(version uint32) OptionalTypesOption {
|
||||
return func(lib *optionalLib) *optionalLib {
|
||||
lib.version = version
|
||||
return lib
|
||||
}
|
||||
}
|
||||
|
||||
type optionalLibrary struct{}
|
||||
|
||||
// LibraryName implements the SingletonLibrary interface method.
|
||||
func (optionalLibrary) LibraryName() string {
|
||||
func (lib *optionalLib) LibraryName() string {
|
||||
return "cel.lib.optional"
|
||||
}
|
||||
|
||||
// CompileOptions implements the Library interface method.
|
||||
func (optionalLibrary) CompileOptions() []EnvOption {
|
||||
func (lib *optionalLib) CompileOptions() []EnvOption {
|
||||
paramTypeK := TypeParamType("K")
|
||||
paramTypeV := TypeParamType("V")
|
||||
optionalTypeV := OptionalType(paramTypeV)
|
||||
listTypeV := ListType(paramTypeV)
|
||||
mapTypeKV := MapType(paramTypeK, paramTypeV)
|
||||
|
||||
return []EnvOption{
|
||||
opts := []EnvOption{
|
||||
// Enable the optional syntax in the parser.
|
||||
enableOptionalSyntax(),
|
||||
|
||||
// Introduce the optional type.
|
||||
Types(types.OptionalType),
|
||||
|
||||
// Configure the optMap macro.
|
||||
// Configure the optMap and optFlatMap macros.
|
||||
Macros(NewReceiverMacro(optMapMacro, 2, optMap)),
|
||||
|
||||
// Global and member functions for working with optional values.
|
||||
@ -202,21 +371,29 @@ func (optionalLibrary) CompileOptions() []EnvOption {
|
||||
// Index overloads to accommodate using an optional value as the operand.
|
||||
Function(operators.Index,
|
||||
Overload("optional_list_index_int", []*Type{OptionalType(listTypeV), IntType}, optionalTypeV),
|
||||
Overload("optional_map_index_optional_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
|
||||
Overload("optional_map_index_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
|
||||
}
|
||||
if lib.version >= 1 {
|
||||
opts = append(opts, Macros(NewReceiverMacro(optFlatMapMacro, 2, optFlatMap)))
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// ProgramOptions implements the Library interface method.
|
||||
func (lib *optionalLib) ProgramOptions() []ProgramOption {
|
||||
return []ProgramOption{
|
||||
CustomDecorator(decorateOptionalOr),
|
||||
}
|
||||
}
|
||||
|
||||
func optMap(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func optMap(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
varIdent := args[0]
|
||||
varName := ""
|
||||
switch varIdent.GetExprKind().(type) {
|
||||
case *exprpb.Expr_IdentExpr:
|
||||
varName = varIdent.GetIdentExpr().GetName()
|
||||
default:
|
||||
return nil, &common.Error{
|
||||
Message: "optMap() variable name must be a simple identifier",
|
||||
Location: meh.OffsetLocation(varIdent.GetId()),
|
||||
}
|
||||
return nil, meh.NewError(varIdent.GetId(), "optMap() variable name must be a simple identifier")
|
||||
}
|
||||
mapExpr := args[1]
|
||||
return meh.GlobalCall(
|
||||
@ -237,11 +414,30 @@ func optMap(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exp
|
||||
), nil
|
||||
}
|
||||
|
||||
// ProgramOptions implements the Library interface method.
|
||||
func (optionalLibrary) ProgramOptions() []ProgramOption {
|
||||
return []ProgramOption{
|
||||
CustomDecorator(decorateOptionalOr),
|
||||
func optFlatMap(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
varIdent := args[0]
|
||||
varName := ""
|
||||
switch varIdent.GetExprKind().(type) {
|
||||
case *exprpb.Expr_IdentExpr:
|
||||
varName = varIdent.GetIdentExpr().GetName()
|
||||
default:
|
||||
return nil, meh.NewError(varIdent.GetId(), "optFlatMap() variable name must be a simple identifier")
|
||||
}
|
||||
mapExpr := args[1]
|
||||
return meh.GlobalCall(
|
||||
operators.Conditional,
|
||||
meh.ReceiverCall(hasValueFunc, target),
|
||||
meh.Fold(
|
||||
unusedIterVar,
|
||||
meh.NewList(),
|
||||
varName,
|
||||
meh.ReceiverCall(valueFunc, target),
|
||||
meh.LiteralBool(false),
|
||||
meh.Ident(varName),
|
||||
mapExpr,
|
||||
),
|
||||
meh.GlobalCall(optionalNoneFunc),
|
||||
), nil
|
||||
}
|
||||
|
||||
func enableOptionalSyntax() EnvOption {
|
||||
@ -358,28 +554,16 @@ var (
|
||||
timeOverloadDeclarations = []EnvOption{
|
||||
Function(overloads.TimeGetHours,
|
||||
MemberOverload(overloads.DurationToHours, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Hours())
|
||||
}))),
|
||||
UnaryBinding(types.DurationGetHours))),
|
||||
Function(overloads.TimeGetMinutes,
|
||||
MemberOverload(overloads.DurationToMinutes, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Minutes())
|
||||
}))),
|
||||
UnaryBinding(types.DurationGetMinutes))),
|
||||
Function(overloads.TimeGetSeconds,
|
||||
MemberOverload(overloads.DurationToSeconds, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Seconds())
|
||||
}))),
|
||||
UnaryBinding(types.DurationGetSeconds))),
|
||||
Function(overloads.TimeGetMilliseconds,
|
||||
MemberOverload(overloads.DurationToMilliseconds, []*Type{DurationType}, IntType,
|
||||
UnaryBinding(func(dur ref.Val) ref.Val {
|
||||
d := dur.(types.Duration)
|
||||
return types.Int(d.Milliseconds())
|
||||
}))),
|
||||
UnaryBinding(types.DurationGetMilliseconds))),
|
||||
Function(overloads.TimeGetFullYear,
|
||||
MemberOverload(overloads.TimestampToYear, []*Type{TimestampType}, IntType,
|
||||
UnaryBinding(func(ts ref.Val) ref.Val {
|
||||
|
11
vendor/github.com/google/cel-go/cel/macro.go
generated
vendored
11
vendor/github.com/google/cel-go/cel/macro.go
generated
vendored
@ -15,7 +15,6 @@
|
||||
package cel
|
||||
|
||||
import (
|
||||
"github.com/google/cel-go/common"
|
||||
"github.com/google/cel-go/parser"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
@ -63,21 +62,21 @@ func NewReceiverVarArgMacro(function string, expander MacroExpander) Macro {
|
||||
}
|
||||
|
||||
// HasMacroExpander expands the input call arguments into a presence test, e.g. has(<operand>.field)
|
||||
func HasMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func HasMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
return parser.MakeHas(meh, target, args)
|
||||
}
|
||||
|
||||
// ExistsMacroExpander expands the input call arguments into a comprehension that returns true if any of the
|
||||
// elements in the range match the predicate expressions:
|
||||
// <iterRange>.exists(<iterVar>, <predicate>)
|
||||
func ExistsMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func ExistsMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
return parser.MakeExists(meh, target, args)
|
||||
}
|
||||
|
||||
// ExistsOneMacroExpander expands the input call arguments into a comprehension that returns true if exactly
|
||||
// one of the elements in the range match the predicate expressions:
|
||||
// <iterRange>.exists_one(<iterVar>, <predicate>)
|
||||
func ExistsOneMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func ExistsOneMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
return parser.MakeExistsOne(meh, target, args)
|
||||
}
|
||||
|
||||
@ -91,14 +90,14 @@ func ExistsOneMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*ex
|
||||
//
|
||||
// In the second form only iterVar values which return true when provided to the predicate expression
|
||||
// are transformed.
|
||||
func MapMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func MapMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
return parser.MakeMap(meh, target, args)
|
||||
}
|
||||
|
||||
// FilterMacroExpander expands the input call arguments into a comprehension which produces a list which contains
|
||||
// only elements which match the provided predicate expression:
|
||||
// <iterRange>.filter(<iterVar>, <predicate>)
|
||||
func FilterMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *common.Error) {
|
||||
func FilterMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
|
||||
return parser.MakeFilter(meh, target, args)
|
||||
}
|
||||
|
||||
|
180
vendor/github.com/google/cel-go/cel/options.go
generated
vendored
180
vendor/github.com/google/cel-go/cel/options.go
generated
vendored
@ -23,12 +23,13 @@ import (
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/dynamicpb"
|
||||
|
||||
"github.com/google/cel-go/checker/decls"
|
||||
"github.com/google/cel-go/checker"
|
||||
"github.com/google/cel-go/common/containers"
|
||||
"github.com/google/cel-go/common/functions"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/pb"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
"github.com/google/cel-go/interpreter/functions"
|
||||
"github.com/google/cel-go/parser"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
@ -41,13 +42,6 @@ import (
|
||||
const (
|
||||
_ = iota
|
||||
|
||||
// Disallow heterogeneous aggregate (list, map) literals.
|
||||
// Note, it is still possible to have heterogeneous aggregates when
|
||||
// provided as variables to the expression, as well as via conversion
|
||||
// of well-known dynamic types, or with unchecked expressions.
|
||||
// Affects checking. Provides a subset of standard behavior.
|
||||
featureDisableDynamicAggregateLiterals
|
||||
|
||||
// Enable the tracking of function call expressions replaced by macros.
|
||||
featureEnableMacroCallTracking
|
||||
|
||||
@ -63,9 +57,10 @@ const (
|
||||
// is not already in UTC.
|
||||
featureDefaultUTCTimeZone
|
||||
|
||||
// Enable the use of optional types in the syntax, type-system, type-checking,
|
||||
// and runtime.
|
||||
featureOptionalTypes
|
||||
// Enable the serialization of logical operator ASTs as variadic calls, thus
|
||||
// compressing the logic graph to a single call when multiple like-operator
|
||||
// expressions occur: e.g. a && b && c && d -> call(_&&_, [a, b, c, d])
|
||||
featureVariadicLogicalASTs
|
||||
)
|
||||
|
||||
// EnvOption is a functional interface for configuring the environment.
|
||||
@ -82,23 +77,26 @@ func ClearMacros() EnvOption {
|
||||
}
|
||||
}
|
||||
|
||||
// CustomTypeAdapter swaps the default ref.TypeAdapter implementation with a custom one.
|
||||
// CustomTypeAdapter swaps the default types.Adapter implementation with a custom one.
|
||||
//
|
||||
// Note: This option must be specified before the Types and TypeDescs options when used together.
|
||||
func CustomTypeAdapter(adapter ref.TypeAdapter) EnvOption {
|
||||
func CustomTypeAdapter(adapter types.Adapter) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.adapter = adapter
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CustomTypeProvider swaps the default ref.TypeProvider implementation with a custom one.
|
||||
// CustomTypeProvider replaces the types.Provider implementation with a custom one.
|
||||
//
|
||||
// The `provider` variable type may either be types.Provider or ref.TypeProvider (deprecated)
|
||||
//
|
||||
// Note: This option must be specified before the Types and TypeDescs options when used together.
|
||||
func CustomTypeProvider(provider ref.TypeProvider) EnvOption {
|
||||
func CustomTypeProvider(provider any) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.provider = provider
|
||||
return e, nil
|
||||
var err error
|
||||
e.provider, err = maybeInteropProvider(provider)
|
||||
return e, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,8 +106,28 @@ func CustomTypeProvider(provider ref.TypeProvider) EnvOption {
|
||||
// for the environment. The NewEnv call builds on top of the standard CEL declarations. For a
|
||||
// purely custom set of declarations use NewCustomEnv.
|
||||
func Declarations(decls ...*exprpb.Decl) EnvOption {
|
||||
declOpts := []EnvOption{}
|
||||
var err error
|
||||
var opt EnvOption
|
||||
// Convert the declarations to `EnvOption` values ahead of time.
|
||||
// Surface any errors in conversion when the options are applied.
|
||||
for _, d := range decls {
|
||||
opt, err = ExprDeclToDeclaration(d)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
declOpts = append(declOpts, opt)
|
||||
}
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.declarations = append(e.declarations, decls...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, o := range declOpts {
|
||||
e, err = o(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
@ -126,14 +144,25 @@ func EagerlyValidateDeclarations(enabled bool) EnvOption {
|
||||
return features(featureEagerlyValidateDeclarations, enabled)
|
||||
}
|
||||
|
||||
// HomogeneousAggregateLiterals option ensures that list and map literal entry types must agree
|
||||
// during type-checking.
|
||||
// HomogeneousAggregateLiterals disables mixed type list and map literal values.
|
||||
//
|
||||
// Note, it is still possible to have heterogeneous aggregates when provided as variables to the
|
||||
// expression, as well as via conversion of well-known dynamic types, or with unchecked
|
||||
// expressions.
|
||||
func HomogeneousAggregateLiterals() EnvOption {
|
||||
return features(featureDisableDynamicAggregateLiterals, true)
|
||||
return ASTValidators(ValidateHomogeneousAggregateLiterals())
|
||||
}
|
||||
|
||||
// variadicLogicalOperatorASTs flatten like-operator chained logical expressions into a single
|
||||
// variadic call with N-terms. This behavior is useful when serializing to a protocol buffer as
|
||||
// it will reduce the number of recursive calls needed to deserialize the AST later.
|
||||
//
|
||||
// For example, given the following expression the call graph will be rendered accordingly:
|
||||
//
|
||||
// expression: a && b && c && (d || e)
|
||||
// ast: call(_&&_, [a, b, c, call(_||_, [d, e])])
|
||||
func variadicLogicalOperatorASTs() EnvOption {
|
||||
return features(featureVariadicLogicalASTs, true)
|
||||
}
|
||||
|
||||
// Macros option extends the macro set configured in the environment.
|
||||
@ -226,7 +255,12 @@ func Abbrevs(qualifiedNames ...string) EnvOption {
|
||||
// Note: This option must be specified after the CustomTypeProvider option when used together.
|
||||
func Types(addTypes ...any) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
reg, isReg := e.provider.(ref.TypeRegistry)
|
||||
var reg ref.TypeRegistry
|
||||
var isReg bool
|
||||
reg, isReg = e.provider.(*types.Registry)
|
||||
if !isReg {
|
||||
reg, isReg = e.provider.(ref.TypeRegistry)
|
||||
}
|
||||
if !isReg {
|
||||
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
|
||||
}
|
||||
@ -436,6 +470,24 @@ func InterruptCheckFrequency(checkFrequency uint) ProgramOption {
|
||||
}
|
||||
}
|
||||
|
||||
// CostEstimatorOptions configure type-check time options for estimating expression cost.
|
||||
func CostEstimatorOptions(costOpts ...checker.CostOption) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
e.costOptions = append(e.costOptions, costOpts...)
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CostTrackerOptions configures a set of options for cost-tracking.
|
||||
//
|
||||
// Note, CostTrackerOptions is a no-op unless CostTracking is also enabled.
|
||||
func CostTrackerOptions(costOpts ...interpreter.CostTrackerOption) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
p.costOptions = append(p.costOptions, costOpts...)
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CostTracking enables cost tracking and registers a ActualCostEstimator that can optionally provide a runtime cost estimate for any function calls.
|
||||
func CostTracking(costEstimator interpreter.ActualCostEstimator) ProgramOption {
|
||||
return func(p *prog) (*prog, error) {
|
||||
@ -457,25 +509,21 @@ func CostLimit(costLimit uint64) ProgramOption {
|
||||
}
|
||||
}
|
||||
|
||||
func fieldToCELType(field protoreflect.FieldDescriptor) (*exprpb.Type, error) {
|
||||
func fieldToCELType(field protoreflect.FieldDescriptor) (*Type, error) {
|
||||
if field.Kind() == protoreflect.MessageKind || field.Kind() == protoreflect.GroupKind {
|
||||
msgName := (string)(field.Message().FullName())
|
||||
wellKnownType, found := pb.CheckedWellKnowns[msgName]
|
||||
if found {
|
||||
return wellKnownType, nil
|
||||
}
|
||||
return decls.NewObjectType(msgName), nil
|
||||
return ObjectType(msgName), nil
|
||||
}
|
||||
if primitiveType, found := pb.CheckedPrimitives[field.Kind()]; found {
|
||||
if primitiveType, found := types.ProtoCELPrimitives[field.Kind()]; found {
|
||||
return primitiveType, nil
|
||||
}
|
||||
if field.Kind() == protoreflect.EnumKind {
|
||||
return decls.Int, nil
|
||||
return IntType, nil
|
||||
}
|
||||
return nil, fmt.Errorf("field %s type %s not implemented", field.FullName(), field.Kind().String())
|
||||
}
|
||||
|
||||
func fieldToDecl(field protoreflect.FieldDescriptor) (*exprpb.Decl, error) {
|
||||
func fieldToVariable(field protoreflect.FieldDescriptor) (EnvOption, error) {
|
||||
name := string(field.Name())
|
||||
if field.IsMap() {
|
||||
mapKey := field.MapKey()
|
||||
@ -488,20 +536,20 @@ func fieldToDecl(field protoreflect.FieldDescriptor) (*exprpb.Decl, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, decls.NewMapType(keyType, valueType)), nil
|
||||
return Variable(name, MapType(keyType, valueType)), nil
|
||||
}
|
||||
if field.IsList() {
|
||||
elemType, err := fieldToCELType(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, decls.NewListType(elemType)), nil
|
||||
return Variable(name, ListType(elemType)), nil
|
||||
}
|
||||
celType, err := fieldToCELType(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decls.NewVar(name, celType), nil
|
||||
return Variable(name, celType), nil
|
||||
}
|
||||
|
||||
// DeclareContextProto returns an option to extend CEL environment with declarations from the given context proto.
|
||||
@ -509,25 +557,53 @@ func fieldToDecl(field protoreflect.FieldDescriptor) (*exprpb.Decl, error) {
|
||||
// https://github.com/google/cel-spec/blob/master/doc/langdef.md#evaluation-environment
|
||||
func DeclareContextProto(descriptor protoreflect.MessageDescriptor) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
var decls []*exprpb.Decl
|
||||
fields := descriptor.Fields()
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
field := fields.Get(i)
|
||||
decl, err := fieldToDecl(field)
|
||||
variable, err := fieldToVariable(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e, err = variable(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decls = append(decls, decl)
|
||||
}
|
||||
var err error
|
||||
e, err = Declarations(decls...)(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Types(dynamicpb.NewMessage(descriptor))(e)
|
||||
}
|
||||
}
|
||||
|
||||
// ContextProtoVars uses the fields of the input proto.Messages as top-level variables within an Activation.
|
||||
//
|
||||
// Consider using with `DeclareContextProto` to simplify variable type declarations and publishing when using
|
||||
// protocol buffers.
|
||||
func ContextProtoVars(ctx proto.Message) (interpreter.Activation, error) {
|
||||
if ctx == nil || !ctx.ProtoReflect().IsValid() {
|
||||
return interpreter.EmptyActivation(), nil
|
||||
}
|
||||
reg, err := types.NewRegistry(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbRef := ctx.ProtoReflect()
|
||||
typeName := string(pbRef.Descriptor().FullName())
|
||||
fields := pbRef.Descriptor().Fields()
|
||||
vars := make(map[string]any, fields.Len())
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
field := fields.Get(i)
|
||||
sft, found := reg.FindStructFieldType(typeName, field.TextName())
|
||||
if !found {
|
||||
return nil, fmt.Errorf("no such field: %s", field.TextName())
|
||||
}
|
||||
fieldVal, err := sft.GetFrom(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vars[field.TextName()] = fieldVal
|
||||
}
|
||||
return interpreter.NewActivation(vars)
|
||||
}
|
||||
|
||||
// EnableMacroCallTracking ensures that call expressions which are replaced by macros
|
||||
// are tracked in the `SourceInfo` of parsed and checked expressions.
|
||||
func EnableMacroCallTracking() EnvOption {
|
||||
@ -545,13 +621,6 @@ func DefaultUTCTimeZone(enabled bool) EnvOption {
|
||||
return features(featureDefaultUTCTimeZone, enabled)
|
||||
}
|
||||
|
||||
// OptionalTypes enable support for optional syntax and types in CEL. The optional value type makes
|
||||
// it possible to express whether variables have been provided, whether a result has been computed,
|
||||
// and in the future whether an object field path, map key value, or list index has a value.
|
||||
func OptionalTypes() EnvOption {
|
||||
return Lib(optionalLibrary{})
|
||||
}
|
||||
|
||||
// features sets the given feature flags. See list of Feature constants above.
|
||||
func features(flag int, enabled bool) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
@ -577,3 +646,14 @@ func ParserExpressionSizeLimit(limit int) EnvOption {
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
func maybeInteropProvider(provider any) (types.Provider, error) {
|
||||
switch p := provider.(type) {
|
||||
case types.Provider:
|
||||
return p, nil
|
||||
case ref.TypeProvider:
|
||||
return &interopCELTypeProvider{TypeProvider: p}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type provider: %T", provider)
|
||||
}
|
||||
}
|
||||
|
69
vendor/github.com/google/cel-go/cel/program.go
generated
vendored
69
vendor/github.com/google/cel-go/cel/program.go
generated
vendored
@ -19,11 +19,10 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
celast "github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
"github.com/google/cel-go/interpreter"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
)
|
||||
|
||||
// Program is an evaluable view of an Ast.
|
||||
@ -62,6 +61,9 @@ func NoVars() interpreter.Activation {
|
||||
// PartialVars returns a PartialActivation which contains variables and a set of AttributePattern
|
||||
// values that indicate variables or parts of variables whose value are not yet known.
|
||||
//
|
||||
// This method relies on manually configured sets of missing attribute patterns. For a method which
|
||||
// infers the missing variables from the input and the configured environment, use Env.PartialVars().
|
||||
//
|
||||
// The `vars` value may either be an interpreter.Activation or any valid input to the
|
||||
// interpreter.NewActivation call.
|
||||
func PartialVars(vars any,
|
||||
@ -104,7 +106,7 @@ func (ed *EvalDetails) State() interpreter.EvalState {
|
||||
// ActualCost returns the tracked cost through the course of execution when `CostTracking` is enabled.
|
||||
// Otherwise, returns nil if the cost was not enabled.
|
||||
func (ed *EvalDetails) ActualCost() *uint64 {
|
||||
if ed.costTracker == nil {
|
||||
if ed == nil || ed.costTracker == nil {
|
||||
return nil
|
||||
}
|
||||
cost := ed.costTracker.ActualCost()
|
||||
@ -128,10 +130,14 @@ type prog struct {
|
||||
// Interpretable configured from an Ast and aggregate decorator set based on program options.
|
||||
interpretable interpreter.Interpretable
|
||||
callCostEstimator interpreter.ActualCostEstimator
|
||||
costOptions []interpreter.CostTrackerOption
|
||||
costLimit *uint64
|
||||
}
|
||||
|
||||
func (p *prog) clone() *prog {
|
||||
costOptsCopy := make([]interpreter.CostTrackerOption, len(p.costOptions))
|
||||
copy(costOptsCopy, p.costOptions)
|
||||
|
||||
return &prog{
|
||||
Env: p.Env,
|
||||
evalOpts: p.evalOpts,
|
||||
@ -153,9 +159,10 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
// Ensure the default attribute factory is set after the adapter and provider are
|
||||
// configured.
|
||||
p := &prog{
|
||||
Env: e,
|
||||
decorators: []interpreter.InterpretableDecorator{},
|
||||
dispatcher: disp,
|
||||
Env: e,
|
||||
decorators: []interpreter.InterpretableDecorator{},
|
||||
dispatcher: disp,
|
||||
costOptions: []interpreter.CostTrackerOption{},
|
||||
}
|
||||
|
||||
// Configure the program via the ProgramOption values.
|
||||
@ -169,7 +176,7 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
|
||||
// Add the function bindings created via Function() options.
|
||||
for _, fn := range e.functions {
|
||||
bindings, err := fn.bindings()
|
||||
bindings, err := fn.Bindings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -208,14 +215,11 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
}
|
||||
// Enable compile-time checking of syntax/cardinality for string.format calls.
|
||||
if p.evalOpts&OptCheckStringFormat == OptCheckStringFormat {
|
||||
var isValidType func(id int64, validTypes ...*types.TypeValue) (bool, error)
|
||||
var isValidType func(id int64, validTypes ...ref.Type) (bool, error)
|
||||
if ast.IsChecked() {
|
||||
isValidType = func(id int64, validTypes ...*types.TypeValue) (bool, error) {
|
||||
t, err := ExprTypeToType(ast.typeMap[id])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if t.kind == DynKind {
|
||||
isValidType = func(id int64, validTypes ...ref.Type) (bool, error) {
|
||||
t := ast.typeMap[id]
|
||||
if t.Kind() == DynKind {
|
||||
return true, nil
|
||||
}
|
||||
for _, vt := range validTypes {
|
||||
@ -223,7 +227,7 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if k == t.kind {
|
||||
if t.Kind() == k {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@ -231,7 +235,7 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
}
|
||||
} else {
|
||||
// if the AST isn't type-checked, short-circuit validation
|
||||
isValidType = func(id int64, validTypes ...*types.TypeValue) (bool, error) {
|
||||
isValidType = func(id int64, validTypes ...ref.Type) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@ -243,6 +247,12 @@ func newProgram(e *Env, ast *Ast, opts []ProgramOption) (Program, error) {
|
||||
factory := func(state interpreter.EvalState, costTracker *interpreter.CostTracker) (Program, error) {
|
||||
costTracker.Estimator = p.callCostEstimator
|
||||
costTracker.Limit = p.costLimit
|
||||
for _, costOpt := range p.costOptions {
|
||||
err := costOpt(costTracker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Limit capacity to guarantee a reallocation when calling 'append(decs, ...)' below. This
|
||||
// prevents the underlying memory from being shared between factory function calls causing
|
||||
// undesired mutations.
|
||||
@ -284,10 +294,11 @@ func (p *prog) initInterpretable(ast *Ast, decs []interpreter.InterpretableDecor
|
||||
}
|
||||
|
||||
// When the AST has been checked it contains metadata that can be used to speed up program execution.
|
||||
var checked *exprpb.CheckedExpr
|
||||
checked, err := AstToCheckedExpr(ast)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
checked := &celast.CheckedAST{
|
||||
Expr: ast.Expr(),
|
||||
SourceInfo: ast.SourceInfo(),
|
||||
TypeMap: ast.typeMap,
|
||||
ReferenceMap: ast.refMap,
|
||||
}
|
||||
interpretable, err := p.interpreter.NewInterpretable(checked, decs...)
|
||||
if err != nil {
|
||||
@ -371,7 +382,11 @@ type progGen struct {
|
||||
// the test is successful.
|
||||
func newProgGen(factory progFactory) (Program, error) {
|
||||
// Test the factory to make sure that configuration errors are spotted at config
|
||||
_, err := factory(interpreter.NewEvalState(), &interpreter.CostTracker{})
|
||||
tracker, err := interpreter.NewCostTracker(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = factory(interpreter.NewEvalState(), tracker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -384,7 +399,10 @@ func (gen *progGen) Eval(input any) (ref.Val, *EvalDetails, error) {
|
||||
// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
|
||||
// results.
|
||||
state := interpreter.NewEvalState()
|
||||
costTracker := &interpreter.CostTracker{}
|
||||
costTracker, err := interpreter.NewCostTracker(nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
det := &EvalDetails{state: state, costTracker: costTracker}
|
||||
|
||||
// Generate a new instance of the interpretable using the factory configured during the call to
|
||||
@ -412,7 +430,10 @@ func (gen *progGen) ContextEval(ctx context.Context, input any) (ref.Val, *EvalD
|
||||
// new EvalState instance for each call to ensure that unique evaluations yield unique stateful
|
||||
// results.
|
||||
state := interpreter.NewEvalState()
|
||||
costTracker := &interpreter.CostTracker{}
|
||||
costTracker, err := interpreter.NewCostTracker(nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
det := &EvalDetails{state: state, costTracker: costTracker}
|
||||
|
||||
// Generate a new instance of the interpretable using the factory configured during the call to
|
||||
@ -498,7 +519,7 @@ type evalActivation struct {
|
||||
// The lazy binding will only be invoked once per evaluation.
|
||||
//
|
||||
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
|
||||
// the ref.TypeAdapter configured in the environment.
|
||||
// the types.Adapter configured in the environment.
|
||||
func (a *evalActivation) ResolveName(name string) (any, bool) {
|
||||
v, found := a.vars[name]
|
||||
if !found {
|
||||
|
388
vendor/github.com/google/cel-go/cel/validator.go
generated
vendored
Normal file
388
vendor/github.com/google/cel-go/cel/validator.go
generated
vendored
Normal file
@ -0,0 +1,388 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"github.com/google/cel-go/common/ast"
|
||||
"github.com/google/cel-go/common/overloads"
|
||||
|
||||
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
homogeneousValidatorName = "cel.lib.std.validate.types.homogeneous"
|
||||
|
||||
// HomogeneousAggregateLiteralExemptFunctions is the ValidatorConfig key used to configure
|
||||
// the set of function names which are exempt from homogeneous type checks. The expected type
|
||||
// is a string list of function names.
|
||||
//
|
||||
// As an example, the `<string>.format([args])` call expects the input arguments list to be
|
||||
// comprised of a variety of types which correspond to the types expected by the format control
|
||||
// clauses; however, all other uses of a mixed element type list, would be unexpected.
|
||||
HomogeneousAggregateLiteralExemptFunctions = homogeneousValidatorName + ".exempt"
|
||||
)
|
||||
|
||||
// ASTValidators configures a set of ASTValidator instances into the target environment.
|
||||
//
|
||||
// Validators are applied in the order in which the are specified and are treated as singletons.
|
||||
// The same ASTValidator with a given name will not be applied more than once.
|
||||
func ASTValidators(validators ...ASTValidator) EnvOption {
|
||||
return func(e *Env) (*Env, error) {
|
||||
for _, v := range validators {
|
||||
if !e.HasValidator(v.Name()) {
|
||||
e.validators = append(e.validators, v)
|
||||
}
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ASTValidator defines a singleton interface for validating a type-checked Ast against an environment.
|
||||
//
|
||||
// Note: the Issues argument is mutable in the sense that it is intended to collect errors which will be
|
||||
// reported to the caller.
|
||||
type ASTValidator interface {
|
||||
// Name returns the name of the validator. Names must be unique.
|
||||
Name() string
|
||||
|
||||
// Validate validates a given Ast within an Environment and collects a set of potential issues.
|
||||
//
|
||||
// The ValidatorConfig is generated from the set of ASTValidatorConfigurer instances prior to
|
||||
// the invocation of the Validate call. The expectation is that the validator configuration
|
||||
// is created in sequence and immutable once provided to the Validate call.
|
||||
//
|
||||
// See individual validators for more information on their configuration keys and configuration
|
||||
// properties.
|
||||
Validate(*Env, ValidatorConfig, *ast.CheckedAST, *Issues)
|
||||
}
|
||||
|
||||
// ValidatorConfig provides an accessor method for querying validator configuration state.
|
||||
type ValidatorConfig interface {
|
||||
GetOrDefault(name string, value any) any
|
||||
}
|
||||
|
||||
// MutableValidatorConfig provides mutation methods for querying and updating validator configuration
|
||||
// settings.
|
||||
type MutableValidatorConfig interface {
|
||||
ValidatorConfig
|
||||
Set(name string, value any) error
|
||||
}
|
||||
|
||||
// ASTValidatorConfigurer indicates that this object, currently expected to be an ASTValidator,
|
||||
// participates in validator configuration settings.
|
||||
//
|
||||
// This interface may be split from the expectation of being an ASTValidator instance in the future.
|
||||
type ASTValidatorConfigurer interface {
|
||||
Configure(MutableValidatorConfig) error
|
||||
}
|
||||
|
||||
// validatorConfig implements the ValidatorConfig and MutableValidatorConfig interfaces.
|
||||
type validatorConfig struct {
|
||||
data map[string]any
|
||||
}
|
||||
|
||||
// newValidatorConfig initializes the validator config with default values for core CEL validators.
|
||||
func newValidatorConfig() *validatorConfig {
|
||||
return &validatorConfig{
|
||||
data: map[string]any{
|
||||
HomogeneousAggregateLiteralExemptFunctions: []string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrDefault returns the configured value for the name, if present, else the input default value.
|
||||
//
|
||||
// Note, the type-agreement between the input default and configured value is not checked on read.
|
||||
func (config *validatorConfig) GetOrDefault(name string, value any) any {
|
||||
v, found := config.data[name]
|
||||
if !found {
|
||||
return value
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Set configures a validator option with the given name and value.
|
||||
//
|
||||
// If the value had previously been set, the new value must have the same reflection type as the old one,
|
||||
// or the call will error.
|
||||
func (config *validatorConfig) Set(name string, value any) error {
|
||||
v, found := config.data[name]
|
||||
if found && reflect.TypeOf(v) != reflect.TypeOf(value) {
|
||||
return fmt.Errorf("incompatible configuration type for %s, got %T, wanted %T", name, value, v)
|
||||
}
|
||||
config.data[name] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtendedValidations collects a set of common AST validations which reduce the likelihood of runtime errors.
|
||||
//
|
||||
// - Validate duration and timestamp literals
|
||||
// - Ensure regex strings are valid
|
||||
// - Disable mixed type list and map literals
|
||||
func ExtendedValidations() EnvOption {
|
||||
return ASTValidators(
|
||||
ValidateDurationLiterals(),
|
||||
ValidateTimestampLiterals(),
|
||||
ValidateRegexLiterals(),
|
||||
ValidateHomogeneousAggregateLiterals(),
|
||||
)
|
||||
}
|
||||
|
||||
// ValidateDurationLiterals ensures that duration literal arguments are valid immediately after type-check.
|
||||
func ValidateDurationLiterals() ASTValidator {
|
||||
return newFormatValidator(overloads.TypeConvertDuration, 0, evalCall)
|
||||
}
|
||||
|
||||
// ValidateTimestampLiterals ensures that timestamp literal arguments are valid immediately after type-check.
|
||||
func ValidateTimestampLiterals() ASTValidator {
|
||||
return newFormatValidator(overloads.TypeConvertTimestamp, 0, evalCall)
|
||||
}
|
||||
|
||||
// ValidateRegexLiterals ensures that regex patterns are validated after type-check.
|
||||
func ValidateRegexLiterals() ASTValidator {
|
||||
return newFormatValidator(overloads.Matches, 0, compileRegex)
|
||||
}
|
||||
|
||||
// ValidateHomogeneousAggregateLiterals checks that all list and map literals entries have the same types, i.e.
|
||||
// no mixed list element types or mixed map key or map value types.
|
||||
//
|
||||
// Note: the string format call relies on a mixed element type list for ease of use, so this check skips all
|
||||
// literals which occur within string format calls.
|
||||
func ValidateHomogeneousAggregateLiterals() ASTValidator {
|
||||
return homogeneousAggregateLiteralValidator{}
|
||||
}
|
||||
|
||||
// ValidateComprehensionNestingLimit ensures that comprehension nesting does not exceed the specified limit.
|
||||
//
|
||||
// This validator can be useful for preventing arbitrarily nested comprehensions which can take high polynomial
|
||||
// time to complete.
|
||||
//
|
||||
// Note, this limit does not apply to comprehensions with an empty iteration range, as these comprehensions have
|
||||
// no actual looping cost. The cel.bind() utilizes the comprehension structure to perform local variable
|
||||
// assignments and supplies an empty iteration range, so they won't count against the nesting limit either.
|
||||
func ValidateComprehensionNestingLimit(limit int) ASTValidator {
|
||||
return nestingLimitValidator{limit: limit}
|
||||
}
|
||||
|
||||
type argChecker func(env *Env, call, arg ast.NavigableExpr) error
|
||||
|
||||
func newFormatValidator(funcName string, argNum int, check argChecker) formatValidator {
|
||||
return formatValidator{
|
||||
funcName: funcName,
|
||||
check: check,
|
||||
argNum: argNum,
|
||||
}
|
||||
}
|
||||
|
||||
type formatValidator struct {
|
||||
funcName string
|
||||
argNum int
|
||||
check argChecker
|
||||
}
|
||||
|
||||
// Name returns the unique name of this function format validator.
|
||||
func (v formatValidator) Name() string {
|
||||
return fmt.Sprintf("cel.lib.std.validate.functions.%s", v.funcName)
|
||||
}
|
||||
|
||||
// Validate searches the AST for uses of a given function name with a constant argument and performs a check
|
||||
// on whether the argument is a valid literal value.
|
||||
func (v formatValidator) Validate(e *Env, _ ValidatorConfig, a *ast.CheckedAST, iss *Issues) {
|
||||
root := ast.NavigateCheckedAST(a)
|
||||
funcCalls := ast.MatchDescendants(root, ast.FunctionMatcher(v.funcName))
|
||||
for _, call := range funcCalls {
|
||||
callArgs := call.AsCall().Args()
|
||||
if len(callArgs) <= v.argNum {
|
||||
continue
|
||||
}
|
||||
litArg := callArgs[v.argNum]
|
||||
if litArg.Kind() != ast.LiteralKind {
|
||||
continue
|
||||
}
|
||||
if err := v.check(e, call, litArg); err != nil {
|
||||
iss.ReportErrorAtID(litArg.ID(), "invalid %s argument", v.funcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func evalCall(env *Env, call, arg ast.NavigableExpr) error {
|
||||
ast := ParsedExprToAst(&exprpb.ParsedExpr{Expr: call.ToExpr()})
|
||||
prg, err := env.Program(ast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err = prg.Eval(NoVars())
|
||||
return err
|
||||
}
|
||||
|
||||
func compileRegex(_ *Env, _, arg ast.NavigableExpr) error {
|
||||
pattern := arg.AsLiteral().Value().(string)
|
||||
_, err := regexp.Compile(pattern)
|
||||
return err
|
||||
}
|
||||
|
||||
type homogeneousAggregateLiteralValidator struct{}
|
||||
|
||||
// Name returns the unique name of the homogeneous type validator.
|
||||
func (homogeneousAggregateLiteralValidator) Name() string {
|
||||
return homogeneousValidatorName
|
||||
}
|
||||
|
||||
// Configure implements the ASTValidatorConfigurer interface and currently sets the list of standard
|
||||
// and exempt functions from homogeneous aggregate literal checks.
|
||||
//
|
||||
// TODO: Move this call into the string.format() ASTValidator once ported.
|
||||
func (homogeneousAggregateLiteralValidator) Configure(c MutableValidatorConfig) error {
|
||||
emptyList := []string{}
|
||||
exemptFunctions := c.GetOrDefault(HomogeneousAggregateLiteralExemptFunctions, emptyList).([]string)
|
||||
exemptFunctions = append(exemptFunctions, "format")
|
||||
return c.Set(HomogeneousAggregateLiteralExemptFunctions, exemptFunctions)
|
||||
}
|
||||
|
||||
// Validate validates that all lists and map literals have homogeneous types, i.e. don't contain dyn types.
|
||||
//
|
||||
// This validator makes an exception for list and map literals which occur at any level of nesting within
|
||||
// string format calls.
|
||||
func (v homogeneousAggregateLiteralValidator) Validate(_ *Env, c ValidatorConfig, a *ast.CheckedAST, iss *Issues) {
|
||||
var exemptedFunctions []string
|
||||
exemptedFunctions = c.GetOrDefault(HomogeneousAggregateLiteralExemptFunctions, exemptedFunctions).([]string)
|
||||
root := ast.NavigateCheckedAST(a)
|
||||
listExprs := ast.MatchDescendants(root, ast.KindMatcher(ast.ListKind))
|
||||
for _, listExpr := range listExprs {
|
||||
if inExemptFunction(listExpr, exemptedFunctions) {
|
||||
continue
|
||||
}
|
||||
l := listExpr.AsList()
|
||||
elements := l.Elements()
|
||||
optIndices := l.OptionalIndices()
|
||||
var elemType *Type
|
||||
for i, e := range elements {
|
||||
et := e.Type()
|
||||
if isOptionalIndex(i, optIndices) {
|
||||
et = et.Parameters()[0]
|
||||
}
|
||||
if elemType == nil {
|
||||
elemType = et
|
||||
continue
|
||||
}
|
||||
if !elemType.IsEquivalentType(et) {
|
||||
v.typeMismatch(iss, e.ID(), elemType, et)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
mapExprs := ast.MatchDescendants(root, ast.KindMatcher(ast.MapKind))
|
||||
for _, mapExpr := range mapExprs {
|
||||
if inExemptFunction(mapExpr, exemptedFunctions) {
|
||||
continue
|
||||
}
|
||||
m := mapExpr.AsMap()
|
||||
entries := m.Entries()
|
||||
var keyType, valType *Type
|
||||
for _, e := range entries {
|
||||
key, val := e.Key(), e.Value()
|
||||
kt, vt := key.Type(), val.Type()
|
||||
if e.IsOptional() {
|
||||
vt = vt.Parameters()[0]
|
||||
}
|
||||
if keyType == nil && valType == nil {
|
||||
keyType, valType = kt, vt
|
||||
continue
|
||||
}
|
||||
if !keyType.IsEquivalentType(kt) {
|
||||
v.typeMismatch(iss, key.ID(), keyType, kt)
|
||||
}
|
||||
if !valType.IsEquivalentType(vt) {
|
||||
v.typeMismatch(iss, val.ID(), valType, vt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inExemptFunction(e ast.NavigableExpr, exemptFunctions []string) bool {
|
||||
if parent, found := e.Parent(); found {
|
||||
if parent.Kind() == ast.CallKind {
|
||||
fnName := parent.AsCall().FunctionName()
|
||||
for _, exempt := range exemptFunctions {
|
||||
if exempt == fnName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if parent.Kind() == ast.ListKind || parent.Kind() == ast.MapKind {
|
||||
return inExemptFunction(parent, exemptFunctions)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isOptionalIndex(i int, optIndices []int32) bool {
|
||||
for _, optInd := range optIndices {
|
||||
if i == int(optInd) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (homogeneousAggregateLiteralValidator) typeMismatch(iss *Issues, id int64, expected, actual *Type) {
|
||||
iss.ReportErrorAtID(id, "expected type '%s' but found '%s'", FormatCELType(expected), FormatCELType(actual))
|
||||
}
|
||||
|
||||
type nestingLimitValidator struct {
|
||||
limit int
|
||||
}
|
||||
|
||||
func (v nestingLimitValidator) Name() string {
|
||||
return "cel.lib.std.validate.comprehension_nesting_limit"
|
||||
}
|
||||
|
||||
func (v nestingLimitValidator) Validate(e *Env, _ ValidatorConfig, a *ast.CheckedAST, iss *Issues) {
|
||||
root := ast.NavigateCheckedAST(a)
|
||||
comprehensions := ast.MatchDescendants(root, ast.KindMatcher(ast.ComprehensionKind))
|
||||
if len(comprehensions) <= v.limit {
|
||||
return
|
||||
}
|
||||
for _, comp := range comprehensions {
|
||||
count := 0
|
||||
e := comp
|
||||
hasParent := true
|
||||
for hasParent {
|
||||
// When the expression is not a comprehension, continue to the next ancestor.
|
||||
if e.Kind() != ast.ComprehensionKind {
|
||||
e, hasParent = e.Parent()
|
||||
continue
|
||||
}
|
||||
// When the comprehension has an empty range, continue to the next ancestor
|
||||
// as this comprehension does not have any associated cost.
|
||||
iterRange := e.AsComprehension().IterRange()
|
||||
if iterRange.Kind() == ast.ListKind && iterRange.AsList().Size() == 0 {
|
||||
e, hasParent = e.Parent()
|
||||
continue
|
||||
}
|
||||
// Otherwise check the nesting limit.
|
||||
count++
|
||||
if count > v.limit {
|
||||
iss.ReportErrorAtID(comp.ID(), "comprehension exceeds nesting limit")
|
||||
break
|
||||
}
|
||||
e, hasParent = e.Parent()
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user