Skip to content

Commit 5bc3f4e

Browse files
Add source info to Name and EntityUID
Signed-off-by: John Kastner <[email protected]>
1 parent 5a78a50 commit 5bc3f4e

File tree

14 files changed

+285
-220
lines changed

14 files changed

+285
-220
lines changed

cedar-policy-core/src/ast/entity.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::ast::*;
1818
use crate::evaluator::{EvaluationError, RestrictedEvaluator};
1919
use crate::extensions::Extensions;
2020
use crate::parser::err::ParseErrors;
21+
use crate::parser::Loc;
2122
use crate::transitive_closure::TCNode;
2223
use crate::FromNormalizedStr;
2324
use itertools::Itertools;
@@ -62,13 +63,44 @@ impl std::fmt::Display for EntityType {
6263
}
6364

6465
/// Unique ID for an entity. These represent entities in the AST.
65-
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
66+
#[derive(Serialize, Deserialize, Debug, Clone)]
6667
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
6768
pub struct EntityUID {
6869
/// Typename of the entity
6970
ty: EntityType,
7071
/// EID of the entity
7172
eid: Eid,
73+
/// Location of the entity in policy source
74+
#[serde(skip)]
75+
loc: Option<Loc>,
76+
}
77+
78+
/// `PartialEq` implementation ignores the `loc`.
79+
impl PartialEq for EntityUID {
80+
fn eq(&self, other: &Self) -> bool {
81+
self.ty == other.ty && self.eid == other.eid
82+
}
83+
}
84+
impl Eq for EntityUID {}
85+
86+
impl std::hash::Hash for EntityUID {
87+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
88+
// hash the ty and eid, in line with the `PartialEq` impl which compares
89+
// the ty and eid.
90+
self.ty.hash(state);
91+
self.eid.hash(state);
92+
}
93+
}
94+
95+
impl PartialOrd for EntityUID {
96+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
97+
Some(self.cmp(other))
98+
}
99+
}
100+
impl Ord for EntityUID {
101+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
102+
self.ty.cmp(&other.ty).then(self.eid.cmp(&other.eid))
103+
}
72104
}
73105

74106
impl StaticallyTyped for EntityUID {
@@ -87,6 +119,7 @@ impl EntityUID {
87119
Self {
88120
ty: Self::test_entity_type(),
89121
eid: Eid(eid.into()),
122+
loc: None,
90123
}
91124
}
92125
// by default, Coverlay does not track coverage for lines after a line
@@ -113,6 +146,7 @@ impl EntityUID {
113146
Ok(Self {
114147
ty: EntityType::Specified(Name::parse_unqualified_name(typename)?),
115148
eid: Eid(eid.into()),
149+
loc: None,
116150
})
117151
}
118152

@@ -122,11 +156,17 @@ impl EntityUID {
122156
(self.ty, self.eid)
123157
}
124158

159+
/// Get the source location for this `EntityUID`.
160+
pub fn loc(&self) -> &Option<Loc> {
161+
&self.loc
162+
}
163+
125164
/// Create a nominally-typed `EntityUID` with the given typename and EID
126-
pub fn from_components(name: Name, eid: Eid) -> Self {
165+
pub fn from_components(name: Name, eid: Eid, loc: Option<Loc>) -> Self {
127166
Self {
128167
ty: EntityType::Specified(name),
129168
eid,
169+
loc,
130170
}
131171
}
132172

@@ -135,6 +175,7 @@ impl EntityUID {
135175
Self {
136176
ty: EntityType::Unspecified,
137177
eid,
178+
loc: None,
138179
}
139180
}
140181

@@ -497,12 +538,14 @@ mod test {
497538
let e2 = EntityUID::from_components(
498539
Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
499540
Eid("foo".into()),
541+
None,
500542
);
501543
let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
502544
let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
503545
let e5 = EntityUID::from_components(
504546
Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
505547
Eid("foo".into()),
548+
None,
506549
);
507550

508551
// an EUID is equal to itself

cedar-policy-core/src/ast/name.rs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,43 @@ use super::PrincipalOrResource;
2929
/// This is the `Name` type used to name types, functions, etc.
3030
/// The name can include namespaces.
3131
/// Clone is O(1).
32-
#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
32+
#[derive(Debug, Clone)]
3333
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
3434
pub struct Name {
3535
/// Basename
3636
pub(crate) id: Id,
3737
/// Namespaces
3838
pub(crate) path: Arc<Vec<Id>>,
39+
/// Location of the name in source
40+
pub(crate) loc: Option<Loc>,
41+
}
42+
43+
/// `PartialEq` implementation ignores the `loc`.
44+
impl PartialEq for Name {
45+
fn eq(&self, other: &Self) -> bool {
46+
self.id == other.id && self.path == other.path
47+
}
48+
}
49+
impl Eq for Name {}
50+
51+
impl std::hash::Hash for Name {
52+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
53+
// hash the ty and eid, in line with the `PartialEq` impl which compares
54+
// the ty and eid.
55+
self.id.hash(state);
56+
self.path.hash(state);
57+
}
58+
}
59+
60+
impl PartialOrd for Name {
61+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
62+
Some(self.cmp(other))
63+
}
64+
}
65+
impl Ord for Name {
66+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
67+
self.id.cmp(&other.id).then(self.path.cmp(&other.path))
68+
}
3969
}
4070

