mt-lang 0.2.8 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fcc2383591d95c6043caebcb271aba6b70650528dafbb1a3cd8b906e74a5a6b6
4
- data.tar.gz: 2e264acc4e563b29caf921062ca9156f92869d101b1f16f6516f9b412061a1c9
3
+ metadata.gz: 440292bdf9bb8c311ff6172c6b063defbae416862b2f3369b70c4249bcc09f37
4
+ data.tar.gz: 4793a630eff4f1672659c682e3fb9df769e6d7aaf20facc3742216b7af8fe388
5
5
  SHA512:
6
- metadata.gz: 17ef473e87741d685323aa279e434ce56a24b7c30cc4b48f61cbb7d4b7c49d47300c7cbad1e4211740fc35ecc29bf36ee941fa0b4bb18ac0fc19d04aee69d7ab
7
- data.tar.gz: 9952dc4f2a306b443cbd57c4840965c8c7e870f8f9f976643b063635c64e371c0f8e903ea3446b124cd0b7e9ddf6754807c924e90426c1bfe789124bc08d6d44
6
+ metadata.gz: ba82f455b038f4bfcf2b3d2748db4327fec835bf676f854578aca8c21f5614836b693385c70679cbece42917a7a2093a405029f4e6b7a7fa9dcb2c0a1af13257
7
+ data.tar.gz: 51f8316e7466ae58c141168a3f350c2104fc61a859d41e1a21316a7d56c11d7ea0ca49113f7c6aa69fe9bf2a06c055ff6d6d94fa0d931096d76ddc1e38610ccd
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.2.8"
6
+ VERSION = "0.2.11"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -227,8 +227,8 @@ module MilkTea
227
227
  RangeExpr = Data.define(:start_expr, :end_expr, :line, :column)
228
228
  ExpressionList = Data.define(:elements, :line, :column)
229
229
  IfExpr = Data.define(:condition, :then_expression, :else_expression)
230
- MatchExpr = Data.define(:expression, :arms, :line, :column, :length) do
231
- def initialize(expression:, arms:, line: nil, column: nil, length: nil) = super
230
+ MatchExpr = Data.define(:expression, :arms, :line, :column, :length, :desugared_from_is) do
231
+ def initialize(expression:, arms:, line: nil, column: nil, length: nil, desugared_from_is: false) = super
232
232
  end
233
233
  UnsafeExpr = Data.define(:expression, :line, :column, :length) do
234
234
  def initialize(expression:, line: nil, column: nil, length: nil) = super
@@ -141,6 +141,7 @@ module MilkTea
141
141
  line:,
142
142
  column:,
143
143
  length: previous.lexeme.length,
144
+ desugared_from_is: true,
144
145
  )
145
146
  end
146
147
  expr
@@ -87,6 +87,7 @@ module MilkTea
87
87
 
88
88
  def parse_local_decl(kind)
89
89
  line = previous.line
90
+ column = previous.column
90
91
  name_token = nil
91
92
  name = nil
92
93
  var_type = nil
@@ -145,7 +146,7 @@ module MilkTea
145
146
  consume_end_of_statement
146
147
  end
147
148
 
148
- AST::LocalDecl.new(kind:, name:, type: var_type, value:, else_binding:, else_body:, line:, column: name_token&.column || line, destructure_bindings:, destructure_type_name:)
149
+ AST::LocalDecl.new(kind:, name:, type: var_type, value:, else_binding:, else_body:, line:, column: name_token&.column || column, destructure_bindings:, destructure_type_name:)
149
150
  rescue ParseError => e
150
151
  raise unless @recovery_errors && name
151
152
 
@@ -8,19 +8,270 @@ module MilkTea
8
8
 
