Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

change implementation of moduledoc and doc generator functions

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Jul 22, 2026, 8:34 AM +0100) commit 055bd0c8 parent f2b95267 change-id mqwzvlxz
+43 -71
+7 -14
compiler-core/src/erlang.rs
··· 20 20 use camino::Utf8Path; 21 21 use ecow::{EcoString, eco_format}; 22 22 use erlang_generation::{ 23 - BitArraySegmentSpecifier, ErlangBuilder, ErlangModuleName, ErlangSourceBuilder, 23 + BitArraySegmentSpecifier, DocContent, ErlangBuilder, ErlangModuleName, ErlangSourceBuilder, 24 24 }; 25 25 use itertools::Itertools; 26 26 use num_bigint::BigInt; ··· 260 260 if self.module.type_info.is_internal { 261 261 // The module is internal so we need to add a `-moduledoc(false).` 262 262 // attribute to make sure its documentation is hidden. 263 - let doc = builder.start_moduledoc_attribute(); 264 - builder.atom("false"); 265 - builder.end_doc_attribute(doc); 263 + builder.moduledoc_attribute(DocContent::False); 266 264 } else if self.module.documentation.is_empty() { 267 265 // The module is not internal, but it has no docs. 268 266 // We don't have to do anything. 269 267 } else { 270 268 // The module has some documentation that we're going to include 271 269 // with a `-moduledoc` attribute. 272 - let doc = builder.start_moduledoc_attribute(); 273 - let documentation = &self.module.documentation.iter().join("\n"); 274 - builder.string(documentation); 275 - builder.end_doc_attribute(doc); 270 + builder.moduledoc_attribute(DocContent::String( 271 + &self.module.documentation.iter().join("\n"), 272 + )); 276 273 } 277 274 } 278 275 ··· 692 689 self.module_generator.module.type_info.is_internal || function.publicity.is_internal(); 693 690 694 691 if is_internal { 695 - let attribute = builder.start_doc_attribute(); 696 - builder.atom("false"); 697 - builder.end_doc_attribute(attribute); 692 + builder.doc_attribute(DocContent::False); 698 693 } else if let Some((_, documentation)) = &function.documentation 699 694 && !documentation.is_empty() 700 695 { 701 - let attribute = builder.start_doc_attribute(); 702 - builder.string(documentation); 703 - builder.end_doc_attribute(attribute); 696 + builder.doc_attribute(DocContent::String(documentation)); 704 697 } 705 698 } 706 699
+36 -57
erlang-generation/src/lib.rs
··· 64 64 Unit(u8), 65 65 } 66 66 67 + /// This represents what can go in a moduledoc or doc comment. Either the atom 68 + /// false, if the function/module is to be hidden from the public docs, or the 69 + /// string content to include. 70 + pub enum DocContent<'string> { 71 + False, 72 + String(&'string str), 73 + } 74 + 67 75 /// A trait defining operations to describe the content of an Erlang module. 68 76 /// 69 77 /// This might look strange in places, for example why is there a `start_tuple` ··· 116 124 117 125 /// Represents an open tuple pattern that has yet to be closed. 118 126 type TuplePattern; 119 - 120 - /// Represents an open doc/moduledoc attribute. 121 - type DocAttribute; 122 127 123 128 /// Represents an open record attribute. 124 129 type RecordAttribute; ··· 201 206 exported: impl IntoIterator<Item = (Name, usize)>, 202 207 ); 203 208 204 - /// Starts a `-doc` attribute. 205 - /// What is generated after calling this function will end up inside the 206 - /// `-doc` attribute. 207 - /// You'll most likely always put a string or the atom "false" inside it. 208 - /// 209 + /// Creates a `-doc` attribute with the given content. 209 210 /// For example: 210 211 /// 211 212 /// ```ignore 212 - /// let doc = builder.start_doc_attribute(); 213 - /// builder.atom("false"); 214 - /// builder.close_doc_attribute(doc); 213 + /// todo!() 215 214 /// ``` 216 215 /// 217 216 /// Corresponds to: ··· 220 219 /// -doc(false). 221 220 /// ``` 222 221 /// 223 - fn start_doc_attribute(&mut self) -> Self::DocAttribute; 222 + fn doc_attribute(&mut self, content: DocContent<'_>); 224 223 225 - /// Starts a `-moduledoc` attribute. 226 - /// What is generated after calling this function will end up inside the 227 - /// `-moduledoc` attribute. 228 - /// You'll most likely always put a string or the atom "false" inside it. 229 - /// 224 + /// Creates a `-moduledoc` attribute with the given content. 230 225 /// For example: 231 226 /// 232 227 /// ```ignore 233 - /// let doc = builder.start_moduledoc_attribute(); 234 - /// builder.atom("false"); 235 - /// builder.close_doc_attribute(doc); 228 + /// todo!() 236 229 /// ``` 237 230 /// 238 231 /// Corresponds to: ··· 241 234 /// -moduledoc(false). 242 235 /// ``` 243 236 /// 244 - fn start_moduledoc_attribute(&mut self) -> Self::DocAttribute; 245 - 246 - /// This closes the currently open doc/moduledoc attribute. 247 - /// Code generated after this is not gonna be part of it. 248 - /// 249 - fn end_doc_attribute(&mut self, attribute: Self::DocAttribute); 237 + fn moduledoc_attribute(&mut self, content: DocContent<'_>); 250 238 251 239 /// This generates the code for a `-compile([]).` attribute where all the 252 240 /// strings produces by the given iterator are going to be passed as atom ··· 1274 1262 /// printed code from an `ErlangSourceBuilder`. 1275 1263 #[derive(Debug)] 1276 1264 enum ErlangSourceBuilderPosition { 1277 - /// We're generating a top level documentation attribute like `-doc(false)`, 1278 - /// or `-moduledoc(~"wibble wobble")`. 1279 - DocAttribute, 1280 - 1281 1265 /// We're generating a function spec like `-spec wibble(atom()) -> atom()`. 1282 1266 FunctionSpec, 1283 1267 ··· 1691 1675 type ClauseBody = (); 1692 1676 type ClauseGuards = (); 1693 1677 type ClausePattern = (); 1694 - type DocAttribute = (); 1695 1678 type Function = (); 1696 1679 type FunctionSpec = (); 1697 1680 type FunctionType = (); ··· 1778 1761 self.code.push_str("]).\n"); 1779 1762 } 1780 1763 1781 - fn start_doc_attribute(&mut self) -> Self::DocAttribute { 1764 + fn doc_attribute(&mut self, content: DocContent<'_>) { 1782 1765 self.new_top_level_form(); 1783 1766 self.code.push_str("-doc("); 1784 - self.position 1785 - .push(ErlangSourceBuilderPosition::DocAttribute); 1767 + self.doc_content(content); 1768 + self.code.push_str(").\n") 1786 1769 } 1787 1770 1788 - fn start_moduledoc_attribute(&mut self) -> Self::DocAttribute { 1771 + fn moduledoc_attribute(&mut self, content: DocContent<'_>) { 1789 1772 self.new_top_level_form(); 1790 1773 self.code.push_str("-moduledoc("); 1791 - self.position 1792 - .push(ErlangSourceBuilderPosition::DocAttribute); 1793 - } 1794 - 1795 - fn end_doc_attribute(&mut self, _attribute: Self::DocAttribute) { 1796 - self.close_currently_open_item(); 1774 + self.doc_content(content); 1775 + self.code.push_str(").\n") 1797 1776 } 1798 1777 1799 1778 fn compile_attribute<'a>(&mut self, arguments: impl IntoIterator<Item = &'a str>) { ··· 2485 2464 }; 2486 2465 2487 2466 match position { 2488 - ErlangSourceBuilderPosition::DocAttribute => (), 2489 - 2490 2467 ErlangSourceBuilderPosition::RecordField { 2491 2468 expected: expected @ ExpectedRecordFieldItem::Name, 2492 2469 } => *expected = ExpectedRecordFieldItem::Type, ··· 2755 2732 | ErlangSourceBuilderPosition::List { .. } 2756 2733 | ErlangSourceBuilderPosition::FunctionStatement { .. } 2757 2734 | ErlangSourceBuilderPosition::AnonymousFunctionStatement { .. } 2758 - | ErlangSourceBuilderPosition::DocAttribute 2759 2735 | ErlangSourceBuilderPosition::UnaryOperator 2760 2736 | ErlangSourceBuilderPosition::Case { .. } 2761 2737 | ErlangSourceBuilderPosition::BinaryOperator { .. } ··· 2894 2870 | ErlangSourceBuilderPosition::UnionType { .. } 2895 2871 | ErlangSourceBuilderPosition::TupleType { .. } 2896 2872 | ErlangSourceBuilderPosition::RecordField { .. } 2897 - | ErlangSourceBuilderPosition::DocAttribute 2898 2873 | ErlangSourceBuilderPosition::RecordAttribute { .. } 2899 2874 | ErlangSourceBuilderPosition::List { 2900 2875 kind: ListKind::Pattern, ··· 3019 2994 } 3020 2995 // There's nothing left to do when a union type ends. 3021 2996 ErlangSourceBuilderPosition::UnionType { .. } => (), 3022 - // When a doc attribute is closed we need to add the closed 3023 - // parentheses and a newline. 3024 - ErlangSourceBuilderPosition::DocAttribute => self.code.push_str(").\n"), 3025 2997 // When a case expression is over, we need to add the closing `end`. 3026 2998 ErlangSourceBuilderPosition::Case { 3027 2999 expected: ExpectedCaseItem::Branches { .. }, ··· 3163 3135 }) 3164 3136 .into() 3165 3137 } 3166 - 3167 - ErlangSourceBuilderPosition::DocAttribute => { 3168 - // Escaping strings generated inside doc attributes is a little 3169 - // different: since their content doesn't come from a Gleam 3170 - // literal string but freeform text, their content might contain 3171 - // all sorts of unescaped characters. 3172 - content.replace("\\", "\\\\").replace("\"", "\\\"") 3173 - } 3174 3138 } 3175 3139 } 3176 3140 ··· 3410 3374 | ErlangSourceBuilderPosition::TupleType { .. } 3411 3375 | ErlangSourceBuilderPosition::FunctionStatement { .. } 3412 3376 | ErlangSourceBuilderPosition::AnonymousFunctionStatement { .. } 3413 - | ErlangSourceBuilderPosition::DocAttribute 3414 3377 | ErlangSourceBuilderPosition::FunctionSpec 3415 3378 | ErlangSourceBuilderPosition::Tuple { .. } 3416 3379 | ErlangSourceBuilderPosition::TuplePattern { .. } ··· 3495 3458 fn error_with_position(&self, expected: &str) -> String { 3496 3459 let position = self.position.last(); 3497 3460 format!("tried {expected}, position: {position:?}") 3461 + } 3462 + 3463 + fn doc_content(&mut self, content: DocContent<'_>) { 3464 + match content { 3465 + DocContent::False => self.code.push_str("false"), 3466 + // Escaping strings generated inside doc attributes is a little 3467 + // different: since their content doesn't come from a Gleam 3468 + // literal string but freeform text, their content might contain 3469 + // all sorts of unescaped characters. 3470 + DocContent::String(content) => { 3471 + self.code.push_str("~\""); 3472 + self.code 3473 + .push_str(&content.replace("\\", "\\\\").replace("\"", "\\\"")); 3474 + self.code.push('"'); 3475 + } 3476 + } 3498 3477 } 3499 3478 } 3500 3479