Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions compiler/qsc/src/interpret/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1447,12 +1447,11 @@ mod given_interpreter {
}

fn is_unit_with_output(result: &InterpretResult, output: &str, expected_output: &str) {
assert_eq!(expected_output, output);

match result {
Ok(value) => assert_eq!(Value::unit(), *value),
Err(e) => panic!("Expected unit value, got {e:?}"),
}
assert_eq!(expected_output, output);
}

fn is_only_error<E>(result: &Result<Value, Vec<E>>, output: &str, expected_errors: &Expect)
Expand Down Expand Up @@ -2089,5 +2088,73 @@ mod given_interpreter {
entry: None,
}
}

#[test]
fn name_resolution_from_source_named_main_should_succeed() {
let sources = SourceMap::new(
[(
"Main".into(),
r#"function Foo() : Unit { Message("hello there..."); }"#.into(),
)],
None,
);
let (std_id, store) =
crate::compile::package_store_with_stdlib(TargetCapabilityFlags::all());
let mut interpreter = Interpreter::new(
sources,
PackageType::Lib,
TargetCapabilityFlags::all(),
LanguageFeatures::default(),
store,
&[(std_id, None)],
)
.expect("interpreter should be created");

// Operations defined in Main.qs should also be visible with Main qualifier.
let (result, output) = line(&mut interpreter, "Main.Foo()");
is_unit_with_output(&result, &output, "hello there...");

// Operations defined in Main.qs should be importable with fully qualified name.
let (result, output) = line(&mut interpreter, "import Main.Foo;");
is_only_value(&result, &output, &Value::unit());

// After import the operation can be invoked without Main qualifier.
let (result, output) = line(&mut interpreter, "Foo()");
is_unit_with_output(&result, &output, "hello there...");
}

#[test]
fn name_resolution_from_source_named_main_without_full_path_or_import_should_fail() {
let sources = SourceMap::new(
[(
"Main".into(),
r#"function Foo() : Unit { Message("hello there..."); }"#.into(),
)],
None,
);
let (std_id, store) =
crate::compile::package_store_with_stdlib(TargetCapabilityFlags::all());
let mut interpreter = Interpreter::new(
sources,
PackageType::Lib,
TargetCapabilityFlags::all(),
LanguageFeatures::default(),
store,
&[(std_id, None)],
)
.expect("interpreter should be created");

// Operations defined in Main.qs should also be visible with Main qualifier.
let (errors, _) = line(&mut interpreter, "Foo()");
is_error(
&errors.expect_err("line invocation should fail with error"),
&expect![[r#"
name error: `Foo` not found
[line_0] [Foo]
type error: insufficient type information to infer type
[line_0] [Foo()]
"#]],
);
}
}
}
10 changes: 6 additions & 4 deletions compiler/qsc_frontend/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1626,9 +1626,11 @@ impl GlobalTable {
global.visibility == hir::Visibility::Public
|| matches!(&global.kind, global::Kind::Term(t) if t.intrinsic)
}) {
// If the namespace is `Main`, we treat it as the root of the package, so there's no
// namespace prefix.
let global_namespace = if global.namespace.len() == 1 && &*global.namespace[0] == "Main"
// If the namespace is `Main` and we have an alias, we treat it as the root of the package, so there's no
// namespace prefix between the dependency alias and the defined items.
let global_namespace = if global.namespace.len() == 1
&& &*global.namespace[0] == "Main"
&& alias.is_some()
{
vec![]
} else {
Expand Down Expand Up @@ -2309,7 +2311,7 @@ fn find_symbol_in_namespace<O>(
return;
}

// Attempt to get the symbol from the global scope. If the namespace is None, use the candidate_namespace_id as a fallback
// Attempt to get the symbol from the global scope.
let res = namespace.and_then(|ns_id| globals.get(kind, ns_id, &provided_symbol_name.name));

// If a symbol was found, insert it into the candidates map
Expand Down
Loading