9
9
  KEYWORD_HOVER_INFO = {
10
10
  'size_of' => {
11
- signature: 'size_of(expr) -> int',
12
- docs: '`size_of(expr)` evaluates the compile-time size (in bytes) of the expression\'s type.',
11
+ signature: 'size_of(Type) -> ptr_uint',
12
+ docs: '`size_of(Type)` evaluates the compile-time size (in bytes) of the type.',
13
13
  },
14
14
  'align_of' => {
15
- signature: 'align_of(expr) -> int',
16
- docs: '`align_of(expr)` evaluates the compile-time alignment (in bytes) of the expression\'s type.',
15
+ signature: 'align_of(Type) -> ptr_uint',
16
+ docs: '`align_of(Type)` evaluates the compile-time alignment (in bytes) of the type.',
17
17
  },
18
18
  'offset_of' => {
19
- signature: 'offset_of(Type, field) -> int',
19
+ signature: 'offset_of(Type, field) -> ptr_uint',
20
20
  docs: '`offset_of(Type, field)` evaluates the compile-time byte offset of `field` within `Type`.',
21
21
  },
22
22
  }.freeze
23
23
 
24
+ LANGUAGE_KEYWORD_HOVER_INFO = {
25
+ 'let' => {
26
+ signature: 'let',
27
+ docs: 'Immutable local variable declaration: `let name = value` or `let name: Type`. Supports `else` guard with `T?`, `Option[T]`, or `Result[T, E]`.',
28
+ },
29
+ 'var' => {
30
+ signature: 'var',
31
+ docs: 'Mutable local or module-level variable: `var name: Type = initializer`. Supports `else` guard. Module `var` requires explicit type.',
32
+ },
33
+ 'const' => {
34
+ signature: 'const',
35
+ docs: 'Compile-time constant, requires explicit type and initializer. Block-bodied form `const NAME -> TYPE:` evaluates at compile time.',
36
+ },
37
+ 'function' => {
38
+ signature: 'function',
39
+ docs: 'Ordinary function declaration: `function name(params) -> ReturnType:`. Parameters are non-rebindable. Return type defaults to `void`.',
40
+ },
41
+ 'async' => {
42
+ signature: 'async',
43
+ docs: 'Marks a function as async; the declared return type is lifted to `Task[T]`. Use `await` inside async functions.',
44
+ },
45
+ 'await' => {
46
+ signature: 'await',
47
+ docs: 'Awaits a `Task[T]` inside an async function, yielding the unwrapped `T`. Only allowed in async function bodies.',
48
+ },
49
+ 'struct' => {
50
+ signature: 'struct',
51
+ docs: 'Value-type struct with named, typed fields: `struct Name: field: Type`. Supports generics, nested structs, and `implements`.',
52
+ },
53
+ 'enum' => {
54
+ signature: 'enum',
55
+ docs: 'Integer-backed enumeration: `enum Name: BackingType`. Backing type defaults to `int`. Values auto-increment from 0 or the last explicit value.',
56
+ },
57
+ 'flags' => {
58
+ signature: 'flags',
59
+ docs: 'Bitmask type backed by an integer primitive: `flags Name: uint`. Members must be compile-time integer constants.',
60
+ },
61
+ 'union' => {
62
+ signature: 'union',
63
+ docs: 'Overlapped storage union: `union Name: field1: Type1, field2: Type2`. All fields share the same memory.',
64
+ },
65
+ 'variant' => {
66
+ signature: 'variant',
67
+ docs: 'Tagged union: `variant Name: arm1(field: Type), arm2`. Arms may carry named payload fields. No-payload arms are bare identifiers.',
68
+ },
69
+ 'opaque' => {
70
+ signature: 'opaque',
71
+ docs: 'Externally-defined type with unknown layout: `opaque Name`. May declare `implements` for interface conformance.',
72
+ },
73
+ 'type' => {
74
+ signature: 'type',
75
+ docs: 'Type alias: `type Name = ExistingType`. Generic type parameters are supported.',
76
+ },
77
+ 'interface' => {
78
+ signature: 'interface',
79
+ docs: 'Declares a method contract: `interface Name: function method(params) -> T`. Implemented via `implements`; used at runtime via `dyn[Interface]`.',
80
+ },
81
+ 'extending' => {
82
+ signature: 'extending',
83
+ docs: 'Extends an existing type with methods: `extending Type: function method():`. Supports `function`, `editable function`, and `static function`.',
84
+ },
85
+ 'implements' => {
86
+ signature: 'implements',
87
+ docs: 'Nominal interface conformance on `struct` or `opaque`: `struct Foo implements Interface`. Must be declared on the type definition.',
88
+ },
89
+ 'editable' => {
90
+ signature: 'editable',
91
+ docs: 'Method receiver modifier: `editable function`. Grants mutable access to `this`. Used in interfaces and extending blocks.',
92
+ },
93
+ 'static' => {
94
+ signature: 'static',
95
+ docs: 'Static method modifier: `static function`. No `this` receiver. Used in interfaces and extending blocks for constructors and utilities.',
96
+ },
97
+ 'import' => {
98
+ signature: 'import',
99
+ docs: 'Module import: `import module.path` or `import module.path as alias`. Module lookup resolves `a.b.c` to `a/b/c.mt`.',
100
+ },
101
+ 'public' => {
102
+ signature: 'public',
103
+ docs: 'Export visibility modifier: `public function`, `public type`, etc. Rejected on `extending`, `external`, and `static_assert` declarations.',
104
+ },
105
+ 'if' => {
106
+ signature: 'if',
107
+ docs: 'Conditional branch: `if condition:`. Condition must be `bool`. Supports `else if` and `else`. Inline form: `if cond: stmt else: stmt`.',
108
+ },
109
+ 'else' => {
110
+ signature: 'else',
111
+ docs: 'Else branch for `if`. Chains as `else if condition:` for additional branches. Supports inline single-statement form `else: stmt`.',
112
+ },
113
+ 'match' => {
114
+ signature: 'match',
115
+ docs: 'Pattern match on enum, variant, integer, `str`, or tuple. Expression form produces a value. Must be exhaustive without `_` wildcard.',
116
+ },
117
+ 'for' => {
118
+ signature: 'for',
119
+ docs: 'Loop: `for i in 0..count:` (exclusive range) or `for item in iterable:`. Multi-iteration `for left, right in xs, ys:` iterates arrays/spans in lockstep.',
120
+ },
121
+ 'while' => {
122
+ signature: 'while',
123
+ docs: 'Conditional loop: `while condition:`. Condition must be `bool`. Supports `break`, `continue`, and `defer` inside the body.',
124
+ },
125
+ 'return' => {
126
+ signature: 'return',
127
+ docs: 'Returns a value from a function. Not allowed inside `defer` blocks.',
128
+ },
129
+ 'break' => {
130
+ signature: 'break',
131
+ docs: 'Exits the nearest enclosing `for`, `while`, or `parallel for` loop.',
132
+ },
133
+ 'continue' => {
134
+ signature: 'continue',
135
+ docs: 'Skips to the next iteration of the nearest enclosing `for` or `while` loop.',
136
+ },
137
+ 'defer' => {
138
+ signature: 'defer',
139
+ docs: 'Defers an expression or block to function exit: `defer expr` or `defer:`. Multiple defers execute in LIFO order. `return` is not allowed inside.',
140
+ },
141
+ 'unsafe' => {
142
+ signature: 'unsafe',
143
+ docs: 'Required for pointer indexing, raw pointer dereference, pointer arithmetic, pointer casts, and `reinterpret[...]`. Single-expr or block form.',
144
+ },
145
+ 'external' => {
146
+ signature: 'external',
147
+ docs: 'Raw C ABI surface: `external function name(params) -> T`. No body, supports variadic `...`. Also marks external files with `external` header.',
148
+ },
149
+ 'foreign' => {
150
+ signature: 'foreign',
151
+ docs: 'Foreign function bridging: `foreign function name(params) -> T = c.FuncName`. Supports `in`, `out`, `inout`, and `consuming` parameter modes.',
152
+ },
153
+ 'consuming' => {
154
+ signature: 'consuming',
155
+ docs: 'Foreign function parameter mode: takes ownership of the argument. The caller\'s binding is consumed. Only on `foreign function` params.',
156
+ },
157
+ 'when' => {
158
+ signature: 'when',
159
+ docs: 'Compile-time conditional: `when CONSTANT:`. Only the chosen branch is type-checked and emitted. Requires `else` unless exhaustive.',
160
+ },
161
+ 'inline' => {
162
+ signature: 'inline',
163
+ docs: 'Compile-time unrolling modifier: `inline for` (loop unrolling), `inline while`, `inline match`, `inline if`. Only the active branch emits code.',
164
+ },
165
+ 'emit' => {
166
+ signature: 'emit',
167
+ docs: 'Emits a declaration at compile time: `emit function ...`. Only allowed inside `const function` or `inline` bodies.',
168
+ },
169
+ 'parallel' => {
170
+ signature: 'parallel',
171
+ docs: 'Concurrency construct: `parallel for i in 0..N:` (data-parallel loop) or `parallel:` block (concurrent statement dispatch). Uses OS threads via libuv.',
172
+ },
173
+ 'detach' => {
174
+ signature: 'detach',
175
+ docs: 'Spawns work on a separate thread, returning a `Handle`: `let h = detach func()`. Use `gather` to wait. Supports global function calls only.',
176
+ },
177
+ 'gather' => {
178
+ signature: 'gather',
179
+ docs: 'Blocks until one or more `detach` handles complete: `gather h1, h2`. Takes one or more `Handle` values.',
180
+ },
181
+ 'event' => {
182
+ signature: 'event',
183
+ docs: 'Typed publisher/subscriber: `event name[capacity]` or `event name[capacity](PayloadType)`. Supports `subscribe`, `emit`, `unsubscribe`, `wait`.',
184
+ },
185
+ 'attribute' => {
186
+ signature: 'attribute',
187
+ docs: 'Declares a reusable declaration attribute: `attribute[target, ...] name(params)`. Targets: struct, field, callable, const, event, enum, flags, union, variant.',
188
+ },
189
+ 'proc' => {
190
+ signature: 'proc',
191
+ docs: 'Ref-counted closure: `proc(params...) -> T: body`. Captures values by value. Storable in structs, arrays, and tuples.',
192
+ },
193
+ 'fn' => {
194
+ signature: 'fn',
195
+ docs: 'Function pointer type: `fn(params...) -> T`. Points to module-level functions. No captured state; capture-free.',
196
+ },
197
+ 'dyn' => {
198
+ signature: 'dyn',
199
+ docs: 'Runtime interface value: `dyn[Interface]`. A fat pointer carrying a data pointer and vtable. Constructed via `adapt[Interface](value)`.',
200
+ },
201
+ 'is' => {
202
+ signature: 'is',
203
+ docs: 'Variant arm membership test: `expr is Variant.arm`. Desugars to a `match` expression evaluating to `bool`. Supports `not` negation.',
204
+ },
205
+ 'pass' => {
206
+ signature: 'pass',
207
+ docs: 'Explicit no-op statement for intentionally empty block bodies.',
208
+ },
209
+ 'null' => {
210
+ signature: 'null',
211
+ docs: 'Null value for nullable types (`T?`). Use typed `null[T]` when context cannot determine the target type.',
212
+ },
213
+ 'true' => {
214
+ signature: 'true',
215
+ docs: 'Boolean literal `true`. Type is `bool`.',
216
+ },
217
+ 'false' => {
218
+ signature: 'false',
219
+ docs: 'Boolean literal `false`. Type is `bool`.',
220
+ },
221
+ 'static_assert' => {
222
+ signature: 'static_assert',
223
+ docs: 'Compile-time assertion: `static_assert(condition, message)`. Fails compilation if the compile-time condition is false.',
224
+ },
225
+ }.freeze
226
+
227
+ BUILTIN_TYPE_DOCS = {
228
+ 'bool' => '1-byte boolean: `true` or `false`.',
229
+ 'byte' => '8-bit signed integer. Range: -128 to 127.',
230
+ 'ubyte' => '8-bit unsigned integer. Range: 0 to 255. Also used for `char` literals like `\'A\'`.',
231
+ 'char' => '8-bit character type (alias for `ubyte`).',
232
+ 'short' => '16-bit signed integer. Range: -32,768 to 32,767.',
233
+ 'ushort' => '16-bit unsigned integer. Range: 0 to 65,535.',
234
+ 'int' => '32-bit signed integer. Range: -2,147,483,648 to 2,147,483,647. Default enum backing type.',
235
+ 'uint' => '32-bit unsigned integer. Range: 0 to 4,294,967,295.',
236
+ 'long' => '64-bit signed integer.',
237
+ 'ulong' => '64-bit unsigned integer.',
238
+ 'ptr_int' => 'Pointer-sized signed integer. Width matches the target platform pointer size.',
239
+ 'ptr_uint' => 'Pointer-sized unsigned integer. Return type for `size_of`, `align_of`, `offset_of`.',
240
+ 'float' => '32-bit IEEE 754 single-precision float.',
241
+ 'double' => '64-bit IEEE 754 double-precision float.',
242
+ 'void' => 'Empty type for functions with no return value. Not a storable type.',
243
+ 'str' => 'Non-owning UTF-8 string view (pointer + length). Not null-terminated.',
244
+ 'cstr' => 'Null-terminated C string. Used at FFI boundaries.',
245
+ 'vec2' => '2-component float vector. Fields: `.x`, `.y`. Supports component-wise arithmetic.',
246
+ 'vec3' => '3-component float vector. Fields: `.x`, `.y`, `.z`. Supports component-wise arithmetic and `dot`/`cross`/`length` via `std.linear_algebra`.',
247
+ 'vec4' => '4-component float vector. Fields: `.x`, `.y`, `.z`, `.w`. Supports component-wise arithmetic.',
248
+ 'ivec2' => '2-component integer vector. Fields: `.x`, `.y`. Supports component-wise arithmetic.',
249
+ 'ivec3' => '3-component integer vector. Fields: `.x`, `.y`, `.z`. Supports component-wise arithmetic.',
250
+ 'ivec4' => '4-component integer vector. Fields: `.x`, `.y`, `.z`, `.w`. Supports component-wise arithmetic.',
251
+ 'mat3' => '3×3 column-major float matrix. Columns: `.col0`–`.col2` (each `vec3`). Supports `identity`, `transpose` via `std.linear_algebra`.',
252
+ 'mat4' => '4×4 column-major float matrix. Columns: `.col0`–`.col3` (each `vec4`). Supports `identity`, `transpose` via `std.linear_algebra`.',
253
+ 'quat' => 'Quaternion. Fields: `.x`, `.y`, `.z`, `.w`. Layout-compatible with `vec4`. Supports `identity`, `conjugate` via `std.linear_algebra`.',
254
+ 'ptr' => 'Generic pointer type: `ptr[T]`. Raw mutable pointer. Requires `unsafe` for indexing, dereference, and arithmetic.',
255
+ 'const_ptr' => 'Read-only pointer type: `const_ptr[T]`. Immutable pointer, does not require `unsafe` for dereference.',
256
+ 'own' => 'Owning heap pointer: `own[T]`. Auto-dereferences like `ref`. Storable, returnable, and nullable. Allocated via `heap.must_alloc[T](count)`.',
257
+ 'ref' => 'Non-null borrow reference: `ref[T]`. Auto-dereferences for member access and method calls. Cannot be stored in module variables or constants.',
258
+ 'span' => 'Borrowed pointer-plus-length view: `span[T]`. Constructed via `span[T](data = ..., len = ...)`. Arrays coerce implicitly.',
259
+ 'array' => 'Fixed-length array: `array[T, N]`. Constructed via `array[T, N](elements...)`. Omitted trailing elements default to zero.',
260
+ 'str_buffer' => 'Fixed-capacity mutable UTF-8 text buffer: `str_buffer[N]`. Methods: `assign`, `append`, `assign_format`, `append_format`, `clear`, `as_str`, `as_cstr`.',
261
+ 'atomic' => 'Atomic value for lock-free concurrent access: `atomic[T]`. `T` must be a primitive integer or `bool`. Methods: `load`, `store`, `add`, `sub`, `exchange`. Uses C11 `_Atomic`.',
262
+ 'Task' => 'Async task future: `Task[T]`. Returned by `async function`. Use `await` to unwrap, or `aio.wait`/`aio.run` to drive.',
263
+ 'Option' => 'Built-in optional type: `Option[T]`. Arms: `some(value: T)` and `none`. Use `let ... else:` or `?` for safe unwrapping.',
264
+ 'Result' => 'Built-in result type: `Result[T, E]`. Arms: `success(value: T)` and `failure(error: E)`. Use `let ... else:` or `?` for error propagation.',
265
+ 'SoA' => 'Struct-of-Arrays: `SoA[T, N]`. Each struct field becomes a separate array of length `N`. Access `soa[i].field` reads from column `field` at row `i`.',
266
+ 'struct_handle' => 'Compile-time handle for a struct type. Obtained via reflection builtins like `fields_of`.',
267
+ 'field_handle' => 'Compile-time handle for a struct field. Exposes `.name` and `.type`. Obtained via `field_of` and `fields_of`.',
268
+ 'callable_handle' => 'Compile-time handle for a callable declaration. Obtained via `callable_of`. Used with `has_attribute`, `attribute_of`.',
269
+ 'attribute_handle' => 'Compile-time handle for an applied attribute. Obtained via `attribute_of` and `attributes_of`. Use `attribute_arg[T]` to read arguments.',
270
+ 'member_handle' => 'Compile-time handle for an enum or flags member. Exposes `.name` and `.value`. Obtained via `members_of`.',
271
+ 'EventError' => 'Built-in enum returned when event listener capacity is exhausted. Single member: `full`.',
272
+ 'Subscription' => 'Opaque handle returned by `event.subscribe`. Pass to `event.unsubscribe` to remove a listener.',
273
+ }.freeze
274
+
24
275
  def handle_hover(params)
