microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billt/revert-mimalloc

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_doc_gen/src/display.rs

666lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use qsc_ast::ast;
5use qsc_frontend::resolve;
6use qsc_hir::{
7 hir::{self, PackageId},
8 ty::{self, GenericParam},
9};
10use regex_lite::Regex;
11use std::{
12 fmt::{Display, Formatter, Result},
13 rc::Rc,
14};
15
16/// Trait describing a struct capable of resolving various ids found in the AST and HIR.
17pub trait Lookup {
18 /// Looks up the type of a node in user code
19 fn get_ty(&self, expr_id: ast::NodeId) -> Option<&ty::Ty>;
20
21 /// Looks up the resolution of a node in user code
22 fn get_res(&self, id: ast::NodeId) -> Option<&resolve::Res>;
23
24 /// Returns the hir `Item` node referred to by `item_id`,
25 /// along with the `Package` and `PackageId` for the package
26 /// that it was found in.
27 fn resolve_item_relative_to_user_package(
28 &self,
29 item_id: &hir::ItemId,
30 ) -> (&hir::Item, &hir::Package, hir::ItemId);
31
32 /// Returns the hir `Item` node referred to by `res`.
33 /// `Res`s can resolve to external packages, and the references
34 /// are relative, so here we also need the
35 /// local `PackageId` that the `res` itself came from.
36 fn resolve_item_res(
37 &self,
38 local_package_id: PackageId,
39 res: &hir::Res,
40 ) -> (&hir::Item, hir::ItemId);
41
42 /// Returns the hir `Item` node referred to by `item_id`.
43 /// `ItemId`s can refer to external packages, and the references
44 /// are relative, so here we also need the local `PackageId`
45 /// that the `ItemId` originates from.
46 fn resolve_item(
47 &self,
48 local_package_id: PackageId,
49 item_id: &hir::ItemId,
50 ) -> (&hir::Item, &hir::Package, hir::ItemId);
51}
52
53pub struct CodeDisplay<'a> {
54 pub compilation: &'a dyn Lookup,
55}
56
57#[allow(clippy::unused_self)]
58impl<'a> CodeDisplay<'a> {
59 #[must_use]
60 pub fn hir_callable_decl(&self, decl: &'a hir::CallableDecl) -> impl Display + '_ {
61 HirCallableDecl { decl }
62 }
63
64 #[must_use]
65 pub fn ast_callable_decl(&self, decl: &'a ast::CallableDecl) -> impl Display + '_ {
66 AstCallableDecl {
67 lookup: self.compilation,
68 decl,
69 }
70 }
71
72 #[must_use]
73 pub fn name_ty_id(&self, name: &'a str, ty_id: ast::NodeId) -> impl Display + '_ {
74 NameTyId {
75 lookup: self.compilation,
76 name,
77 ty_id,
78 }
79 }
80
81 #[must_use]
82 pub fn ident_ty(&self, ident: &'a ast::Ident, ty: &'a ast::Ty) -> impl Display + '_ {
83 IdentTy { ident, ty }
84 }
85
86 #[must_use]
87 pub fn ident_ty_def(&self, ident: &'a ast::Ident, def: &'a ast::TyDef) -> impl Display + 'a {
88 IdentTyDef { ident, def }
89 }
90
91 #[must_use]
92 pub fn hir_udt(&self, udt: &'a ty::Udt) -> impl Display + '_ {
93 HirUdt { udt }
94 }
95
96 #[must_use]
97 pub fn hir_pat(&self, pat: &'a hir::Pat) -> impl Display + '_ {
98 HirPat { pat }
99 }
100
101 #[must_use]
102 pub fn get_param_offset(&self, decl: &hir::CallableDecl) -> u32 {
103 HirCallableDecl { decl }.get_param_offset()
104 }
105
106 // The rest of the display implementations are not made public b/c they're not used,
107 // but there's no reason they couldn't be
108}
109
110// Display impls for each syntax/hir element we may encounter
111
112struct IdentTy<'a> {
113 ident: &'a ast::Ident,
114 ty: &'a ast::Ty,
115}
116
117impl<'a> Display for IdentTy<'a> {
118 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
119 write!(f, "{} : {}", self.ident.name, AstTy { ty: self.ty },)
120 }
121}
122
123struct NameTyId<'a> {
124 lookup: &'a dyn Lookup,
125 name: &'a str,
126 ty_id: ast::NodeId,
127}
128
129impl<'a> Display for NameTyId<'a> {
130 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
131 write!(
132 f,
133 "{} : {}",
134 self.name,
135 TyId {
136 lookup: self.lookup,
137 ty_id: self.ty_id,
138 },
139 )
140 }
141}
142
143struct HirCallableDecl<'a> {
144 decl: &'a hir::CallableDecl,
145}
146
147impl HirCallableDecl<'_> {
148 fn get_param_offset(&self) -> u32 {
149 let offset = match self.decl.kind {
150 hir::CallableKind::Function => "function".len(),
151 hir::CallableKind::Operation => "operation".len(),
152 } + 1 // this is for the space between keyword and name
153 + self.decl.name.name.len()
154 + display_type_params(&self.decl.generics).len();
155
156 u32::try_from(offset)
157 .expect("failed to cast usize to u32 while calculating parameter offset")
158 }
159}
160
161impl Display for HirCallableDecl<'_> {
162 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
163 let kind = match self.decl.kind {
164 hir::CallableKind::Function => "function",
165 hir::CallableKind::Operation => "operation",
166 };
167
168 write!(f, "{} {}", kind, self.decl.name.name)?;
169 let type_params = display_type_params(&self.decl.generics);
170 write!(f, "{type_params}")?;
171 let input = HirPat {
172 pat: &self.decl.input,
173 };
174 if matches!(self.decl.input.kind, hir::PatKind::Tuple(_)) {
175 write!(f, "{input}")?;
176 } else {
177 write!(f, "({input})")?;
178 }
179 write!(
180 f,
181 " : {}{}",
182 self.decl.output.display(),
183 FunctorSetValue {
184 functors: self.decl.functors,
185 },
186 )
187 }
188}
189
190struct AstCallableDecl<'a> {
191 lookup: &'a dyn Lookup,
192 decl: &'a ast::CallableDecl,
193}
194
195impl<'a> Display for AstCallableDecl<'a> {
196 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
197 let kind = match self.decl.kind {
198 ast::CallableKind::Function => "function",
199 ast::CallableKind::Operation => "operation",
200 };
201
202 let functors = ast_callable_functors(self.decl);
203 let functors = FunctorSetValue { functors };
204
205 write!(f, "{} {}", kind, self.decl.name.name)?;
206 if !self.decl.generics.is_empty() {
207 let type_params = self
208 .decl
209 .generics
210 .iter()
211 .map(|p| p.name.clone())
212 .collect::<Vec<_>>()
213 .join(", ");
214 write!(f, "<{type_params}>")?;
215 }
216 let input = AstPat {
217 pat: &self.decl.input,
218 lookup: self.lookup,
219 };
220 if matches!(*self.decl.input.kind, ast::PatKind::Tuple(_)) {
221 write!(f, "{input}")?;
222 } else {
223 write!(f, "({input})")?;
224 }
225 write!(
226 f,
227 " : {}{}",
228 AstTy {
229 ty: &self.decl.output
230 },
231 functors,
232 )
233 }
234}
235
236struct HirPat<'a> {
237 pat: &'a hir::Pat,
238}
239
240impl<'a> Display for HirPat<'a> {
241 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
242 match &self.pat.kind {
243 hir::PatKind::Bind(name) => write!(f, "{} : {}", name.name, self.pat.ty.display()),
244 hir::PatKind::Discard => write!(f, "_ : {}", self.pat.ty.display()),
245 hir::PatKind::Tuple(items) => {
246 let mut elements = items.iter();
247 if let Some(elem) = elements.next() {
248 write!(f, "({}", HirPat { pat: elem })?;
249 for elem in elements {
250 write!(f, ", {}", HirPat { pat: elem })?;
251 }
252 write!(f, ")")
253 } else {
254 write!(f, "()")
255 }
256 }
257 hir::PatKind::Err => write!(f, "?"),
258 }
259 }
260}
261
262struct AstPat<'a> {
263 lookup: &'a dyn Lookup,
264 pat: &'a ast::Pat,
265}
266
267impl<'a> Display for AstPat<'a> {
268 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
269 match &*self.pat.kind {
270 ast::PatKind::Bind(ident, anno) => match anno {
271 Some(ty) => write!(f, "{}", IdentTy { ident, ty }),
272 None => write!(
273 f,
274 "{}",
275 NameTyId {
276 lookup: self.lookup,
277 name: &ident.name,
278 ty_id: self.pat.id
279 }
280 ),
281 },
282 ast::PatKind::Discard(anno) => match anno {
283 Some(ty) => write!(f, "{}", AstTy { ty }),
284 None => write!(
285 f,
286 "_ : {}",
287 TyId {
288 lookup: self.lookup,
289 ty_id: self.pat.id,
290 }
291 ),
292 },
293 ast::PatKind::Elided => write!(f, "..."),
294 ast::PatKind::Paren(item) => write!(
295 f,
296 "{}",
297 AstPat {
298 lookup: self.lookup,
299 pat: item,
300 }
301 ),
302 ast::PatKind::Tuple(items) => {
303 let mut elements = items.iter();
304 if let Some(elem) = elements.next() {
305 write!(
306 f,
307 "({}",
308 AstPat {
309 lookup: self.lookup,
310 pat: elem,
311 }
312 )?;
313 for elem in elements {
314 write!(
315 f,
316 ", {}",
317 AstPat {
318 lookup: self.lookup,
319 pat: elem,
320 }
321 )?;
322 }
323 write!(f, ")")
324 } else {
325 write!(f, "()")
326 }
327 }
328 ast::PatKind::Err => write!(f, "?"),
329 }
330 }
331}
332
333struct IdentTyDef<'a> {
334 ident: &'a ast::Ident,
335 def: &'a ast::TyDef,
336}
337
338impl<'a> Display for IdentTyDef<'a> {
339 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
340 write!(
341 f,
342 "newtype {} = {}",
343 self.ident.name,
344 TyDef { def: self.def }
345 )
346 }
347}
348
349struct HirUdt<'a> {
350 udt: &'a ty::Udt,
351}
352
353impl<'a> Display for HirUdt<'a> {
354 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
355 let udt_def = UdtDef::new(&self.udt.definition);
356 write!(f, "newtype {} = {}", self.udt.name, udt_def)
357 }
358}
359
360struct UdtDef<'a> {
361 name: Option<Rc<str>>,
362 kind: UdtDefKind<'a>,
363}
364
365enum UdtDefKind<'a> {
366 SingleTy(&'a ty::Ty),
367 TupleTy(Vec<UdtDef<'a>>),
368}
369
370impl<'a> UdtDef<'a> {
371 pub fn new(def: &'a ty::UdtDef) -> Self {
372 match &def.kind {
373 ty::UdtDefKind::Field(field) => UdtDef {
374 name: field.name.as_ref().cloned(),
375 kind: UdtDefKind::SingleTy(&field.ty),
376 },
377 ty::UdtDefKind::Tuple(defs) => UdtDef {
378 name: None,
379 kind: UdtDefKind::TupleTy(defs.iter().map(UdtDef::new).collect()),
380 },
381 }
382 }
383}
384
385impl Display for UdtDef<'_> {
386 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
387 if let Some(name) = &self.name {
388 write!(f, "{name}: ")?;
389 }
390
391 match &self.kind {
392 UdtDefKind::SingleTy(ty) => {
393 write!(f, "{}", ty.display())
394 }
395 UdtDefKind::TupleTy(defs) => fmt_tuple(f, defs, |def| def),
396 }
397 }
398}
399
400struct FunctorSet<'a> {
401 functor_set: &'a ty::FunctorSet,
402}
403
404impl<'a> Display for FunctorSet<'a> {
405 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
406 if *self.functor_set == ty::FunctorSet::Value(ty::FunctorSetValue::Empty) {
407 Ok(())
408 } else {
409 write!(f, " is {}", self.functor_set)
410 }
411 }
412}
413
414struct FunctorSetValue {
415 functors: ty::FunctorSetValue,
416}
417
418impl Display for FunctorSetValue {
419 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
420 if let ty::FunctorSetValue::Empty = self.functors {
421 Ok(())
422 } else {
423 write!(f, " is {}", self.functors)
424 }
425 }
426}
427
428struct TyId<'a> {
429 lookup: &'a dyn Lookup,
430 ty_id: ast::NodeId,
431}
432
433impl<'a> Display for TyId<'a> {
434 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
435 if let Some(ty) = self.lookup.get_ty(self.ty_id) {
436 write!(f, "{}", ty.display())
437 } else {
438 write!(f, "?")
439 }
440 }
441}
442
443struct AstTy<'a> {
444 ty: &'a ast::Ty,
445}
446
447impl<'a> Display for AstTy<'a> {
448 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
449 match self.ty.kind.as_ref() {
450 ast::TyKind::Array(ty) => write!(f, "{}[]", AstTy { ty }),
451 ast::TyKind::Arrow(kind, input, output, functors) => {
452 let arrow = match kind {
453 ast::CallableKind::Function => "->",
454 ast::CallableKind::Operation => "=>",
455 };
456 write!(
457 f,
458 "({} {} {}{})",
459 AstTy { ty: input },
460 arrow,
461 AstTy { ty: output },
462 FunctorExpr { functors }
463 )
464 }
465 ast::TyKind::Hole => write!(f, "_"),
466 ast::TyKind::Paren(ty) => write!(f, "{}", AstTy { ty }),
467 ast::TyKind::Path(path) => write!(f, "{}", AstPath { path }),
468 ast::TyKind::Param(id) => write!(f, "{}", id.name),
469 ast::TyKind::Tuple(tys) => fmt_tuple(f, tys, |ty| AstTy { ty }),
470 ast::TyKind::Err => write!(f, "?"),
471 }
472 }
473}
474
475struct FunctorExpr<'a> {
476 functors: &'a Option<Box<ast::FunctorExpr>>,
477}
478
479impl<'a> Display for FunctorExpr<'a> {
480 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
481 match self.functors {
482 Some(functors) => {
483 let functors = eval_functor_expr(functors);
484 write!(f, "{}", FunctorSetValue { functors })
485 }
486 None => Ok(()),
487 }
488 }
489}
490
491struct AstPath<'a> {
492 path: &'a ast::Path,
493}
494
495impl<'a> Display for AstPath<'a> {
496 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
497 match self.path.namespace.as_ref() {
498 Some(ns) => write!(f, "{ns}.{}", self.path.name.name),
499 None => write!(f, "{}", self.path.name.name),
500 }
501 }
502}
503
504struct TyDef<'a> {
505 def: &'a ast::TyDef,
506}
507
508impl<'a> Display for TyDef<'a> {
509 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
510 match self.def.kind.as_ref() {
511 ast::TyDefKind::Field(name, ty) => match name {
512 Some(name) => write!(f, "{} : {}", name.name, AstTy { ty }),
513 None => write!(f, "{}", AstTy { ty }),
514 },
515 ast::TyDefKind::Paren(def) => write!(f, "{}", TyDef { def }),
516 ast::TyDefKind::Tuple(tys) => fmt_tuple(f, tys, |def| TyDef { def }),
517 ast::TyDefKind::Err => write!(f, "?"),
518 }
519 }
520}
521
522fn fmt_tuple<'a, 'b, D, I>(
523 formatter: &'a mut Formatter,
524 elements: &'b [I],
525 map: impl Fn(&'b I) -> D,
526) -> Result
527where
528 D: Display,
529{
530 let mut elements = elements.iter();
531 if let Some(elem) = elements.next() {
532 write!(formatter, "({}", map(elem))?;
533 if elements.len() == 0 {
534 write!(formatter, ",)")?;
535 } else {
536 for elem in elements {
537 write!(formatter, ", {}", map(elem))?;
538 }
539 write!(formatter, ")")?;
540 }
541 } else {
542 write!(formatter, "Unit")?;
543 }
544 Ok(())
545}
546
547fn display_type_params(generics: &[GenericParam]) -> String {
548 let type_params = generics
549 .iter()
550 .filter_map(|generic| match generic {
551 GenericParam::Ty(name) => Some(name.name.clone()),
552 GenericParam::Functor(_) => None,
553 })
554 .collect::<Vec<_>>()
555 .join(", ");
556 if type_params.is_empty() {
557 type_params
558 } else {
559 format!("<{type_params}>")
560 }
561}
562
563//
564// helpers that don't manipulate any strings
565//
566
567fn ast_callable_functors(callable: &ast::CallableDecl) -> ty::FunctorSetValue {
568 let mut functors = callable
569 .functors
570 .as_ref()
571 .map_or(ty::FunctorSetValue::Empty, |f| {
572 eval_functor_expr(f.as_ref())
573 });
574
575 if let ast::CallableBody::Specs(specs) = callable.body.as_ref() {
576 for spec in specs.iter() {
577 let spec_functors = match spec.spec {
578 ast::Spec::Body => ty::FunctorSetValue::Empty,
579 ast::Spec::Adj => ty::FunctorSetValue::Adj,
580 ast::Spec::Ctl => ty::FunctorSetValue::Ctl,
581 ast::Spec::CtlAdj => ty::FunctorSetValue::CtlAdj,
582 };
583 functors = functors.union(&spec_functors);
584 }
585 }
586
587 functors
588}
589
590fn eval_functor_expr(expr: &ast::FunctorExpr) -> ty::FunctorSetValue {
591 match expr.kind.as_ref() {
592 ast::FunctorExprKind::BinOp(op, lhs, rhs) => {
593 let lhs_functors = eval_functor_expr(lhs);
594 let rhs_functors = eval_functor_expr(rhs);
595 match op {
596 ast::SetOp::Union => lhs_functors.union(&rhs_functors),
597 ast::SetOp::Intersect => lhs_functors.intersect(&rhs_functors),
598 }
599 }
600 ast::FunctorExprKind::Lit(ast::Functor::Adj) => ty::FunctorSetValue::Adj,
601 ast::FunctorExprKind::Lit(ast::Functor::Ctl) => ty::FunctorSetValue::Ctl,
602 ast::FunctorExprKind::Paren(inner) => eval_functor_expr(inner),
603 }
604}
605
606//
607// parsing functions for working with doc comments
608//
609
610/// Takes a doc string from Q# and increases all of the markdown header levels by one level.
611/// i.e. `# Summary` becomes `## Summary`
612#[must_use]
613pub fn increase_header_level(doc: &str) -> String {
614 let re = Regex::new(r"(?mi)^(#+)( [\s\S]+?)$").expect("Invalid regex");
615 re.replace_all(doc, "$1#$2").to_string()
616}
617
618/// Takes a doc string from Q# and returns the contents of the `# Summary` section. If no
619/// such section can be found, returns the original doc string.
620#[must_use]
621pub fn parse_doc_for_summary(doc: &str) -> String {
622 let re = Regex::new(r"(?mi)(?:^# Summary$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
623 match re.captures(doc) {
624 Some(captures) => {
625 let capture = captures
626 .get(1)
627 .expect("Didn't find the capture for the given regex");
628 capture.as_str()
629 }
630 None => doc,
631 }
632 .trim()
633 .to_string()
634}
635
636/// Takes a doc string from a Q# callable and the name of a parameter of
637/// that callable. Returns the description of that parameter found in the
638/// doc string. If no description is found, returns the empty string.
639#[must_use]
640pub fn parse_doc_for_param(doc: &str, param: &str) -> String {
641 let re = Regex::new(r"(?mi)(?:^# Input$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
642 let input = match re.captures(doc) {
643 Some(captures) => {
644 let capture = captures
645 .get(1)
646 .expect("Didn't find the capture for the given regex");
647 capture.as_str()
648 }
649 None => return String::new(),
650 }
651 .trim();
652
653 let re = Regex::new(format!(r"(?mi)(?:^## {param}$)([\s\S]*?)(?:(^(#|##) .*)|\z)").as_str())
654 .expect("Invalid regex");
655 match re.captures(input) {
656 Some(captures) => {
657 let capture = captures
658 .get(1)
659 .expect("Didn't find the capture for the given regex");
660 capture.as_str()
661 }
662 None => return String::new(),
663 }
664 .trim()
665 .to_string()
666}
667