Skip to content

Commit 0341704

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for resolving aliases.
PiperOrigin-RevId: 904596936
1 parent 4017673 commit 0341704

10 files changed

Lines changed: 482 additions & 91 deletions

checker/internal/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,12 @@ cc_library(
8989
srcs = ["namespace_generator.cc"],
9090
hdrs = ["namespace_generator.h"],
9191
deps = [
92+
"//common:container",
9293
"//internal:lexis",
94+
"@com_google_absl//absl/base:core_headers",
95+
"@com_google_absl//absl/base:nullability",
9396
"@com_google_absl//absl/functional:function_ref",
97+
"@com_google_absl//absl/log:absl_check",
9498
"@com_google_absl//absl/status",
9599
"@com_google_absl//absl/status:statusor",
96100
"@com_google_absl//absl/strings",
@@ -104,6 +108,7 @@ cc_test(
104108
srcs = ["namespace_generator_test.cc"],
105109
deps = [
106110
":namespace_generator",
111+
"//common:container",
107112
"//internal:testing",
108113
"@com_google_absl//absl/status",
109114
"@com_google_absl//absl/strings:string_view",

checker/internal/namespace_generator.cc

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,28 @@
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

3334
namespace cel::checker_internal {
3435
namespace {
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

6282
absl::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

90107
void 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

106149
void 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
}

checker/internal/namespace_generator.h

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,26 @@
1919
#include <utility>
2020
#include <vector>
2121

22+
#include "absl/base/attributes.h"
23+
#include "absl/base/nullability.h"
2224
#include "absl/functional/function_ref.h"
2325
#include "absl/status/statusor.h"
2426
#include "absl/strings/string_view.h"
2527
#include "absl/types/span.h"
28+
#include "common/container.h"
2629

2730
namespace cel::checker_internal {
2831

2932
// Utility class for generating namespace qualified candidates for reference
3033
// resolution.
34+
//
35+
// This class is expected to be scoped to a single type checking operation and
36+
// borrows the ExpressionContainer from the TypeCheckEnv.
3137
class NamespaceGenerator {
3238
public:
33-
static absl::StatusOr<NamespaceGenerator> Create(absl::string_view container);
39+
static absl::StatusOr<NamespaceGenerator> Create(
40+
const ExpressionContainer& expression_container
41+
ABSL_ATTRIBUTE_LIFETIME_BOUND);
3442

3543
// Copyable and movable.
3644
NamespaceGenerator(const NamespaceGenerator&) = default;
@@ -51,8 +59,18 @@ class NamespaceGenerator {
5159
// and unqualified name foo
5260
//
5361
// com.google.foo, com.foo, foo
54-
void GenerateCandidates(absl::string_view unqualified_name,
55-
absl::FunctionRef<bool(absl::string_view)> callback);
62+
//
63+
// If aliases are present, they override the normal container resolution.
64+
//
65+
// Example:
66+
// container (com.google)
67+
// alias (foo = com.example)
68+
// unqualified name foo
69+
//
70+
// com.example
71+
void GenerateCandidates(
72+
absl::string_view simple_name,
73+
absl::FunctionRef<bool(absl::string_view)> callback) const;
5674

5775
// For a partially qualified name, generate all the qualified candidates in
5876
// order of resolution precedence and pass them to the provided callback. The
@@ -72,16 +90,30 @@ class NamespaceGenerator {
7290
// (com.Foo).bar, <com.Foo, 0>
7391
// (Foo.bar), <Foo.bar, 1>
7492
// (Foo).bar, <Foo, 0>
93+
//
94+
// If aliases are present, they override the normal container resolution.
95+
//
96+
// Example:
97+
// container (com.google)
98+
// alias (Foo = com.example.Foo)
99+
// partially qualified name Foo.bar
100+
//
101+
// (com.example.Foo.bar), <com.example.Foo.bar, 1>
102+
// (com.example.Foo).bar, <com.example.Foo, 0>
75103
void GenerateCandidates(
76104
absl::Span<const std::string> partly_qualified_name,
77-
absl::FunctionRef<bool(absl::string_view, int)> callback);
105+
absl::FunctionRef<bool(absl::string_view, int)> callback) const;
78106

79107
private:
80-
explicit NamespaceGenerator(std::vector<std::string> candidates)
81-
: candidates_(std::move(candidates)) {}
108+
explicit NamespaceGenerator(
109+
const ExpressionContainer* absl_nonnull expression_container,
110+
std::vector<std::string> candidates)
111+
: candidates_(std::move(candidates)),
112+
expression_container_(expression_container) {}
82113

83114
// list of prefixes ordered from most qualified to least.
84115
std::vector<std::string> candidates_;
116+
const ExpressionContainer* absl_nonnull expression_container_;
85117
};
86118
} // namespace cel::checker_internal
87119

checker/internal/namespace_generator_test.cc

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@
1818
#include <utility>
1919
#include <vector>
2020

21-
#include "absl/status/status.h"
2221
#include "absl/strings/string_view.h"
22+
#include "common/container.h"
2323
#include "internal/testing.h"
2424

2525
namespace cel::checker_internal {
2626
namespace {
2727

28-
using ::absl_testing::StatusIs;
28+
using ::absl_testing::IsOk;
2929
using ::testing::ElementsAre;
3030
using ::testing::Pair;
3131

3232
TEST(NamespaceGeneratorTest, EmptyContainer) {
33-
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(""));
33+
ExpressionContainer container;
34+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
3435
std::vector<std::string> candidates;
3536
generator.GenerateCandidates("foo", [&](absl::string_view candidate) {
3637
candidates.push_back(std::string(candidate));
@@ -40,8 +41,9 @@ TEST(NamespaceGeneratorTest, EmptyContainer) {
4041
}
4142

4243
TEST(NamespaceGeneratorTest, MultipleSegments) {
43-
ASSERT_OK_AND_ASSIGN(auto generator,
44-
NamespaceGenerator::Create("com.example"));
44+
ExpressionContainer container;
45+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
46+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
4547
std::vector<std::string> candidates;
4648
generator.GenerateCandidates("foo", [&](absl::string_view candidate) {
4749
candidates.push_back(std::string(candidate));
@@ -51,8 +53,9 @@ TEST(NamespaceGeneratorTest, MultipleSegments) {
5153
}
5254

5355
TEST(NamespaceGeneratorTest, MultipleSegmentsRootNamespace) {
54-
ASSERT_OK_AND_ASSIGN(auto generator,
55-
NamespaceGenerator::Create("com.example"));
56+
ExpressionContainer container;
57+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
58+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
5659
std::vector<std::string> candidates;
5760
generator.GenerateCandidates(".foo", [&](absl::string_view candidate) {
5861
candidates.push_back(std::string(candidate));
@@ -61,18 +64,46 @@ TEST(NamespaceGeneratorTest, MultipleSegmentsRootNamespace) {
6164
EXPECT_THAT(candidates, ElementsAre("foo"));
6265
}
6366

64-
TEST(NamespaceGeneratorTest, InvalidContainers) {
65-
EXPECT_THAT(NamespaceGenerator::Create(".com.example"),
66-
StatusIs(absl::StatusCode::kInvalidArgument));
67-
EXPECT_THAT(NamespaceGenerator::Create("com..example"),
68-
StatusIs(absl::StatusCode::kInvalidArgument));
69-
EXPECT_THAT(NamespaceGenerator::Create("com.$example"),
70-
StatusIs(absl::StatusCode::kInvalidArgument));
67+
TEST(NamespaceGeneratorTest, MultipleSegmentsSelectInterpretation) {
68+
ExpressionContainer container;
69+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
70+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
71+
std::vector<std::string> qualified_ident = {"foo", "Bar"};
72+
std::vector<std::pair<std::string, int>> candidates;
73+
generator.GenerateCandidates(
74+
qualified_ident, [&](absl::string_view candidate, int segment_index) {
75+
candidates.push_back(std::pair(std::string(candidate), segment_index));
76+
return true;
77+
});
78+
EXPECT_THAT(
79+
candidates,
80+
ElementsAre(Pair("com.example.foo.Bar", 1), Pair("com.example.foo", 0),
81+
Pair("com.foo.Bar", 1), Pair("com.foo", 0),
82+
Pair("foo.Bar", 1), Pair("foo", 0)));
7183
}
7284

73-
TEST(NamespaceGeneratorTest, MultipleSegmentsSelectInterpretation) {
74-
ASSERT_OK_AND_ASSIGN(auto generator,
75-
NamespaceGenerator::Create("com.example"));
85+
TEST(NamespaceGeneratorTest, MultipleSegmentsSelectInterpretationAliasMatch) {
86+
ExpressionContainer container;
87+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
88+
ASSERT_THAT(container.AddAlias("foo", "bar.baz"), IsOk());
89+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
90+
std::vector<std::string> qualified_ident = {"foo", "Bar"};
91+
std::vector<std::pair<std::string, int>> candidates;
92+
generator.GenerateCandidates(
93+
qualified_ident, [&](absl::string_view candidate, int segment_index) {
94+
candidates.push_back(std::pair(std::string(candidate), segment_index));
95+
return true;
96+
});
97+
EXPECT_THAT(candidates,
98+
ElementsAre(Pair("bar.baz.Bar", 1), Pair("bar.baz", 0)));
99+
}
100+
101+
TEST(NamespaceGeneratorTest, MultipleSegmentsSelectInterpretationAliasNoMatch) {
102+
ExpressionContainer container;
103+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
104+
ASSERT_THAT(container.AddAbbreviation("foo.Bar"), IsOk());
105+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
106+
// No match on the alias (Bar) since it's not the first segment.
76107
std::vector<std::string> qualified_ident = {"foo", "Bar"};
77108
std::vector<std::pair<std::string, int>> candidates;
78109
generator.GenerateCandidates(
@@ -89,8 +120,9 @@ TEST(NamespaceGeneratorTest, MultipleSegmentsSelectInterpretation) {
89120

90121
TEST(NamespaceGeneratorTest,
91122
MultipleSegmentsSelectInterpretationRootNamespace) {
92-
ASSERT_OK_AND_ASSIGN(auto generator,
93-
NamespaceGenerator::Create("com.example"));
123+
ExpressionContainer container;
124+
ASSERT_THAT(container.SetContainer("com.example"), IsOk());
125+
ASSERT_OK_AND_ASSIGN(auto generator, NamespaceGenerator::Create(container));
94126
std::vector<std::string> qualified_ident = {".foo", "Bar"};
95127
std::vector<std::pair<std::string, int>> candidates;
96128
generator.GenerateCandidates(

0 commit comments

Comments
 (0)