25
276
  stages = new_perf_stages
26
277
  total_start = stages ? monotonic_time : nil
@@ -35,7 +286,16 @@ module MilkTea
35
286
  token_kind = token&.type || :none
36
287
  unless token&.type == :identifier
37
288
  info = builtin_keyword_hover_info(token)
38
- return info if info
289
+ if info
290
+ result_state = 'hit'
291
+ return {
292
+ contents: {
293
+ kind: 'markdown',
294
+ value: render_hover_markdown(info)
295
+ },
296
+ range: token_to_range(token)
297
+ }
298
+ end
39
299
 
40
300
  result_state = 'not-identifier'
41
301
  return nil
@@ -166,6 +426,7 @@ module MilkTea
166
426
  elsif facts.types.key?(name)
167
427
  type = facts.types[name]
168
428
  signature = type_hover_signature(name, type)
429
+ docs ||= BUILTIN_TYPE_DOCS[name]
169
430
  elsif (binding = facts.values[name])
170
431
  signature = value_hover_signature(binding)
171
432
  elsif (import_binding = facts.imports[name])
@@ -183,6 +444,7 @@ module MilkTea
183
444
  signature = value_hover_signature(val)
184
445
  elsif module_binding.types.key?(name)
185
446
  signature = "type #{name}"
