Skip to content

Commit 64025cb

Browse files
Natalie Jamesonfacebook-github-bot
authored andcommitted
Use the original visibility of symbols in ScopeData
Summary: When evaluating a module, we do some funkiness with scopes during compilation. We create a scope, run enter_module, then pull that data back out with `exit_module`, and feed that to the compiler. As part of enter_module, we copy all of that module's bindings over, but ignore their visibility, and set it to Public. When we run the compiler on that incorrect data, we spit out a module with the wrong visibilities. Correct this so that we maintain the original visibilities (also adding a quick helper on Module. Instead of fetching the names, then grabbing the visibility for each, return the names and visibilities) Reviewed By: ndmitchell Differential Revision: D30916481 fbshipit-source-id: f93b1964b119db830e4c3f31465a3e8c87cc9c4c
1 parent dd1e76b commit 64025cb

File tree

4 files changed

+55
-5
lines changed

4 files changed

+55
-5
lines changed

starlark/src/environment/modules.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ impl Module {
322322

323323
/// Set the value of a variable in the environment. Set its visibliity to
324324
/// "private" to ensure that it is not re-exported
325-
fn set_private<'v>(&'v self, name: &str, value: Value<'v>) {
325+
pub(crate) fn set_private<'v>(&'v self, name: &str, value: Value<'v>) {
326326
let slot = self.names.add_name_visibility(name, Visibility::Private);
327327
let slots = self.slots();
328328
slots.ensure_slot(slot);

starlark/src/environment/names.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ impl MutableNames {
102102
.collect()
103103
}
104104

105+
pub(crate) fn all_names_and_visibilities(&self) -> IndexMap<String, Visibility> {
106+
self.0
107+
.borrow()
108+
.iter()
109+
.map(|(name, (_slot, vis))| (name.to_owned(), *vis))
110+
.collect()
111+
}
112+
105113
pub fn freeze(self) -> FrozenNames {
106114
FrozenNames(self.0.into_inner())
107115
}

starlark/src/eval/compiler/scope.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,9 @@ impl<'a> Scope<'a> {
173173

174174
let mut locals = IndexMap::new();
175175

176-
let existing_module_names = module.all_names();
177-
for name in existing_module_names.keys() {
178-
let (binding_id, _binding) =
179-
scope_data.new_binding(Visibility::Public, AssignCount::AtMostOnce);
176+
let existing_module_names_and_visibilites = module.all_names_and_visibilities();
177+
for (name, vis) in existing_module_names_and_visibilites.iter() {
178+
let (binding_id, _binding) = scope_data.new_binding(*vis, AssignCount::AtMostOnce);
180179
locals.insert(name.as_str(), binding_id);
181180
}
182181

starlark/src/eval/tests/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,49 @@ fn test_load_reexport() {
402402
);
403403
}
404404

405+
#[test]
406+
fn test_module_visibility_preserved_by_evaluator() -> anyhow::Result<()> {
407+
// Make sure that when we use a module in the evaluator, the entering / exiting the
408+
// module with ScopeData preserves the visibility of symbols.
409+
410+
let globals = Globals::standard();
411+
412+
let import = Module::new();
413+
import.set("a", Value::new_int(1));
414+
import.set_private("b", Value::new_int(2));
415+
416+
let mut eval = Evaluator::new(&import, &globals);
417+
let ast = AstModule::parse("prelude.bzl", "c = 3".to_owned(), &Dialect::Standard).unwrap();
418+
// This mutates the original module named `import`
419+
let _: Value = eval.eval_module(ast)?;
420+
let frozen_import = import.freeze()?;
421+
422+
let m_uses_public = Module::new();
423+
m_uses_public.import_public_symbols(&frozen_import);
424+
let mut eval = Evaluator::new(&m_uses_public, &globals);
425+
let ast = AstModule::parse("code.bzl", "d = a".to_owned(), &Dialect::Standard).unwrap();
426+
let _: Value = eval.eval_module(ast)?;
427+
428+
let m_uses_private = Module::new();
429+
m_uses_private.import_public_symbols(&frozen_import);
430+
let mut eval = Evaluator::new(&m_uses_private, &globals);
431+
let ast = AstModule::parse("code.bzl", "d = b".to_owned(), &Dialect::Standard).unwrap();
432+
let err = eval
433+
.eval_module(ast)
434+
.expect_err("Evaluation should have failed using a private symbol");
435+
436+
let msg = err.to_string();
437+
let expected_msg = "Variable `b` not found";
438+
if !msg.contains(expected_msg) {
439+
panic!(
440+
"Expected `{}` to be in error message `{}`",
441+
expected_msg, msg
442+
);
443+
}
444+
445+
Ok(())
446+
}
447+
405448
#[test]
406449
fn test_load_did_you_mean() {
407450
let mut a = Assert::new();

0 commit comments

Comments
 (0)