@@ -32,33 +32,58 @@ class _State:
3232 block_type : str | None = None # "bundle" | "body" | "promise" | None
3333 promise_type : str | None = None # "vars" | "files" | "classes" | ... | None
3434 attribute_name : str | None = None # "if" | "string" | "slist" | ... | None
35+ namespace : str = "default" # "ns" | "default" | ... |
3536
3637 def update (self , node ) -> "_State" :
3738 """Updates and returns the state that should apply to the children of `node`."""
39+ if node .type == "}" :
40+ assert node .parent
41+ assert self .attribute_name == "list" or node .parent .type in [
42+ "bundle_block_body" ,
43+ "promise_block_body" ,
44+ "body_block_body" ,
45+ ]
46+ if self .attribute_name != "list" :
47+ self .block_type = None
48+ self .promise_type = None
49+ self .attribute_name = None
50+ return self
51+ if node .type == ";" :
52+ self .attribute_name = None
53+ return self
3854 if node .type == "bundle_block" :
39- return _State (block_type = "bundle" )
55+ self .block_type = "bundle"
56+ return self
4057 if node .type == "body_block" :
41- return _State (block_type = "body" )
58+ self .block_type = "body"
59+ return self
4260 if node .type == "promise_block" :
43- return _State (block_type = "promise" )
61+ self .block_type = "promise"
62+ return self
4463 if node .type == "bundle_section" :
45- for child in node .children :
46- if child .type == "promise_guard" :
47- return _State (
48- block_type = self .block_type ,
49- promise_type = _text (child )[:- 1 ], # strip trailing ':'
50- )
51- return _State (block_type = self .block_type )
64+ # A bundle_section is always: promise_guard, [promises], [class_guarded_promises...]
65+ # The promise_guard is guaranteed to exist by the grammar
66+ guard = next ((c for c in node .children if c .type == "promise_guard" ), None )
67+ if guard is None : # Should never happen
68+ print ("ERROR: Bundle section without a promise guard" )
69+ return self
70+
71+ self .promise_type = _text (guard )[:- 1 ] # strip trailing ':'
72+ return self
5273 if node .type == "attribute" :
5374 for child in node .children :
5475 if child .type == "attribute_name" :
55- return _State (
56- block_type = self .block_type ,
57- promise_type = self .promise_type ,
58- attribute_name = _text (child ),
59- )
76+ self .attribute_name = _text (child )
77+ if self .attribute_name == "namespace" :
78+ self .namespace = _text (child .next_named_sibling ).strip ("\" '" )
79+ return self
6080 return self
6181
82+ @staticmethod
83+ def qualify (name : str , namespace : str ) -> str :
84+ """If name is already qualified (contains ':'), return as-is. Otherwise prepend namespace."""
85+ return name if ":" in name else f"{ namespace } :{ name } "
86+
6287
6388def lint_cfbs_json (filename ) -> int :
6489 assert os .path .isfile (filename )
@@ -184,7 +209,8 @@ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
184209 if node .type == "calling_identifier" :
185210 if (
186211 strict
187- and _text (node ) in user_definition .get ("all_bundle_names" , set ())
212+ and state .qualify (_text (node ), state .namespace )
213+ in user_definition .get ("all_bundle_names" , set ())
188214 and state .promise_type in user_definition .get ("custom_promise_types" , set ())
189215 ):
190216 _highlight_range (node , lines )
@@ -193,11 +219,12 @@ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
193219 )
194220 return 1
195221 if strict and (
196- _text (node )
197- not in BUILTIN_FUNCTIONS .union (
222+ state . qualify ( _text (node ), state . namespace )
223+ not in set .union (
198224 user_definition .get ("all_bundle_names" , set ()),
199225 user_definition .get ("all_body_names" , set ()),
200226 )
227+ and _text (node ) not in BUILTIN_FUNCTIONS
201228 ):
202229 _highlight_range (node , lines )
203230 print (
@@ -215,11 +242,9 @@ def _stateful_walk(
215242
216243 errors = _node_checks (filename , lines , node , user_definition , strict , state )
217244
218- child_state = state .update (node )
245+ state .update (node )
219246 for child in node .children :
220- errors += _stateful_walk (
221- filename , lines , child , user_definition , strict , child_state
222- )
247+ errors += _stateful_walk (filename , lines , child , user_definition , strict , state )
223248 return errors
224249
225250
@@ -239,18 +264,55 @@ def _walk(filename, lines, node, user_definition=None, strict=True) -> int:
239264 line = node .range .start_point [0 ] + 1
240265 column = node .range .start_point [1 ] + 1
241266
242- return _stateful_walk (filename , lines , node , user_definition , strict )
267+ state = _State ()
268+ ret = _stateful_walk (filename , lines , node , user_definition , strict , state = state )
269+ state = _State () # Clear state
270+ return ret
243271
244272
245273def _parse_user_definition (filename , lines , root_node ):
246- promise_blocks = _find_node_type (filename , lines , root_node , "promise_block_name" )
247- bundle_blocks = _find_node_type (filename , lines , root_node , "bundle_block_name" )
248- body_blocks = _find_node_type (filename , lines , root_node , "body_block_name" )
274+ ns = "default"
275+ promise_blocks = set ()
276+ bundle_blocks = set ()
277+ body_blocks = set ()
278+
279+ for child in root_node .children :
280+ if child .type == "body_block" :
281+ name_node = next (
282+ (c for c in child .named_children if c .type == "body_block_name" ),
283+ None ,
284+ )
285+ ns_attr = next (
286+ (
287+ c
288+ for c in _find_node_type (filename , lines , child , "attribute_name" )
289+ if _text (c ) == "namespace"
290+ ),
291+ None ,
292+ )
293+ if ns_attr is not None :
294+ ns = _text (ns_attr .next_named_sibling ).strip ("\" '" )
295+ elif name_node is not None :
296+ body_blocks .add (_State .qualify (_text (name_node ), ns ))
297+ elif child .type == "bundle_block" :
298+ name_node = next (
299+ (c for c in child .named_children if c .type == "bundle_block_name" ),
300+ None ,
301+ )
302+ if name_node is not None :
303+ bundle_blocks .add (_State .qualify (_text (name_node ), ns ))
304+ elif child .type == "promise_block" :
305+ name_node = next (
306+ (c for c in child .named_children if c .type == "promise_block_name" ),
307+ None ,
308+ )
309+ if name_node is not None :
310+ promise_blocks .add (_text (name_node ))
249311
250312 return {
251- "custom_promise_types" : { _text ( x ) for x in promise_blocks } ,
252- "all_bundle_names" : { _text ( x ) for x in bundle_blocks } ,
253- "all_body_names" : { _text ( x ) for x in body_blocks } ,
313+ "custom_promise_types" : promise_blocks ,
314+ "all_bundle_names" : bundle_blocks ,
315+ "all_body_names" : body_blocks ,
254316 }
255317
256318
@@ -323,6 +385,7 @@ def lint_policy_file(
323385 else :
324386 print (f"Error: Empty policy file '{ filename } '" )
325387 errors += 1
388+ print (filename )
326389 errors += _walk (filename , lines , root_node , user_definition , strict )
327390 if prefix :
328391 print (prefix , end = "" )
0 commit comments