@@ -19,6 +19,8 @@ package checker
1919import (
2020 "fmt"
2121 "reflect"
22+ "slices"
23+ "strings"
2224
2325 "github.com/google/cel-go/common"
2426 "github.com/google/cel-go/common/ast"
@@ -104,11 +106,15 @@ func (c *checker) check(e ast.Expr) {
104106func (c * checker ) checkIdent (e ast.Expr ) {
105107 identName := e .AsIdent ()
106108 // Check to see if the identifier is declared.
107- if ident := c .env .LookupIdent (identName ); ident != nil {
109+ if ident := c .env .resolveSimpleIdent (identName ); ident != nil {
110+ name := strings .TrimPrefix (ident .Name (), "." )
111+ if ident .requiresDisambiguation {
112+ name = "." + name
113+ }
108114 c .setType (e , ident .Type ())
109- c .setReference (e , ast .NewIdentReference (ident . Name () , ident .Value ()))
115+ c .setReference (e , ast .NewIdentReference (name , ident .Value ()))
110116 // Overwrite the identifier with its fully qualified name.
111- e .SetKindCase (c .NewIdent (e .ID (), ident . Name () ))
117+ e .SetKindCase (c .NewIdent (e .ID (), name ))
112118 return
113119 }
114120
@@ -119,18 +125,22 @@ func (c *checker) checkIdent(e ast.Expr) {
119125func (c * checker ) checkSelect (e ast.Expr ) {
120126 sel := e .AsSelect ()
121127 // Before traversing down the tree, try to interpret as qualified name.
122- qname , found := containers . ToQualifiedName (e )
128+ qualifiers , found := c . computeQualifiers (e )
123129 if found {
124- ident := c .env .LookupIdent ( qname )
130+ ident := c .env .resolveQualifiedIdent ( qualifiers ... )
125131 if ident != nil {
126132 // We don't check for a TestOnly expression here since the `found` result is
127133 // always going to be false for TestOnly expressions.
128134
129135 // Rewrite the node to be a variable reference to the resolved fully-qualified
130136 // variable name.
137+ name := ident .Name ()
138+ if ident .requiresDisambiguation {
139+ name = "." + name
140+ }
131141 c .setType (e , ident .Type ())
132- c .setReference (e , ast .NewIdentReference (ident . Name () , ident .Value ()))
133- e .SetKindCase (c .NewIdent (e .ID (), ident . Name () ))
142+ c .setReference (e , ast .NewIdentReference (name , ident .Value ()))
143+ e .SetKindCase (c .NewIdent (e .ID (), name ))
134144 return
135145 }
136146 }
@@ -142,6 +152,29 @@ func (c *checker) checkSelect(e ast.Expr) {
142152 c .setType (e , substitute (c .mappings , resultType , false ))
143153}
144154
155+ // computeQualifiers computes the qualified names parts of a select expression.
156+ func (c * checker ) computeQualifiers (e ast.Expr ) ([]string , bool ) {
157+ var qualifiers []string
158+ for e .Kind () == ast .SelectKind {
159+ sel := e .AsSelect ()
160+ // test only expressions are not considered for qualified name selection.
161+ if sel .IsTestOnly () {
162+ return qualifiers , false
163+ }
164+ // otherwise append the select field name to the qualifier list (reverse order)
165+ qualifiers = append (qualifiers , sel .FieldName ())
166+ e = sel .Operand ()
167+ // If the next operand is an identifier, then append it, reverse the name sequence
168+ // and return it to the caller.s
169+ if e .Kind () == ast .IdentKind {
170+ qualifiers = append (qualifiers , e .AsIdent ())
171+ slices .Reverse (qualifiers )
172+ return qualifiers , true
173+ }
174+ }
175+ return qualifiers , false
176+ }
177+
145178func (c * checker ) checkOptSelect (e ast.Expr ) {
146179 // Collect metadata related to the opt select call packaged by the parser.
147180 call := e .AsCall ()
@@ -234,7 +267,7 @@ func (c *checker) checkCall(e ast.Expr) {
234267 // Regular static call with simple name.
235268 if ! call .IsMemberFunction () {
236269 // Check for the existence of the function.
237- fn := c .env .LookupFunction (fnName )
270+ fn := c .env .lookupFunction (fnName )
238271 if fn == nil {
239272 c .errors .undeclaredReference (e .ID (), c .location (e ), c .env .container .Name (), fnName )
240273 c .setType (e , types .ErrorType )
@@ -256,7 +289,7 @@ func (c *checker) checkCall(e ast.Expr) {
256289 qualifiedPrefix , maybeQualified := containers .ToQualifiedName (target )
257290 if maybeQualified {
258291 maybeQualifiedName := qualifiedPrefix + "." + fnName
259- fn := c .env .LookupFunction (maybeQualifiedName )
292+ fn := c .env .lookupFunction (maybeQualifiedName )
260293 if fn != nil {
261294 // The function name is namespaced and so preserving the target operand would
262295 // be an inaccurate representation of the desired evaluation behavior.
@@ -269,7 +302,7 @@ func (c *checker) checkCall(e ast.Expr) {
269302
270303 // Regular instance call.
271304 c .check (target )
272- fn := c .env .LookupFunction (fnName )
305+ fn := c .env .lookupFunction (fnName )
273306 // Function found, attempt overload resolution.
274307 if fn != nil {
275308 c .resolveOverloadOrError (e , fn , target , args )
@@ -441,7 +474,7 @@ func (c *checker) checkCreateStruct(e ast.Expr) {
441474 msgVal := e .AsStruct ()
442475 // Determine the type of the message.
443476 resultType := types .ErrorType
444- ident := c .env .LookupIdent (msgVal .TypeName ())
477+ ident := c .env .resolveTypeIdent (msgVal .TypeName ())
445478 if ident == nil {
446479 c .errors .undeclaredReference (
447480 e .ID (), c .location (e ), c .env .container .Name (), msgVal .TypeName ())
0 commit comments