mt-lang 0.3.8 → 0.3.10
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 +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- metadata +2 -2
|
@@ -3,28 +3,26 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterFlowRules
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
def emit_dead_assignment_warnings(stmts)
|
|
9
7
|
analysis = dead_assignment_analysis(stmts)
|
|
10
8
|
return unless analysis
|
|
11
|
-
|
|
9
|
+
|
|
12
10
|
graph = analysis.graph
|
|
13
11
|
liveness = analysis.liveness
|
|
14
12
|
readable_bindings = analysis.readable_bindings
|
|
15
13
|
locally_declared = analysis.locally_declared
|
|
16
|
-
|
|
14
|
+
|
|
17
15
|
graph.each_node do |node|
|
|
18
16
|
node.writes_info.each do |write|
|
|
19
17
|
next if write[:origin] == :call_argument
|
|
20
18
|
next if write[:origin] == :declaration
|
|
21
|
-
|
|
19
|
+
|
|
22
20
|
binding_key = write[:binding_key]
|
|
23
21
|
name = write[:name]
|
|
24
22
|
next unless readable_bindings.include?(binding_key)
|
|
25
23
|
next unless locally_declared.include?(binding_key)
|
|
26
24
|
next if liveness.live_out[node.id].include?(binding_key)
|
|
27
|
-
|
|
25
|
+
|
|
28
26
|
@warnings << Warning.new(
|
|
29
27
|
path: @path,
|
|
30
28
|
line: write[:line],
|
|
@@ -40,17 +38,17 @@ module MilkTea
|
|
|
40
38
|
def emit_unreachable_warnings(stmts)
|
|
41
39
|
analysis = statement_flow_analysis(stmts)
|
|
42
40
|
return unless analysis
|
|
43
|
-
|
|
41
|
+
|
|
44
42
|
graph = analysis.graph
|
|
45
43
|
reachable = analysis.reachability
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
graph.each_node do |node|
|
|
48
46
|
next if reachable.reachable_ids.include?(node.id)
|
|
49
47
|
next if node.kind == :exit
|
|
50
48
|
next unless node.statement
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
statement = node.statement
|
|
53
|
-
line = statement.
|
|
51
|
+
line = statement.line
|
|
54
52
|
@warnings << Warning.new(
|
|
55
53
|
path: @path,
|
|
56
54
|
line:,
|
|
@@ -65,10 +63,10 @@ module MilkTea
|
|
|
65
63
|
# is taken from a local variable that is also written later in the same body.
|
|
66
64
|
def emit_borrow_warnings(stmts)
|
|
67
65
|
return if stmts.nil? || stmts.empty?
|
|
68
|
-
|
|
66
|
+
|
|
69
67
|
borrowed = collect_borrowed_names(stmts)
|
|
70
68
|
return if borrowed.empty?
|
|
71
|
-
|
|
69
|
+
|
|
72
70
|
written = collect_written_names(stmts)
|
|
73
71
|
(borrowed & written).each do |name|
|
|
74
72
|
# Find the earliest borrow site for the warning location
|
|
@@ -87,7 +85,7 @@ module MilkTea
|
|
|
87
85
|
end
|
|
88
86
|
def cfg_binding_resolution
|
|
89
87
|
return @cfg_binding_resolution if @cfg_binding_resolution_computed
|
|
90
|
-
|
|
88
|
+
|
|
91
89
|
binding_resolution = @sema_facts&.binding_resolution
|
|
92
90
|
@cfg_binding_resolution = if binding_resolution
|
|
93
91
|
ControlFlow::BindingResolution.new(
|
|
@@ -101,7 +99,7 @@ module MilkTea
|
|
|
101
99
|
end
|
|
102
100
|
def statement_flow_analysis(stmts)
|
|
103
101
|
return nil if stmts.nil? || stmts.empty?
|
|
104
|
-
|
|
102
|
+
|
|
105
103
|
@statement_flow_analysis_cache[stmts.object_id] ||= begin
|
|
106
104
|
binding_resolution = cfg_binding_resolution
|
|
107
105
|
graph = profile_phase("flow.graph") do
|
|
@@ -118,7 +116,7 @@ module MilkTea
|
|
|
118
116
|
end
|
|
119
117
|
def dead_assignment_analysis(stmts)
|
|
120
118
|
return nil if stmts.nil? || stmts.empty?
|
|
121
|
-
|
|
119
|
+
|
|
122
120
|
@dead_assignment_analysis_cache[stmts.object_id] ||= begin
|
|
123
121
|
binding_resolution = cfg_binding_resolution
|
|
124
122
|
graph = profile_phase("dead_assignment.graph") do
|
|
@@ -147,7 +145,7 @@ module MilkTea
|
|
|
147
145
|
def cfg_identifier_binding_key(identifier)
|
|
148
146
|
binding_resolution = cfg_binding_resolution
|
|
149
147
|
return identifier.name unless binding_resolution
|
|
150
|
-
|
|
148
|
+
|
|
151
149
|
binding_resolution.identifier_binding_ids[identifier.object_id] || identifier.name
|
|
152
150
|
end
|
|
153
151
|
def ignored_binding_name?(name)
|
|
@@ -159,18 +157,18 @@ module MilkTea
|
|
|
159
157
|
# Uses ConstantPropagation to detect conditions that are always true/false.
|
|
160
158
|
# Skips `while true` — it is an idiomatic infinite loop.
|
|
161
159
|
# Skips if conditions inside loops, since variables can change across iterations.
|
|
162
|
-
|
|
160
|
+
|
|
163
161
|
def emit_constant_condition_warnings(stmts)
|
|
164
162
|
return if stmts.nil? || stmts.empty?
|
|
165
|
-
|
|
163
|
+
|
|
166
164
|
analysis = statement_flow_analysis(stmts)
|
|
167
165
|
return unless analysis
|
|
168
|
-
|
|
166
|
+
|
|
169
167
|
binding_resolution = cfg_binding_resolution
|
|
170
168
|
graph = analysis.graph
|
|
171
169
|
cp = analysis.constant_propagation
|
|
172
170
|
loop_bodies = analysis.loop_body_nodes
|
|
173
|
-
|
|
171
|
+
|
|
174
172
|
graph.each_node do |node|
|
|
175
173
|
cond_expr, line, keyword_pattern, skip_node =
|
|
176
174
|
case node.kind
|
|
@@ -188,9 +186,9 @@ module MilkTea
|
|
|
188
186
|
else
|
|
189
187
|
next
|
|
190
188
|
end
|
|
191
|
-
|
|
189
|
+
|
|
192
190
|
next if skip_node || cond_expr.nil?
|
|
193
|
-
|
|
191
|
+
|
|
194
192
|
in_state = cp.in_states[node.id] || {}
|
|
195
193
|
const_val = ControlFlow::ConstantPropagation.constant_value_of(
|
|
196
194
|
cond_expr,
|
|
@@ -199,7 +197,7 @@ module MilkTea
|
|
|
199
197
|
strict_binding_ids: !binding_resolution.nil?
|
|
200
198
|
)
|
|
201
199
|
next unless const_val == true || const_val == false
|
|
202
|
-
|
|
200
|
+
|
|
203
201
|
ctx = node.kind == :while_condition ? "loop condition" : "branch condition"
|
|
204
202
|
line, column, length = condition_span(cond_expr, line:, keyword_pattern:)
|
|
205
203
|
@warnings << Warning.new(
|
|
@@ -218,7 +216,7 @@ module MilkTea
|
|
|
218
216
|
# A back-edge exists when a node is reachable from its own successors.
|
|
219
217
|
private def compute_loop_body_nodes(graph)
|
|
220
218
|
loop_nodes = Set.new
|
|
221
|
-
|
|
219
|
+
|
|
222
220
|
# Find all back-edges: detect cycles by checking if any successor of a node
|
|
223
221
|
# can reach back to that node.
|
|
224
222
|
graph.each_node do |node|
|
|
@@ -230,38 +228,38 @@ module MilkTea
|
|
|
230
228
|
end
|
|
231
229
|
end
|
|
232
230
|
end
|
|
233
|
-
|
|
231
|
+
|
|
234
232
|
loop_nodes
|
|
235
233
|
end
|
|
236
|
-
|
|
234
|
+
|
|
237
235
|
# Returns true if target_id is reachable from start_id via forward edges.
|
|
238
236
|
private def reachable_from?(graph, start_id, target_id)
|
|
239
237
|
visited = Set.new
|
|
240
238
|
queue = [start_id]
|
|
241
|
-
|
|
239
|
+
|
|
242
240
|
while queue.any?
|
|
243
241
|
node_id = queue.shift
|
|
244
242
|
return true if node_id == target_id
|
|
245
243
|
next if visited.include?(node_id)
|
|
246
|
-
|
|
244
|
+
|
|
247
245
|
visited.add(node_id)
|
|
248
246
|
node = graph.nodes[node_id]
|
|
249
247
|
node.succs.each { |succ| queue.push(succ) }
|
|
250
248
|
end
|
|
251
|
-
|
|
249
|
+
|
|
252
250
|
false
|
|
253
251
|
end
|
|
254
|
-
|
|
252
|
+
|
|
255
253
|
# Mark all nodes reachable from start_id as being inside a loop.
|
|
256
254
|
private def mark_reachable_nodes(graph, start_id, loop_nodes)
|
|
257
255
|
visited = Set.new
|
|
258
256
|
queue = [start_id]
|
|
259
|
-
|
|
257
|
+
|
|
260
258
|
while queue.any?
|
|
261
259
|
node_id = queue.shift
|
|
262
260
|
loop_nodes.add(node_id)
|
|
263
261
|
next if visited.include?(node_id)
|
|
264
|
-
|
|
262
|
+
|
|
265
263
|
visited.add(node_id)
|
|
266
264
|
node = graph.nodes[node_id]
|
|
267
265
|
node.succs.each { |succ| queue.push(succ) unless visited.include?(succ) }
|
|
@@ -270,31 +268,31 @@ module MilkTea
|
|
|
270
268
|
# ── redundant-null-check ───────────────────────────────────────────────
|
|
271
269
|
# After a variable has been narrowed to non-null by a prior check,
|
|
272
270
|
# a subsequent `x != nil` guard is always true.
|
|
273
|
-
|
|
271
|
+
|
|
274
272
|
def emit_redundant_null_check_warnings(stmts)
|
|
275
273
|
return if stmts.nil? || stmts.empty?
|
|
276
|
-
|
|
274
|
+
|
|
277
275
|
analysis = statement_flow_analysis(stmts)
|
|
278
276
|
return unless analysis
|
|
279
|
-
|
|
277
|
+
|
|
280
278
|
graph = analysis.graph
|
|
281
279
|
nf = analysis.nullability
|
|
282
|
-
|
|
280
|
+
|
|
283
281
|
graph.each_node do |node|
|
|
284
282
|
next unless node.kind == :if_condition
|
|
285
|
-
|
|
283
|
+
|
|
286
284
|
branch = node.statement
|
|
287
285
|
next unless branch.is_a?(AST::IfBranch)
|
|
288
|
-
|
|
286
|
+
|
|
289
287
|
identifier = null_check_identifier(branch.condition)
|
|
290
288
|
next unless identifier
|
|
291
289
|
next if ignored_binding_name?(identifier.name)
|
|
292
|
-
|
|
290
|
+
|
|
293
291
|
nonnull = nf.nonnull_before(branch)
|
|
294
292
|
next unless nonnull.include?(cfg_identifier_binding_key(identifier))
|
|
295
|
-
|
|
293
|
+
|
|
296
294
|
line, column, length = condition_span(branch.condition, line: node.line, keyword_pattern: "else if|if")
|
|
297
|
-
|
|
295
|
+
|
|
298
296
|
@warnings << Warning.new(
|
|
299
297
|
path: @path,
|
|
300
298
|
line:,
|
|
@@ -307,12 +305,12 @@ module MilkTea
|
|
|
307
305
|
)
|
|
308
306
|
end
|
|
309
307
|
end
|
|
310
|
-
|
|
308
|
+
|
|
311
309
|
# Returns the Identifier being nil-tested if `cond` is `x != nil` or
|
|
312
310
|
# `nil != x`, otherwise nil.
|
|
313
311
|
def null_check_identifier(cond)
|
|
314
312
|
return nil unless cond.is_a?(AST::BinaryOp) && cond.operator == "!="
|
|
315
|
-
|
|
313
|
+
|
|
316
314
|
if cond.left.is_a?(AST::Identifier) && cond.right.is_a?(AST::NullLiteral)
|
|
317
315
|
cond.left
|
|
318
316
|
elsif cond.left.is_a?(AST::NullLiteral) && cond.right.is_a?(AST::Identifier)
|
|
@@ -322,13 +320,13 @@ module MilkTea
|
|
|
322
320
|
# ── loop-single-iteration ──────────────────────────────────────────────
|
|
323
321
|
# A loop whose body unconditionally exits (return/break) before the
|
|
324
322
|
# back-edge is taken will execute at most once.
|
|
325
|
-
|
|
323
|
+
|
|
326
324
|
def emit_loop_single_iteration_warnings(stmts)
|
|
327
325
|
return if stmts.nil? || stmts.empty?
|
|
328
|
-
|
|
326
|
+
|
|
329
327
|
walk_stmts_for_loop_check(stmts)
|
|
330
328
|
end
|
|
331
|
-
|
|
329
|
+
|
|
332
330
|
def walk_stmts_for_loop_check(stmts)
|
|
333
331
|
stmts.each do |stmt|
|
|
334
332
|
case stmt
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterImportsPlatform
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
# ── unused-import ────────────────────────────────────────────────────
|
|
9
7
|
|
|
10
8
|
def check_unused_imports(source_file)
|
|
@@ -73,7 +71,7 @@ module MilkTea
|
|
|
73
71
|
end
|
|
74
72
|
return first_export_site if first_export_site
|
|
75
73
|
|
|
76
|
-
first_declaration = source_file.declarations.find { |declaration| declaration.
|
|
74
|
+
first_declaration = source_file.declarations.find { |declaration| declaration.line }
|
|
77
75
|
return declaration_anchor(first_declaration) if first_declaration
|
|
78
76
|
|
|
79
77
|
{ line: source_file.line || 1, column: 1, length: 1, symbol_name: nil }
|
|
@@ -128,9 +126,9 @@ module MilkTea
|
|
|
128
126
|
|
|
129
127
|
symbol_name = declaration.respond_to?(:name) ? declaration.name : nil
|
|
130
128
|
length = symbol_name.to_s.empty? ? 1 : symbol_name.to_s.length
|
|
131
|
-
line = declaration.
|
|
129
|
+
line = declaration.line ? declaration.line : 1
|
|
132
130
|
|
|
133
|
-
if declaration.
|
|
131
|
+
if declaration.column
|
|
134
132
|
return { line:, column: declaration.column, length:, symbol_name: }
|
|
135
133
|
end
|
|
136
134
|
|
|
@@ -314,17 +312,17 @@ module MilkTea
|
|
|
314
312
|
unless attribute.arguments.empty?
|
|
315
313
|
rendered_arguments = attribute.arguments.map do |argument|
|
|
316
314
|
value = case argument.value
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
315
|
+
when AST::IntegerLiteral, AST::FloatLiteral
|
|
316
|
+
argument.value.lexeme
|
|
317
|
+
when AST::StringLiteral
|
|
318
|
+
argument.value.lexeme
|
|
319
|
+
when AST::Identifier
|
|
320
|
+
argument.value.name
|
|
321
|
+
when AST::MemberAccess
|
|
322
|
+
"#{argument.value.receiver.name}.#{argument.value.member}"
|
|
323
|
+
else
|
|
324
|
+
"..."
|
|
325
|
+
end
|
|
328
326
|
argument.name ? "#{argument.name} = #{value}" : value
|
|
329
327
|
end
|
|
330
328
|
text << "(#{rendered_arguments.join(', ')})"
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterReleaseRules
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
# Entry point — called from visit_function and proc expr in full_tier mode.
|
|
9
7
|
def emit_owning_release_warnings(function_or_body)
|
|
10
8
|
body = function_or_body.is_a?(Array) ? function_or_body : function_or_body.body
|
|
@@ -83,7 +81,7 @@ module MilkTea
|
|
|
83
81
|
if type_name
|
|
84
82
|
result[stmt.name] = {
|
|
85
83
|
line: stmt.line,
|
|
86
|
-
column: stmt.
|
|
84
|
+
column: stmt.column,
|
|
87
85
|
type_name: type_name,
|
|
88
86
|
}
|
|
89
87
|
end
|
|
@@ -289,7 +287,7 @@ module MilkTea
|
|
|
289
287
|
if type_name
|
|
290
288
|
result[stmt.name] = {
|
|
291
289
|
line: stmt.line,
|
|
292
|
-
column: stmt.
|
|
290
|
+
column: stmt.column,
|
|
293
291
|
type_name: type_name,
|
|
294
292
|
}
|
|
295
293
|
end
|
|
@@ -725,7 +723,7 @@ module MilkTea
|
|
|
725
723
|
end
|
|
726
724
|
|
|
727
725
|
def extract_line(stmt)
|
|
728
|
-
stmt.
|
|
726
|
+
stmt.line
|
|
729
727
|
end
|
|
730
728
|
|
|
731
729
|
def owning_base_name(qname)
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterReservedNames
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
def declare_reserved_value_type_module_binding(name, kind_label:, line:, column:, unavailable_names:)
|
|
9
7
|
declare_reserved_module_binding(
|
|
10
8
|
name,
|
|
@@ -15,7 +13,7 @@ module MilkTea
|
|
|
15
13
|
reserved_names: RESERVED_VALUE_TYPE_NAMES,
|
|
16
14
|
)
|
|
17
15
|
end
|
|
18
|
-
|
|
16
|
+
|
|
19
17
|
def declare_reserved_import_alias_module_binding(name, kind_label:, line:, column:, unavailable_names:)
|
|
20
18
|
declare_reserved_module_binding(
|
|
21
19
|
name,
|
|
@@ -26,7 +24,7 @@ module MilkTea
|
|
|
26
24
|
reserved_names: RESERVED_IMPORT_ALIAS_NAMES,
|
|
27
25
|
)
|
|
28
26
|
end
|
|
29
|
-
|
|
27
|
+
|
|
30
28
|
def declare_reserved_module_binding(name, kind_label:, line:, column:, unavailable_names:, reserved_names:)
|
|
31
29
|
replacement_name = nil
|
|
32
30
|
replacement_base_name = nil
|
|
@@ -37,7 +35,7 @@ module MilkTea
|
|
|
37
35
|
unavailable_names,
|
|
38
36
|
)
|
|
39
37
|
end
|
|
40
|
-
|
|
38
|
+
|
|
41
39
|
binding = Binding.new(
|
|
42
40
|
name:,
|
|
43
41
|
line:,
|
|
@@ -52,13 +50,13 @@ module MilkTea
|
|
|
52
50
|
register_reserved_primitive_name_fix(binding, kind_label:, replacement_name:) if replacement_name
|
|
53
51
|
@module_bindings[name] = binding
|
|
54
52
|
end
|
|
55
|
-
|
|
53
|
+
|
|
56
54
|
def declare_reserved_primitive_module_binding(name, kind_label:, line:, column:, unavailable_names:)
|
|
57
55
|
declare_reserved_value_type_module_binding(name, kind_label:, line:, column:, unavailable_names:)
|
|
58
56
|
end
|
|
59
57
|
def warn_reserved_primitive_name(name, line:, column:, kind_label:, reserved_names: RESERVED_VALUE_TYPE_NAMES)
|
|
60
58
|
return unless reserved_names.include?(name)
|
|
61
|
-
|
|
59
|
+
|
|
62
60
|
@warnings << Warning.new(
|
|
63
61
|
path: @path,
|
|
64
62
|
line:,
|
|
@@ -70,10 +68,10 @@ module MilkTea
|
|
|
70
68
|
symbol_name: name,
|
|
71
69
|
)
|
|
72
70
|
end
|
|
73
|
-
|
|
71
|
+
|
|
74
72
|
def warn_large_event_capacity(event_decl, owner_name:)
|
|
75
73
|
label = owner_name ? "#{owner_name}.#{event_decl.name}" : event_decl.name
|
|
76
|
-
|
|
74
|
+
|
|
77
75
|
@warnings << Warning.new(
|
|
78
76
|
path: @path,
|
|
79
77
|
line: event_decl.line,
|
|
@@ -85,16 +83,16 @@ module MilkTea
|
|
|
85
83
|
symbol_name: event_decl.name,
|
|
86
84
|
)
|
|
87
85
|
end
|
|
88
|
-
|
|
86
|
+
|
|
89
87
|
def warn_redundant_ignored_match_binding(name, line:, column:)
|
|
90
88
|
return unless name == "_"
|
|
91
|
-
|
|
89
|
+
|
|
92
90
|
span = nil
|
|
93
91
|
if line && column
|
|
94
92
|
source_line = @source_lines[line - 1].to_s
|
|
95
93
|
span = self.class.redundant_ignored_match_binding_span(source_line, column:)
|
|
96
94
|
end
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
@warnings << Warning.new(
|
|
99
97
|
path: @path,
|
|
100
98
|
line:,
|
|
@@ -105,7 +103,7 @@ module MilkTea
|
|
|
105
103
|
severity: :hint,
|
|
106
104
|
)
|
|
107
105
|
end
|
|
108
|
-
|
|
106
|
+
|
|
109
107
|
def warn_reserved_primitive_type_params(type_params, kind_label:)
|
|
110
108
|
Array(type_params).each do |type_param|
|
|
111
109
|
warn_reserved_primitive_name(
|
|
@@ -120,7 +118,7 @@ module MilkTea
|
|
|
120
118
|
def register_reserved_primitive_name_fix(binding, kind_label:, replacement_name:)
|
|
121
119
|
warn_reserved_primitive_name(binding.name, line: binding.line, column: binding.column, kind_label:)
|
|
122
120
|
return unless binding.line && binding.column
|
|
123
|
-
|
|
121
|
+
|
|
124
122
|
binding.fix_index = @reserved_primitive_name_fixes.length
|
|
125
123
|
@reserved_primitive_name_fixes << ReservedPrimitiveNameFix.new(
|
|
126
124
|
kind: kind_label,
|
|
@@ -129,7 +127,7 @@ module MilkTea
|
|
|
129
127
|
sites: [ReservedPrimitiveNameSite.new(line: binding.line, column: binding.column, length: binding.name.length)],
|
|
130
128
|
)
|
|
131
129
|
end
|
|
132
|
-
|
|
130
|
+
|
|
133
131
|
def suggested_reserved_primitive_name(name, kind_label:)
|
|
134
132
|
case kind_label
|
|
135
133
|
when "function"
|
|
@@ -140,20 +138,20 @@ module MilkTea
|
|
|
140
138
|
"#{name}_value"
|
|
141
139
|
end
|
|
142
140
|
end
|
|
143
|
-
|
|
141
|
+
|
|
144
142
|
def next_available_reserved_primitive_name(base_name, unavailable_names)
|
|
145
143
|
unavailable = unavailable_names.to_set
|
|
146
144
|
return base_name unless unavailable.include?(base_name)
|
|
147
|
-
|
|
145
|
+
|
|
148
146
|
suffix = 2
|
|
149
147
|
loop do
|
|
150
148
|
candidate = "#{base_name}_#{suffix}"
|
|
151
149
|
return candidate unless unavailable.include?(candidate)
|
|
152
|
-
|
|
150
|
+
|
|
153
151
|
suffix += 1
|
|
154
152
|
end
|
|
155
153
|
end
|
|
156
|
-
|
|
154
|
+
|
|
157
155
|
def visible_binding_names(excluding_name: nil)
|
|
158
156
|
names = visible_bindings.each_with_object(Set.new) do |binding, result|
|
|
159
157
|
result << binding.name
|
|
@@ -162,24 +160,24 @@ module MilkTea
|
|
|
162
160
|
names.delete(excluding_name) if excluding_name
|
|
163
161
|
names
|
|
164
162
|
end
|
|
165
|
-
|
|
163
|
+
|
|
166
164
|
def visible_bindings
|
|
167
165
|
@module_bindings.values + @scopes.flat_map(&:values)
|
|
168
166
|
end
|
|
169
|
-
|
|
167
|
+
|
|
170
168
|
def resolve_reserved_primitive_name_conflicts!(declared_name)
|
|
171
169
|
visible_bindings.each do |binding|
|
|
172
170
|
next unless binding.replacement_name == declared_name
|
|
173
|
-
|
|
171
|
+
|
|
174
172
|
replacement_name = next_available_reserved_primitive_name(
|
|
175
173
|
binding.replacement_base_name || binding.replacement_name,
|
|
176
174
|
visible_binding_names(excluding_name: binding.name),
|
|
177
175
|
)
|
|
178
176
|
next if replacement_name == binding.replacement_name
|
|
179
|
-
|
|
177
|
+
|
|
180
178
|
binding.replacement_name = replacement_name
|
|
181
179
|
next unless binding.fix_index
|
|
182
|
-
|
|
180
|
+
|
|
183
181
|
fix = @reserved_primitive_name_fixes[binding.fix_index]
|
|
184
182
|
@reserved_primitive_name_fixes[binding.fix_index] = fix.with(replacement_name:)
|
|
185
183
|
end
|
|
@@ -187,7 +185,7 @@ module MilkTea
|
|
|
187
185
|
def record_reserved_primitive_identifier_use(binding, identifier)
|
|
188
186
|
return unless binding.fix_index
|
|
189
187
|
return unless identifier&.line && identifier&.column
|
|
190
|
-
|
|
188
|
+
|
|
191
189
|
@reserved_primitive_name_fixes[binding.fix_index].sites << ReservedPrimitiveNameSite.new(
|
|
192
190
|
line: identifier.line,
|
|
193
191
|
column: identifier.column,
|