Skip to content

Commit 377d2df

Browse files
committed
Fix name resolution from a project's Main.qs in Python and Circuits
Fixes #2207
1 parent a3aaf4f commit 377d2df

File tree

2 files changed

+75
-6
lines changed

2 files changed

+75
-6
lines changed

compiler/qsc/src/interpret/tests.rs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,12 +1447,11 @@ mod given_interpreter {
14471447
}
14481448

14491449
fn is_unit_with_output(result: &InterpretResult, output: &str, expected_output: &str) {
1450-
assert_eq!(expected_output, output);
1451-
14521450
match result {
14531451
Ok(value) => assert_eq!(Value::unit(), *value),
14541452
Err(e) => panic!("Expected unit value, got {e:?}"),
14551453
}
1454+
assert_eq!(expected_output, output);
14561455
}
14571456

14581457
fn is_only_error<E>(result: &Result<Value, Vec<E>>, output: &str, expected_errors: &Expect)
@@ -2089,5 +2088,73 @@ mod given_interpreter {
20892088
entry: None,
20902089
}
20912090
}
2091+
2092+
#[test]
2093+
fn name_resolution_from_source_named_main_should_succeed() {
2094+
let sources = SourceMap::new(
2095+
[(
2096+
"Main".into(),
2097+
r#"function Foo() : Unit { Message("hello there..."); }"#.into(),
2098+
)],
2099+
None,
2100+
);
2101+
let (std_id, store) =
2102+
crate::compile::package_store_with_stdlib(TargetCapabilityFlags::all());
2103+
let mut interpreter = Interpreter::new(
2104+
sources,
2105+
PackageType::Lib,
2106+
TargetCapabilityFlags::all(),
2107+
LanguageFeatures::default(),
2108+
store,
2109+
&[(std_id, None)],
2110+
)
2111+
.expect("interpreter should be created");
2112+
2113+
// Operations defined in Main.qs should also be visible with Main qualifier.
2114+
let (result, output) = line(&mut interpreter, "Main.Foo()");
2115+
is_unit_with_output(&result, &output, "hello there...");
2116+
2117+
// Operations defined in Main.qs should be importable with fully qualified name.
2118+
let (result, output) = line(&mut interpreter, "import Main.Foo;");
2119+
is_only_value(&result, &output, &Value::unit());
2120+
2121+
// After import the operation can be invoked without Main qualifier.
2122+
let (result, output) = line(&mut interpreter, "Foo()");
2123+
is_unit_with_output(&result, &output, "hello there...");
2124+
}
2125+
2126+
#[test]
2127+
fn name_resolution_from_source_named_main_without_full_path_or_import_should_fail() {
2128+
let sources = SourceMap::new(
2129+
[(
2130+
"Main".into(),
2131+
r#"function Foo() : Unit { Message("hello there..."); }"#.into(),
2132+
)],
2133+
None,
2134+
);
2135+
let (std_id, store) =
2136+
crate::compile::package_store_with_stdlib(TargetCapabilityFlags::all());
2137+
let mut interpreter = Interpreter::new(
2138+
sources,
2139+
PackageType::Lib,
2140+
TargetCapabilityFlags::all(),
2141+
LanguageFeatures::default(),
2142+
store,
2143+
&[(std_id, None)],
2144+
)
2145+
.expect("interpreter should be created");
2146+
2147+
// Operations defined in Main.qs should also be visible with Main qualifier.
2148+
let (errors, _) = line(&mut interpreter, "Foo()");
2149+
is_error(
2150+
&errors.expect_err("line invocation should fail with error"),
2151+
&expect![[r#"
2152+
name error: `Foo` not found
2153+
[line_0] [Foo]
2154+
type error: insufficient type information to infer type
2155+
[line_0] [Foo()]
2156+
"#]],
2157+
);
2158+
}
20922159
}
20932160
}

compiler/qsc_frontend/src/resolve.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,9 +1626,11 @@ impl GlobalTable {
16261626
global.visibility == hir::Visibility::Public
16271627
|| matches!(&global.kind, global::Kind::Term(t) if t.intrinsic)
16281628
}) {
1629-
// If the namespace is `Main`, we treat it as the root of the package, so there's no
1630-
// namespace prefix.
1631-
let global_namespace = if global.namespace.len() == 1 && &*global.namespace[0] == "Main"
1629+
// If the namespace is `Main` and we have an alias, we treat it as the root of the package, so there's no
1630+
// namespace prefix between the dependency alias and the defined items.
1631+
let global_namespace = if global.namespace.len() == 1
1632+
&& &*global.namespace[0] == "Main"
1633+
&& alias.is_some()
16321634
{
16331635
vec![]
16341636
} else {
@@ -2309,7 +2311,7 @@ fn find_symbol_in_namespace<O>(
23092311
return;
23102312
}
23112313

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

23152317
// If a symbol was found, insert it into the candidates map

0 commit comments

Comments
 (0)