447
+ docs ||= BUILTIN_TYPE_DOCS[name]
186
448
  elsif (binding = module_binding.interfaces[name])
187
449
  signature = interface_signature(binding)
188
450
  source_location = module_member_definition_location(uri, module_binding.name, name)
@@ -1132,7 +1394,7 @@ module MilkTea
1132
1394
  end
1133
1395
 
1134
1396
  def builtin_type_constructor_hover_info(name, tokens, token_index)
1135
- return nil unless %w[array span Option Result SoA].include?(name)
1397
+ return nil unless %w[array span Option Result SoA str_buffer].include?(name)
1136
1398
 
1137
1399
  lbracket_index = next_non_trivia_token_index(tokens, token_index + 1)
1138
1400
  return nil unless lbracket_index && tokens[lbracket_index].type == :lbracket
@@ -1184,8 +1446,10 @@ module MilkTea
1184
1446
  when 'Option'
1185
1447
  '`Option[T]` is the built-in optional value type with `some(value = ...)` and `none` arms.'
1186
1448
  when 'Result'
1187
- '`Result[T, E]` is the built-in success/failure type with `success(value = ...)` and `failure(error = ...)` arms.'
1188
- end
1449
+ '`Result[T, E]` is the built-in success/failure type with `success(value = ...)` and `failure(error = ...)` arms.'
1450
+ when 'str_buffer'
1451
+ '`str_buffer[N]` is a fixed-capacity mutable UTF-8 text buffer. Methods: `assign`, `append`, `assign_format`, `append_format`, `clear`, `len`, `as_str`, `as_cstr`.'
1452
+ end
1189
1453
 
