microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
alex/pythontelem

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_frontend/src/incremental/tests.rs

720lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::{Compiler, Increment};
5use crate::{
6 compile::{self, CompileUnit, PackageStore, SourceMap},
7 incremental::Error,
8};
9use expect_test::{expect, Expect};
10use indoc::indoc;
11use miette::Diagnostic;
12use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
13use std::{fmt::Write, sync::Arc};
14
15#[allow(clippy::too_many_lines)]
16#[test]
17fn one_callable() {
18 let store = PackageStore::new(compile::core());
19 let mut compiler = Compiler::new(
20 &store,
21 &[],
22 TargetCapabilityFlags::all(),
23 LanguageFeatures::default(),
24 );
25 let unit = compiler
26 .compile_fragments(
27 &mut CompileUnit::default(),
28 "test_1",
29 "namespace Foo { operation Main() : Unit {} }",
30 fail_on_error,
31 )
32 .expect("compilation should succeed");
33
34 check_unit(
35 &expect![[r#"
36 ast:
37 Package 0:
38 Namespace 1 [0-44] (Ident 2 [10-13] "Foo"):
39 Item 3 [16-42]:
40 Callable 4 [16-42] (Operation):
41 name: Ident 5 [26-30] "Main"
42 input: Pat 6 [30-32]: Unit
43 output: Type 7 [35-39]: Path: Path 8 [35-39] (Ident 9 [35-39] "Unit")
44 body: Block: Block 10 [40-42]: <empty>
45 names:
46 node_id:1,node_id:5,node_id:8,
47 terms:
48 node_id:6,node_id:10,
49 locals:
50 Locals {
51 scopes: [
52 Scope {
53 span: Span {
54 lo: 0,
55 hi: 4294967295,
56 },
57 kind: Block,
58 opens: {},
59 tys: {},
60 terms: {},
61 vars: {},
62 ty_vars: {},
63 },
64 Scope {
65 span: Span {
66 lo: 0,
67 hi: 44,
68 },
69 kind: Namespace(
70 NamespaceId(
71 5,
72 ),
73 ),
74 opens: {
75 []: [
76 Open {
77 namespace: NamespaceId(
78 5,
79 ),
80 span: Span {
81 lo: 10,
82 hi: 13,
83 },
84 },
85 ],
86 },
87 tys: {},
88 terms: {},
89 vars: {},
90 ty_vars: {},
91 },
92 Scope {
93 span: Span {
94 lo: 16,
95 hi: 42,
96 },
97 kind: Callable,
98 opens: {},
99 tys: {},
100 terms: {},
101 vars: {},
102 ty_vars: {},
103 },
104 Scope {
105 span: Span {
106 lo: 40,
107 hi: 42,
108 },
109 kind: Block,
110 opens: {},
111 tys: {},
112 terms: {},
113 vars: {},
114 ty_vars: {},
115 },
116 ],
117 }
118 hir:
119 Package:
120 Item 0 [0-44] (Public):
121 Namespace (Ident 5 [10-13] "Foo"): Item 1
122 Item 1 [16-42] (Internal):
123 Parent: 0
124 Callable 0 [16-42] (operation):
125 name: Ident 1 [26-30] "Main"
126 input: Pat 2 [30-32] [Type Unit]: Unit
127 output: Unit
128 functors: empty set
129 body: SpecDecl 3 [16-42]: Impl:
130 Block 4 [40-42]: <empty>
131 adj: <none>
132 ctl: <none>
133 ctl-adj: <none>"#]],
134 &unit,
135 );
136}
137
138#[test]
139fn one_statement() {
140 let store = PackageStore::new(compile::core());
141 let mut compiler = Compiler::new(
142 &store,
143 &[],
144 TargetCapabilityFlags::all(),
145 LanguageFeatures::default(),
146 );
147 let unit = compiler
148 .compile_fragments(
149 &mut CompileUnit::default(),
150 "test_1",
151 "use q = Qubit();",
152 fail_on_error,
153 )
154 .expect("compilation should succeed");
155
156 check_unit(
157 &expect![[r#"
158 ast:
159 Package 0:
160 Stmt 1 [0-16]: Qubit (Fresh)
161 Pat 2 [4-5]: Bind:
162 Ident 3 [4-5] "q"
163 QubitInit 4 [8-15] Single
164 names:
165 node_id:3,
166 terms:
167 node_id:1,node_id:2,node_id:3,node_id:4,
168 locals:
169 Locals {
170 scopes: [
171 Scope {
172 span: Span {
173 lo: 0,
174 hi: 4294967295,
175 },
176 kind: Block,
177 opens: {},
178 tys: {},
179 terms: {},
180 vars: {
181 "q": (
182 16,
183 NodeId(
184 3,
185 ),
186 ),
187 },
188 ty_vars: {},
189 },
190 ],
191 }
192 hir:
193 Package:
194 Stmt 0 [0-16]: Qubit (Fresh)
195 Pat 1 [4-5] [Type Qubit]: Bind: Ident 2 [4-5] "q"
196 QubitInit 3 [8-15] [Type Qubit]: Single"#]],
197 &unit,
198 );
199}
200
201#[test]
202fn parse_error() {
203 let store = PackageStore::new(compile::core());
204 let mut compiler = Compiler::new(
205 &store,
206 &[],
207 TargetCapabilityFlags::all(),
208 LanguageFeatures::default(),
209 );
210 let errors = compiler
211 .compile_fragments(&mut CompileUnit::default(), "test_1", "}}", fail_on_error)
212 .expect_err("should fail");
213
214 expect![[r#"
215 [
216 WithSource {
217 sources: [
218 Source {
219 name: "test_1",
220 contents: "}}",
221 offset: 0,
222 },
223 ],
224 error: Error(
225 Parse(
226 Error(
227 Token(
228 Eof,
229 Close(
230 Brace,
231 ),
232 Span {
233 lo: 0,
234 hi: 1,
235 },
236 ),
237 ),
238 ),
239 ),
240 },
241 ]
242 "#]]
243 .assert_debug_eq(&errors);
244}
245
246#[test]
247fn conditional_compilation_not_available() {
248 let store = PackageStore::new(compile::core());
249 let mut compiler = Compiler::new(
250 &store,
251 &[],
252 TargetCapabilityFlags::all(),
253 LanguageFeatures::default(),
254 );
255 let errors = compiler
256 .compile_fragments(
257 &mut CompileUnit::default(),
258 "test_1",
259 indoc! {"
260 @Config(Base)
261 function Dropped() : Unit {}
262
263 function Usage() : Unit {
264 Dropped();
265 }
266 "},
267 fail_on_error,
268 )
269 .expect_err("should fail");
270
271 assert!(!errors.is_empty());
272}
273
274#[test]
275fn errors_across_multiple_lines() {
276 let mut store = PackageStore::new(compile::core());
277 let std_id = store.insert(compile::std(&store, TargetCapabilityFlags::all()));
278 let mut compiler = Compiler::new(
279 &store,
280 &[(std_id, None)],
281 TargetCapabilityFlags::all(),
282 LanguageFeatures::default(),
283 );
284 let mut unit = CompileUnit::default();
285 compiler
286 .compile_fragments(
287 &mut unit,
288 "line_1",
289 "namespace Other { operation DumpMachine() : Unit { } }",
290 fail_on_error,
291 )
292 .expect("should succeed");
293
294 compiler
295 .compile_fragments(&mut unit, "line_2", "open Other;", fail_on_error)
296 .expect("should succeed");
297
298 compiler
299 .compile_fragments(
300 &mut unit,
301 "line_3",
302 "open Microsoft.Quantum.Diagnostics;",
303 fail_on_error,
304 )
305 .expect("should succeed");
306
307 let errors = compiler
308 .compile_fragments(&mut unit, "line_4", "DumpMachine()", fail_on_error)
309 .expect_err("should fail");
310
311 // Here we're validating that the compiler is able to return
312 // error labels mapping to different lines.
313 // The `Ambiguous` error is chosen as a test case because
314 // it contains multiple spans.
315 let labels = errors
316 .iter()
317 .flat_map(|e| e.labels().into_iter().flatten())
318 .map(|l| {
319 unit.sources
320 .find_by_offset(u32::try_from(l.offset()).expect("offset should fit into u32"))
321 .map(|s| &s.name)
322 })
323 .collect::<Vec<_>>();
324
325 expect![[r#"
326 [
327 Some(
328 "line_4",
329 ),
330 Some(
331 "line_2",
332 ),
333 Some(
334 "line_3",
335 ),
336 Some(
337 "line_4",
338 ),
339 ]
340 "#]]
341 .assert_debug_eq(&labels);
342}
343
344#[test]
345fn continue_after_parse_error() {
346 let store = PackageStore::new(compile::core());
347 let mut compiler = Compiler::new(
348 &store,
349 &Vec::new(),
350 TargetCapabilityFlags::all(),
351 LanguageFeatures::default(),
352 );
353 let mut errors = Vec::new();
354
355 compiler
356 .compile_fragments(
357 &mut CompileUnit::default(),
358 "test_1",
359 "operation Main() : Foo {
360 }}",
361 |e| -> Result<(), ()> {
362 errors.extend(e);
363 Ok(())
364 },
365 )
366 .expect("compile_fragments should succeed");
367
368 expect![[r#"
369 [
370 WithSource {
371 sources: [
372 Source {
373 name: "test_1",
374 contents: "operation Main() : Foo {\n }}",
375 offset: 0,
376 },
377 ],
378 error: Error(
379 Parse(
380 Error(
381 Token(
382 Eof,
383 Close(
384 Brace,
385 ),
386 Span {
387 lo: 38,
388 hi: 39,
389 },
390 ),
391 ),
392 ),
393 ),
394 },
395 WithSource {
396 sources: [
397 Source {
398 name: "test_1",
399 contents: "operation Main() : Foo {\n }}",
400 offset: 0,
401 },
402 ],
403 error: Error(
404 Resolve(
405 NotFound(
406 "Foo",
407 Span {
408 lo: 19,
409 hi: 22,
410 },
411 ),
412 ),
413 ),
414 },
415 ]
416 "#]]
417 .assert_debug_eq(&errors);
418}
419
420#[test]
421fn continue_after_lower_error() {
422 let store = PackageStore::new(compile::core());
423 let mut compiler = Compiler::new(
424 &store,
425 &[],
426 TargetCapabilityFlags::all(),
427 LanguageFeatures::default(),
428 );
429 let mut unit = CompileUnit::default();
430
431 let mut errors = Vec::new();
432
433 compiler
434 .compile_fragments(
435 &mut unit,
436 "test_1",
437 "operation A(q : Qubit) : Unit is Adj {
438 adjoint ... {}
439 }",
440 |e| -> Result<(), ()> {
441 errors = e;
442 Ok(())
443 },
444 )
445 .expect("compile_fragments should succeed");
446
447 expect![[r#"
448 [
449 WithSource {
450 sources: [
451 Source {
452 name: "test_1",
453 contents: "operation A(q : Qubit) : Unit is Adj {\n adjoint ... {}\n }",
454 offset: 0,
455 },
456 ],
457 error: Error(
458 Lower(
459 MissingBody(
460 Span {
461 lo: 0,
462 hi: 83,
463 },
464 ),
465 ),
466 ),
467 },
468 ]
469 "#]].assert_debug_eq(&errors);
470}
471#[test]
472fn import_foo() {
473 multi_package_test(
474 vec![(
475 "PackageA.qs",
476 indoc! {"
477 operation Foo(x: Int, y: Bool) : Int {
478 x
479 }
480 export Foo;
481 "},
482 )],
483 vec![(
484 "PackageB.qs",
485 indoc! {"
486 import A.PackageA.Foo;
487 "},
488 )],
489 &[("A", "PackageA")],
490 "",
491 );
492}
493
494#[test]
495fn import_foo_with_alias() {
496 multi_package_test(
497 vec![(
498 "PackageA.qs",
499 indoc! {"
500 operation Foo(x: Int, y: Bool) : Int {
501 x
502 }
503 export Foo;
504 "},
505 )],
506 vec![(
507 "PackageB.qs",
508 indoc! {"
509 import A.PackageA.Foo as Foo2;
510 "},
511 )],
512 &[("A", "PackageA")],
513 "",
514 );
515}
516
517#[test]
518fn export_foo_with_alias() {
519 multi_package_test(
520 vec![(
521 "PackageA.qs",
522 indoc! {"
523 operation Foo(x: Int, y: Bool) : Int {
524 x
525 }
526 export Foo;
527 "},
528 )],
529 vec![(
530 "PackageB.qs",
531 indoc! {"
532 import A.PackageA.Foo;
533 export Foo as Bar;
534 "},
535 )],
536 &[("A", "PackageA")],
537 "",
538 );
539}
540
541#[test]
542fn combined_import_export() {
543 multi_package_test(
544 vec![(
545 "PackageA.qs",
546 indoc! {"
547 operation Foo(x: Int, y: Bool) : Int {
548 x
549 }
550 export Foo;
551 "},
552 )],
553 vec![(
554 "PackageB.qs",
555 indoc! {"
556 import A.PackageA.Foo;
557 import A.PackageA.Foo as Foo2;
558 export Foo, Foo as Bar, Foo2, Foo2 as Bar2;
559 "},
560 )],
561 &[("A", "PackageA")],
562 indoc! {"
563 import B.PackageB.Foo, B.PackageB.Bar, B.PackageB.Foo2, B.PackageB.Bar2;
564 @EntryPoint()
565 function Main() : Unit {
566 Foo(10, true);
567 Foo2(10, true);
568 Bar(10, true);
569 Bar2(10, true);
570 }
571 "},
572 );
573}
574
575#[test]
576fn reexport_operation_from_a_dependency() {
577 multi_package_test(
578 vec![(
579 "PackageA.qs",
580 indoc! {"
581 operation Foo(x: Int, y: Bool) : Int {
582 x
583 }
584 export Foo;
585 "},
586 )],
587 vec![(
588 "PackageB.qs",
589 indoc! {"
590 import A.PackageA.Foo;
591 export Foo as Bar;
592 "},
593 )],
594 &[("A", "PackageA")],
595 indoc! {"
596 import B.PackageB.Bar;
597 @EntryPoint()
598 function Main() : Unit {
599 Bar(10, true);
600 }
601 "},
602 );
603}
604
605fn multi_package_test(
606 packages: Vec<(&str, &str)>,
607 dependencies: Vec<(&str, &str)>,
608 imports: &[(&str, &str)],
609 user_code: &str,
610) {
611 let mut store = PackageStore::new(compile::core());
612
613 let packages = packages
614 .into_iter()
615 .map(|(name, code)| {
616 let source_map = SourceMap::new([(name.into(), code.into())], None);
617 let compiled_package = compile::compile(
618 &store,
619 &[],
620 source_map,
621 TargetCapabilityFlags::all(),
622 LanguageFeatures::default(),
623 );
624 assert!(
625 compiled_package.errors.is_empty(),
626 "{:#?}",
627 compiled_package.errors
628 );
629 store.insert(compiled_package)
630 })
631 .collect::<Vec<_>>();
632
633 let dependencies = dependencies
634 .into_iter()
635 .map(|(name, code)| {
636 let source_map = SourceMap::new([(name.into(), code.into())], None);
637 let compiled_package = compile::compile(
638 &store,
639 &imports
640 .iter()
641 .map(|(alias, _)| (packages[0], Some(Arc::from(*alias))))
642 .collect::<Vec<_>>(),
643 source_map,
644 TargetCapabilityFlags::all(),
645 LanguageFeatures::default(),
646 );
647 assert!(
648 compiled_package.errors.is_empty(),
649 "{:#?}",
650 compiled_package.errors
651 );
652 store.insert(compiled_package)
653 })
654 .collect::<Vec<_>>();
655
656 let mut compiler = Compiler::new(
657 &store,
658 &dependencies
659 .iter()
660 .map(|&pkg| (pkg, Some(Arc::from("B"))))
661 .collect::<Vec<_>>(),
662 TargetCapabilityFlags::all(),
663 LanguageFeatures::default(),
664 );
665 let mut unit = CompileUnit::default();
666
667 let mut errors = Vec::new();
668
669 compiler
670 .compile_fragments(&mut unit, "UserCode", user_code, |e| -> Result<(), ()> {
671 errors = e;
672 Ok(())
673 })
674 .expect("compile_fragments should succeed");
675
676 expect!["[]"].assert_eq(&format!("{errors:#?}"));
677}
678fn check_unit(expect: &Expect, actual: &Increment) {
679 let ast = format!("ast:\n{}", actual.ast.package);
680
681 let names = format!(
682 "\nnames:\n{}",
683 actual
684 .ast
685 .names
686 .iter()
687 .fold(String::new(), |mut output, n| {
688 let _ = write!(output, "node_id:{},", n.0);
689 output
690 })
691 );
692 let terms = format!(
693 "\nterms:\n{}",
694 actual
695 .ast
696 .tys
697 .terms
698 .iter()
699 .fold(String::new(), |mut output, n| {
700 let _ = write!(output, "node_id:{},", n.0);
701 output
702 })
703 );
704 let locals = format!("\nlocals:\n{:#?}", actual.ast.locals);
705
706 let hir = format!("\nhir:\n{}", actual.hir);
707
708 expect.assert_eq(
709 &[ast, names, terms, locals, hir]
710 .into_iter()
711 .collect::<String>(),
712 );
713}
714
715fn fail_on_error(errors: Vec<Error>) -> Result<(), Vec<Error>> {
716 if !errors.is_empty() {
717 return Err(errors);
718 }
719 Ok(())
720}
721