microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_doc_gen/src/display.rs

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