microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
compiler/qsc_frontend/src/compile/preprocess.rs
145lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | use core::str::FromStr; |
| 5 | use qsc_ast::{ |
| 6 | ast::{Attr, ExprKind, ItemKind, Namespace, Stmt, StmtKind}, |
| 7 | mut_visit::MutVisitor, |
| 8 | }; |
| 9 | use qsc_hir::hir; |
| 10 | use std::rc::Rc; |
| 11 | |
| 12 | use super::{ConfigAttr, RuntimeCapabilityFlags}; |
| 13 | |
| 14 | #[derive(PartialEq, Hash, Clone, Debug)] |
| 15 | pub struct TrackedName { |
| 16 | pub name: Rc<str>, |
| 17 | pub namespace: Rc<str>, |
| 18 | } |
| 19 | |
| 20 | pub(crate) struct Conditional { |
| 21 | capabilities: RuntimeCapabilityFlags, |
| 22 | dropped_names: Vec<TrackedName>, |
| 23 | included_names: Vec<TrackedName>, |
| 24 | } |
| 25 | |
| 26 | impl Conditional { |
| 27 | pub(crate) fn new(capabilities: RuntimeCapabilityFlags) -> Self { |
| 28 | Self { |
| 29 | capabilities, |
| 30 | dropped_names: Vec::new(), |
| 31 | included_names: Vec::new(), |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | pub(crate) fn into_names(self) -> Vec<TrackedName> { |
| 36 | self.dropped_names |
| 37 | .into_iter() |
| 38 | .filter(|n| !self.included_names.contains(n)) |
| 39 | .collect() |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | impl MutVisitor for Conditional { |
| 44 | fn visit_namespace(&mut self, namespace: &mut Namespace) { |
| 45 | namespace.items = namespace |
| 46 | .items |
| 47 | .iter() |
| 48 | .filter_map(|item| { |
| 49 | if matches_config(&item.attrs, self.capabilities) { |
| 50 | match item.kind.as_ref() { |
| 51 | ItemKind::Callable(callable) => { |
| 52 | self.included_names.push(TrackedName { |
| 53 | name: callable.name.name.clone(), |
| 54 | namespace: namespace.name.name.clone(), |
| 55 | }); |
| 56 | } |
| 57 | ItemKind::Ty(ident, _) => self.included_names.push(TrackedName { |
| 58 | name: ident.name.clone(), |
| 59 | namespace: namespace.name.name.clone(), |
| 60 | }), |
| 61 | _ => {} |
| 62 | } |
| 63 | Some(item.clone()) |
| 64 | } else { |
| 65 | match item.kind.as_ref() { |
| 66 | ItemKind::Callable(callable) => { |
| 67 | self.dropped_names.push(TrackedName { |
| 68 | name: callable.name.name.clone(), |
| 69 | namespace: namespace.name.name.clone(), |
| 70 | }); |
| 71 | } |
| 72 | ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { |
| 73 | name: ident.name.clone(), |
| 74 | namespace: namespace.name.name.clone(), |
| 75 | }), |
| 76 | _ => {} |
| 77 | } |
| 78 | None |
| 79 | } |
| 80 | }) |
| 81 | .collect::<Vec<_>>() |
| 82 | .into_boxed_slice(); |
| 83 | } |
| 84 | |
| 85 | fn visit_stmt(&mut self, stmt: &mut Stmt) { |
| 86 | if let StmtKind::Item(item) = stmt.kind.as_mut() { |
| 87 | if matches_config(&item.attrs, self.capabilities) { |
| 88 | match item.kind.as_ref() { |
| 89 | ItemKind::Callable(callable) => { |
| 90 | self.included_names.push(TrackedName { |
| 91 | name: callable.name.name.clone(), |
| 92 | namespace: Rc::from(""), |
| 93 | }); |
| 94 | } |
| 95 | ItemKind::Ty(ident, _) => self.included_names.push(TrackedName { |
| 96 | name: ident.name.clone(), |
| 97 | namespace: Rc::from(""), |
| 98 | }), |
| 99 | _ => {} |
| 100 | } |
| 101 | } else { |
| 102 | match item.kind.as_ref() { |
| 103 | ItemKind::Callable(callable) => { |
| 104 | self.dropped_names.push(TrackedName { |
| 105 | name: callable.name.name.clone(), |
| 106 | namespace: Rc::from(""), |
| 107 | }); |
| 108 | } |
| 109 | ItemKind::Ty(ident, _) => self.dropped_names.push(TrackedName { |
| 110 | name: ident.name.clone(), |
| 111 | namespace: Rc::from(""), |
| 112 | }), |
| 113 | _ => {} |
| 114 | } |
| 115 | stmt.kind = Box::new(StmtKind::Empty); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | fn matches_config(attrs: &[Box<Attr>], capabilities: RuntimeCapabilityFlags) -> bool { |
| 122 | attrs.iter().all(|attr| { |
| 123 | if hir::Attr::from_str(attr.name.name.as_ref()) == Ok(hir::Attr::Config) { |
| 124 | if let ExprKind::Paren(inner) = attr.arg.kind.as_ref() { |
| 125 | match inner.kind.as_ref() { |
| 126 | // If there is no config attribute, then we assume that the item matches |
| 127 | // the target. We can't do membership tests on the capabilities because |
| 128 | // Base is not a subset of any capabilities, it is a lack of capabilities. |
| 129 | ExprKind::Path(path) => match ConfigAttr::from_str(path.name.name.as_ref()) { |
| 130 | Ok(ConfigAttr::Unrestricted) => capabilities.is_all(), |
| 131 | Ok(ConfigAttr::Base) => capabilities.is_empty(), |
| 132 | _ => true, |
| 133 | }, |
| 134 | _ => true, // Unknown config attribute, so we assume it matches |
| 135 | } |
| 136 | } else { |
| 137 | // Something other than a parenthesized expression, so we assume it matches |
| 138 | true |
| 139 | } |
| 140 | } else { |
| 141 | // Unknown attribute, so we assume it matches |
| 142 | true |
| 143 | } |
| 144 | }) |
| 145 | } |
| 146 | |