2020#include < vector>
2121
2222#include " absl/functional/function_ref.h"
23- #include " absl/status/status .h"
23+ #include " absl/log/absl_check .h"
2424#include " absl/status/statusor.h"
2525#include " absl/strings/match.h"
2626#include " absl/strings/str_cat.h"
2727#include " absl/strings/str_join.h"
2828#include " absl/strings/str_split.h"
2929#include " absl/strings/string_view.h"
3030#include " absl/types/span.h"
31+ #include " common/container.h"
3132#include " internal/lexis.h"
3233
3334namespace cel ::checker_internal {
3435namespace {
3536
36- bool FieldSelectInterpretationCandidates (
37+ bool FieldSelectInterpretationCandidatesImpl (
3738 absl::string_view prefix,
38- absl::Span<const std::string> partly_qualified_name,
39+ absl::Span<const std::string> partly_qualified_name, bool prefix_is_alias,
3940 absl::FunctionRef<bool (absl::string_view, int )> callback) {
4041 for (int i = 0 ; i < partly_qualified_name.size (); ++i) {
4142 std::string buf;
4243 int count = partly_qualified_name.size () - i;
43- auto end_idx = count - 1 ;
44+ auto end_idx = count - (prefix_is_alias ? 0 : 1 ) ;
4445 auto ident = absl::StrJoin (partly_qualified_name.subspan (0 , count), " ." );
4546 absl::string_view candidate = ident;
4647 if (absl::StartsWith (candidate, " ." )) {
@@ -54,28 +55,44 @@ bool FieldSelectInterpretationCandidates(
5455 return false ;
5556 }
5657 }
58+ if (prefix_is_alias) {
59+ return callback (prefix, 0 );
60+ }
5761 return true ;
5862}
5963
64+ bool FieldSelectInterpretationCandidates (
65+ absl::string_view prefix,
66+ absl::Span<const std::string> partly_qualified_name,
67+ absl::FunctionRef<bool (absl::string_view, int )> callback) {
68+ return FieldSelectInterpretationCandidatesImpl (
69+ prefix, partly_qualified_name, /* prefix_is_alias=*/ false , callback);
70+ }
71+
72+ bool FieldSelectInterpretationCandidatesWithAlias (
73+ absl::string_view prefix,
74+ absl::Span<const std::string> partly_qualified_name,
75+ absl::FunctionRef<bool (absl::string_view, int )> callback) {
76+ return FieldSelectInterpretationCandidatesImpl (
77+ prefix, partly_qualified_name, /* prefix_is_alias=*/ true , callback);
78+ }
79+
6080} // namespace
6181
6282absl::StatusOr<NamespaceGenerator> NamespaceGenerator::Create (
63- absl::string_view container ) {
83+ const ExpressionContainer& expression_container ) {
6484 std::vector<std::string> candidates;
6585
86+ absl::string_view container = expression_container.container ();
6687 if (container.empty ()) {
67- return NamespaceGenerator (std::move (candidates));
88+ return NamespaceGenerator (&expression_container, std::move (candidates));
6889 }
6990
70- if (absl::StartsWith (container, " ." )) {
71- return absl::InvalidArgumentError (" container must not start with a '.'" );
72- }
7391 std::string prefix;
7492 for (auto segment : absl::StrSplit (container, ' .' )) {
75- if (!internal::LexisIsIdentifier (segment)) {
76- return absl::InvalidArgumentError (
77- " container must only contain valid identifier segments" );
78- }
93+ // Assumes the the ExpressionContainer has already validated the container
94+ // and aliases.
95+ ABSL_DCHECK (internal::LexisIsIdentifier (segment));
7996 if (prefix.empty ()) {
8097 prefix = segment;
8198 } else {
@@ -84,31 +101,75 @@ absl::StatusOr<NamespaceGenerator> NamespaceGenerator::Create(
84101 candidates.push_back (prefix);
85102 }
86103 std::reverse (candidates.begin (), candidates.end ());
87- return NamespaceGenerator (std::move (candidates));
104+ return NamespaceGenerator (&expression_container, std::move (candidates));
88105}
89106
90107void NamespaceGenerator::GenerateCandidates (
91- absl::string_view unqualified_name,
92- absl::FunctionRef<bool (absl::string_view)> callback) {
93- if (absl::StartsWith (unqualified_name, " ." )) {
94- callback (unqualified_name.substr (1 ));
108+ absl::string_view simple_name,
109+ absl::FunctionRef<bool (absl::string_view)> callback) const {
110+ // Special case for root-relative names. Aliases still apply first.
111+ bool is_root_relative = absl::StartsWith (simple_name, " ." );
112+ if (is_root_relative) {
113+ simple_name = simple_name.substr (1 );
114+ }
115+
116+ // The name is unqualified, but may include a namespace (struct creation).
117+ // This is just a quirk of the parser.
118+ if (auto dot_pos = simple_name.find (' .' );
119+ dot_pos != absl::string_view::npos) {
120+ absl::string_view first_segment = simple_name.substr (0 , dot_pos);
121+ absl::string_view rest = simple_name.substr (dot_pos + 1 );
122+ if (auto resolved_alias = expression_container_->FindAlias (first_segment);
123+ !resolved_alias.empty ()) {
124+ callback (absl::StrCat (resolved_alias, " ." , rest));
125+ return ;
126+ }
127+ } else {
128+ if (auto resolved_alias = expression_container_->FindAlias (simple_name);
129+ !resolved_alias.empty ()) {
130+ callback (resolved_alias);
131+ return ;
132+ }
133+ }
134+
135+ if (is_root_relative) {
136+ callback (simple_name);
95137 return ;
96138 }
139+
97140 for (const auto & prefix : candidates_) {
98- std::string candidate = absl::StrCat (prefix, " ." , unqualified_name );
141+ std::string candidate = absl::StrCat (prefix, " ." , simple_name );
99142 if (!callback (candidate)) {
100143 return ;
101144 }
102145 }
103- callback (unqualified_name );
146+ callback (simple_name );
104147}
105148
106149void NamespaceGenerator::GenerateCandidates (
107150 absl::Span<const std::string> partly_qualified_name,
108- absl::FunctionRef<bool (absl::string_view, int )> callback) {
109- // Special case for explicit root relative name. e.g. '.com.example.Foo'
110- if (!partly_qualified_name.empty () &&
111- absl::StartsWith (partly_qualified_name[0 ], " ." )) {
151+ absl::FunctionRef<bool (absl::string_view, int )> callback) const {
152+ if (partly_qualified_name.empty ()) {
153+ return ;
154+ }
155+
156+ // Special case for root-relative names. Aliases still apply first.
157+ absl::string_view first_segment = partly_qualified_name[0 ];
158+ bool is_root_relative = absl::StartsWith (first_segment, " ." );
159+ if (is_root_relative) {
160+ first_segment = first_segment.substr (1 );
161+ }
162+
163+ if (auto resolved_alias = expression_container_->FindAlias (first_segment);
164+ !resolved_alias.empty ()) {
165+ FieldSelectInterpretationCandidatesWithAlias (
166+ resolved_alias, partly_qualified_name.subspan (1 ), callback);
167+ // If the alias matches, we don't check the container even if name
168+ // resolution fails.
169+ return ;
170+ }
171+
172+ if (is_root_relative) {
112173 FieldSelectInterpretationCandidates (" " , partly_qualified_name, callback);
113174 return ;
114175 }
0 commit comments