1190
1454
  {
1191
1455
  signature: "builtin type #{specialization}",
@@ -1260,10 +1524,9 @@ module MilkTea
1260
1524
  return nil unless token
1261
1525
 
1262
1526
  info = KEYWORD_HOVER_INFO[token.lexeme]
1263
- return nil unless info
1264
- return nil unless [:size_of, :align_of, :offset_of].include?(token.type)
1527
+ return info if info && [:size_of, :align_of, :offset_of].include?(token.type)
1265
1528
 
1266
- info
1529
+ LANGUAGE_KEYWORD_HOVER_INFO[token.lexeme]
1267
1530
  end
1268
1531
 
1269
1532
  def render_builtin_specialization(tokens)
@@ -58,7 +58,7 @@ module MilkTea
58
58
  KEYWORD_TOKEN_TYPES = Token::KEYWORDS.values.to_set.freeze
59
59
  DEFAULT_LIBRARY_TYPE_NAMES = Types::BUILTIN_TYPE_NAMES.to_set.freeze
60
60
  BUILTIN_FUNCTION_NAMES = %w[
61
- ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get
61
+ ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get field_of callable_of has_attribute attribute_of attribute_arg fields_of members_of attributes_of
62
62
  ].to_set.freeze
63
63
  BUILTIN_ASSOCIATED_HOOK_NAMES = %w[hash equal order].to_set.freeze
64
64
  BUILTIN_CALL_HOVER_INFO = {
@@ -68,7 +68,7 @@ module MilkTea
68
68
  },
69
69
  'ref_of' => {
70
70
  signature: 'builtin ref_of(value) -> ref[T]',
71
- docs: '`ref_of(x)` borrows a mutable safe lvalue as `ref[T]`.'
71
+ docs: '`ref_of(x)` borrows a safe lvalue as `ref[T]`.'
72
72
  },
73
73
  'const_ptr_of' => {
74
74
  signature: 'builtin const_ptr_of(value) -> const_ptr[T]',
@@ -76,7 +76,7 @@ module MilkTea
76
76
  },
77
77
  'ptr_of' => {
78
78
  signature: 'builtin ptr_of(value) -> ptr[T]',
79
- docs: '`ptr_of(x)` takes the address of a mutable safe lvalue as `ptr[T]`.'
79
+ docs: '`ptr_of(x)` takes the address of a safe lvalue as `ptr[T]`.'
80
80
  },
81
81
  'read' => {
82
82
  signature: 'builtin read(value) -> T',
@@ -130,6 +130,18 @@ module MilkTea
130
130
  signature: 'builtin attribute_of(target, attribute_name) -> attribute_handle',
131
131
  docs: '`attribute_of(target, attribute_name)` returns the applied attribute handle for the resolved target-and-attribute pair; use `has_attribute(...)` when absence is expected.'
132
132
  },
133
+ 'fields_of' => {
134
+ signature: 'builtin fields_of(Type) -> array[field_handle, N]',
135
+ docs: '`fields_of(Type)` returns all struct fields as a compile-time `array[field_handle, N]`, in declaration order. Use with `inline for` for reflective field iteration.'
136
+ },
137
+ 'members_of' => {
138
+ signature: 'builtin members_of(Type) -> array[member_handle, N]',
139
+ docs: '`members_of(Type)` returns all members of an enum or flags type as a compile-time `array[member_handle, N]`. Each handle exposes `.name` and `.value`.'
140
+ },
141
+ 'attributes_of' => {
142
+ signature: 'builtin attributes_of(target [, name]) -> array[attribute_handle, N]',
143
+ docs: '`attributes_of(target)` returns all attributes applied to a type, field, or callable as a compile-time array. `attributes_of(target, name)` filters by attribute kind.'
144
+ },
133
145
  }.freeze