4171
/// A shortcut for `Name::unqualified_name`
@@ -61,10 +91,11 @@ impl TryFrom<Name> for Id {
6191

6292
impl Name {
6393
/// A full constructor for `Name`
64-
pub fn new(basename: Id, path: impl IntoIterator<Item = Id>) -> Self {
94+
pub fn new(basename: Id, path: impl IntoIterator<Item = Id>, loc: Option<Loc>) -> Self {
6595
Self {
6696
id: basename,
6797
path: Arc::new(path.into_iter().collect()),
98+
loc,
6899
}
69100
}
70101

@@ -73,6 +104,7 @@ impl Name {
73104
Self {
74105
id,
75106
path: Arc::new(vec![]),
107+
loc: None,
76108
}
77109
}
78110

@@ -82,15 +114,21 @@ impl Name {
82114
Ok(Self {
83115
id: s.parse()?,
84116
path: Arc::new(vec![]),
117+
loc: None,
85118
})
86119
}
87120

88121
/// Given a type basename and a namespace (as a `Name` itself),
89122
/// return a `Name` representing the type's fully qualified name
90-
pub fn type_in_namespace(basename: Id, namespace: Name) -> Name {
123+
pub fn type_in_namespace(basename: Id, namespace: Name, loc: Option<Loc>) -> Name {
91124
let mut path = Arc::unwrap_or_clone(namespace.path);
92125
path.push(namespace.id);
93-
Name::new(basename, path)
126+
Name::new(basename, path, loc)
127+
}
128+
129+
/// Get the source location
130+
pub fn loc(&self) -> &Option<Loc> {
131+
&self.loc
94132
}
95133

96134
/// Get the basename of the `Name` (ie, with namespaces stripped).
@@ -129,6 +167,7 @@ impl Name {
129167
.namespace_components()
130168
.chain(std::iter::once(namespace.basename()))
131169
.cloned(),
170+
self.loc.clone(),
132171
),
133172
None => self.clone(),
134173
}

cedar-policy-core/src/ast/policy.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,7 @@ mod test {
20992099
let id = EntityUID::from_components(
21002100
name::Name::unqualified_name(id::Id::new_unchecked("s")),
21012101
entity::Eid::new("eid"),
2102+
None,
21022103
);
21032104
let mut i = EntityIterator::One(&id);
21042105
assert_eq!(i.next(), Some(&id));
@@ -2110,10 +2111,12 @@ mod test {
21102111
let id1 = EntityUID::from_components(
21112112
name::Name::unqualified_name(id::Id::new_unchecked("s")),
21122113
entity::Eid::new("eid1"),
2114+
None,
21132115
);
21142116
let id2 = EntityUID::from_components(
21152117
name::Name::unqualified_name(id::Id::new_unchecked("s")),
21162118
entity::Eid::new("eid2"),
2119+
None,
21172120
);
21182121
let v = vec![&id1, &id2];
21192122
let mut i = EntityIterator::Bunch(v);

cedar-policy-core/src/entities/json/value.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ impl TryFrom<TypeAndId> for EntityUID {
198198
Ok(EntityUID::from_components(
199199
Name::from_normalized_str(&e.entity_type)?,
200200
Eid::new(e.id),
201+
None,
201202
))
202203
}
203204
}

cedar-policy-core/src/est/expr.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,7 @@ fn interpret_primary(
11081108
__entity: TypeAndId::from(ast::EntityUID::from_components(
11091109
name,
11101110
ast::Eid::new(eid),
1111+
None,
11111112
)),
11121113
}))),
11131114
Err(unescape_errs) => {
@@ -1143,6 +1144,7 @@ fn interpret_primary(
11431144
.and_then(|id| id.to_string().parse().map_err(Into::into))
11441145
})
11451146
.collect::<Result<Vec<ast::Id>, ParseErrors>>()?,
1147+
Some(node.loc.clone()),
11461148
))),
11471149
(path, id) => {
11481150
let (l, r, src) = match (path.first(), path.last()) {

cedar-policy-core/src/parser/cst_to_ast.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,7 +2122,9 @@ impl Node<Option<cst::Name>> {
21222122

21232123
// computation and error generation is complete, so fail or construct
21242124
match (maybe_name, path.len()) {
2125-
(Some(r), len) if len == name.path.len() => Some(construct_name(path, r)),
2125+
(Some(r), len) if len == name.path.len() => {
2126+
Some(construct_name(path, r, self.loc.clone()))
2127+
}
21262128
_ => None,
21272129
}
21282130
}
@@ -2231,7 +2233,7 @@ impl Node<Option<cst::Ref>> {
22312233
};
22322234

22332235
match (maybe_path, maybe_eid) {
2234-
(Some(p), Some(e)) => Some(construct_refr(p, e)),
2236+
(Some(p), Some(e)) => Some(construct_refr(p, e, self.loc.clone())),
22352237
_ => None,
22362238
}
22372239
}
@@ -2346,15 +2348,16 @@ fn construct_string_from_var(v: ast::Var) -> SmolStr {
23462348
ast::Var::Context => "context".into(),
23472349
}
23482350
}
2349-
fn construct_name(path: Vec<ast::Id>, id: ast::Id) -> ast::Name {
2351+
fn construct_name(path: Vec<ast::Id>, id: ast::Id, loc: Loc) -> ast::Name {
23502352
ast::Name {
23512353
id,
23522354
path: Arc::new(path),
2355+
loc: Some(loc),
23532356
}
23542357
}
2355-
fn construct_refr(p: ast::Name, n: SmolStr) -> ast::EntityUID {
2358+
fn construct_refr(p: ast::Name, n: SmolStr, loc: Loc) -> ast::EntityUID {
23562359
let eid = ast::Eid::new(n);
2357-
ast::EntityUID::from_components(p, eid)
2360+
ast::EntityUID::from_components(p, eid, Some(loc))
23582361
}
23592362
fn construct_expr_ref(r: ast::EntityUID, loc: Loc) -> ast::Expr {
23602363
ast::ExprBuilder::new().with_source_loc(loc).val(r)

cedar-policy-validator/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ wasm = ["serde-wasm-bindgen", "tsify", "wasm-bindgen"]
4747

4848
[dev-dependencies]
4949
cool_asserts = "2.0"
50+
cedar-policy-core = { version = "=4.0.0", path = "../cedar-policy-core", features = ["test-util"] }
5051

5152
[build-dependencies]
5253
lalrpop = "0.20.0"

cedar-policy-validator/src/human_schema/ast.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ impl std::fmt::Display for Path {
113113

114114
impl From<Path> for Name {
115115
fn from(value: Path) -> Self {
116-
value.0.node.into()
116+
Self::new(
117+
value.0.node.basename,
118+
value.0.node.namespace,
119+
Some(value.0.loc),
120+
)
117121
}
118122
}
119123

@@ -123,12 +127,6 @@ struct PathInternal {
123127
namespace: Vec<Id>,
124128
}
125129

126-
impl From<PathInternal> for Name {
127-
fn from(value: PathInternal) -> Self {
128-
Self::new(value.basename, value.namespace)
129-
}
130-
}
131-
132130
impl PathInternal {
133131
fn iter(&self) -> impl Iterator<Item = &Id> {
134132
self.namespace.iter().chain(once(&self.basename))

cedar-policy-validator/src/human_schema/to_json_schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ impl<'a> ConversionContext<'a> {
434434
&Some(Name::new(
435435
prefix_base.clone(),
436436
prefix_prefix.into_iter().cloned(),
437+
None,
437438
)),
438439
),
439440
None =>

0 commit comments

Comments
 (0)