134
146
  OPERATOR_TOKEN_TYPES = %i[
135
147
  amp colon comma caret dot lparen rparen pipe lbracket rbracket question
@@ -586,6 +586,8 @@ module MilkTea
586
586
  return unless statement.type
587
587
  return unless statement.value
588
588
 
589
+ return if statement.type.nullable
590
+
589
591
  declared_name = type_ref_name(statement.type)
590
592
  return unless declared_name
591
593
 
@@ -666,6 +668,7 @@ module MilkTea
666
668
  # enum/int/str bool-matches are never misreported.
667
669
  def check_prefer_is_variant(match_expr)
668
670
  return unless @sema_facts
671
+ return if match_expr.desugared_from_is
669
672
 
670
673
  arms = match_expr.arms
671
674
  return unless arms.size == 2
@@ -771,6 +774,8 @@ module MilkTea
771
774
  return if stmts.any?(&:nil?)
772
775
  return unless stmts.all? { |s| inline_simple_statement?(s) }
773
776
 
777
+ return if visually_inline_if?(statement, stmts)
778
+
774
779
  emit_conciseness_hint(
775
780
  "prefer-inline-if",
776
781
  line: statement.line,
@@ -779,6 +784,15 @@ module MilkTea
779
784
  )
780
785
  end
781
786
 
787
+ def visually_inline_if?(statement, stmts)
788
+ n = statement.branches.length
789
+ statement.branches.each_with_index.all? { |b, i|
790
+ stmts[i].respond_to?(:line) && stmts[i].line == b.line
791
+ } && (
792
+ stmts[n].respond_to?(:line) && statement.else_line && stmts[n].line == statement.else_line
793
+ )
794
+ end
795
+
782
796
  # prefer-conditional-expression: if/match whose every branch either
783
797
  # returns a value or assigns the same lvalue can become an expression form
784
798
  # (`return if …: … else: …` or `x = match …`).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mt-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -583,7 +583,7 @@ metadata:
583
583
  homepage_uri: https://teefan.github.io/mt-lang/
584
584
  source_code_uri: https://github.com/teefan/mt-lang
585
585
  post_install_message: |
586
- Milk Tea 0.2.8 installed!
586
+ Milk Tea 0.2.11 installed!
587
587
 
588
588
  System requirements:
589
589
  - A C compiler (gcc or clang) must be available on PATH