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,19 +3,17 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterRules
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
# ── missing-return ───────────────────────────────────────────────────
|
|
9
|
-
|
|
7
|
+
|
|
10
8
|
def check_missing_return(function)
|
|
11
9
|
return unless function.return_type # implicit void — no check
|
|
12
10
|
return if void_return_type?(function.return_type)
|
|
13
11
|
return if always_returns?(function.body)
|
|
14
|
-
|
|
12
|
+
|
|
15
13
|
@warnings << Warning.new(
|
|
16
14
|
path: @path,
|
|
17
15
|
line: function.line,
|
|
18
|
-
column: function.
|
|
16
|
+
column: function.column,
|
|
19
17
|
length: function.name.length,
|
|
20
18
|
code: "missing-return",
|
|
21
19
|
message: "function '#{function.name}' does not always return a value",
|
|
@@ -23,15 +21,15 @@ module MilkTea
|
|
|
23
21
|
symbol_name: function.name
|
|
24
22
|
)
|
|
25
23
|
end
|
|
26
|
-
|
|
24
|
+
|
|
27
25
|
def emit_redundant_return_warnings(function)
|
|
28
26
|
return unless function.return_type
|
|
29
27
|
return unless void_return_type?(function.return_type)
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
final_statement = function.body&.last
|
|
32
30
|
return unless final_statement.is_a?(AST::ReturnStmt)
|
|
33
31
|
return unless final_statement.value.nil?
|
|
34
|
-
|
|
32
|
+
|
|
35
33
|
@warnings << Warning.new(
|
|
36
34
|
path: @path,
|
|
37
35
|
line: final_statement.line,
|
|
@@ -42,7 +40,7 @@ module MilkTea
|
|
|
42
40
|
severity: :hint,
|
|
43
41
|
)
|
|
44
42
|
end
|
|
45
|
-
|
|
43
|
+
|
|
46
44
|
def void_return_type?(return_type)
|
|
47
45
|
case return_type
|
|
48
46
|
when AST::TypeRef
|
|
@@ -54,7 +52,7 @@ module MilkTea
|
|
|
54
52
|
false
|
|
55
53
|
end
|
|
56
54
|
end
|
|
57
|
-
|
|
55
|
+
|
|
58
56
|
# Returns true if every execution path through `stmts` ends with a
|
|
59
57
|
# guaranteed return. Conservative: only IfStmt with else + MatchStmt
|
|
60
58
|
# with arms are considered exhaustive.
|
|
@@ -87,23 +85,23 @@ module MilkTea
|
|
|
87
85
|
end
|
|
88
86
|
end
|
|
89
87
|
end
|
|
90
|
-
|
|
88
|
+
|
|
91
89
|
def infinite_while_without_break?(stmt)
|
|
92
90
|
stmt.condition.is_a?(AST::BooleanLiteral) &&
|
|
93
91
|
stmt.condition.value == true &&
|
|
94
92
|
!loop_body_can_break?(stmt.body)
|
|
95
93
|
end
|
|
96
|
-
|
|
94
|
+
|
|
97
95
|
def loop_body_can_break?(body)
|
|
98
96
|
return false if body.nil? || body.empty?
|
|
99
|
-
|
|
97
|
+
|
|
100
98
|
graph = ControlFlow::Builder.new.build_loop_body(body)
|
|
101
99
|
reachability = ControlFlow::Reachability.solve(graph)
|
|
102
100
|
graph.each_node.any? do |node|
|
|
103
101
|
node.kind == :break_exit && reachability.reachable_ids.include?(node.id)
|
|
104
102
|
end
|
|
105
103
|
end
|
|
106
|
-
|
|
104
|
+
|
|
107
105
|
def terminating_expression?(expression)
|
|
108
106
|
case expression
|
|
109
107
|
when AST::Call
|
|
@@ -122,7 +120,7 @@ module MilkTea
|
|
|
122
120
|
first_arg = expression.arguments.first
|
|
123
121
|
first_arg.is_a?(AST::BooleanLiteral) && first_arg.value == false
|
|
124
122
|
end
|
|
125
|
-
|
|
123
|
+
|
|
126
124
|
def terminating_callee?(callee)
|
|
127
125
|
case callee
|
|
128
126
|
when AST::Identifier
|
|
@@ -139,9 +137,9 @@ module MilkTea
|
|
|
139
137
|
def check_redundant_else(stmt)
|
|
140
138
|
return unless stmt.else_body && !stmt.else_body.empty?
|
|
141
139
|
return unless stmt.branches.all? { |b| always_returns?(b.body) }
|
|
142
|
-
|
|
140
|
+
|
|
143
141
|
# Use the line of the first else-body statement as the diagnostic anchor.
|
|
144
|
-
else_line = stmt.else_body.first.
|
|
142
|
+
else_line = stmt.else_body.first.line || stmt.line
|
|
145
143
|
@warnings << Warning.new(
|
|
146
144
|
path: @path,
|
|
147
145
|
line: stmt.else_line || else_line,
|
|
@@ -170,13 +168,13 @@ module MilkTea
|
|
|
170
168
|
return if expr.is_a?(AST::UnaryOp) && expr.operator == "?"
|
|
171
169
|
return if contains_side_effecting_expression?(expr)
|
|
172
170
|
|
|
173
|
-
line = expression_line(expr) || (stmt.
|
|
171
|
+
line = expression_line(expr) || (stmt.line)
|
|
174
172
|
column = expression_column(expr)
|
|
175
173
|
length = expression_length(expr)
|
|
176
|
-
if line && (!column || !length ||
|
|
174
|
+
if line && (!column || !length || expr.is_a?(AST::UnsafeExpr))
|
|
177
175
|
fallback_span = source_statement_span(line)
|
|
178
176
|
column ||= fallback_span&.first
|
|
179
|
-
length = fallback_span&.last if !length ||
|
|
177
|
+
length = fallback_span&.last if !length || expr.is_a?(AST::UnsafeExpr)
|
|
180
178
|
end
|
|
181
179
|
|
|
182
180
|
@warnings << Warning.new(
|
|
@@ -206,12 +204,12 @@ module MilkTea
|
|
|
206
204
|
end
|
|
207
205
|
end
|
|
208
206
|
# ── self-assignment ────────────────────────────────────────────────────
|
|
209
|
-
|
|
207
|
+
|
|
210
208
|
def check_self_assignment(stmt)
|
|
211
209
|
return unless stmt.operator == "="
|
|
212
210
|
return unless stmt.target.is_a?(AST::Identifier) && stmt.value.is_a?(AST::Identifier)
|
|
213
211
|
return unless stmt.target.name == stmt.value.name
|
|
214
|
-
|
|
212
|
+
|
|
215
213
|
@warnings << Warning.new(
|
|
216
214
|
path: @path,
|
|
217
215
|
line: expression_line(stmt.target) || stmt.line,
|
|
@@ -223,14 +221,14 @@ module MilkTea
|
|
|
223
221
|
symbol_name: stmt.target.name
|
|
224
222
|
)
|
|
225
223
|
end
|
|
226
|
-
|
|
224
|
+
|
|
227
225
|
# ── self-comparison ────────────────────────────────────────────────────
|
|
228
|
-
|
|
226
|
+
|
|
229
227
|
def check_self_comparison(expr)
|
|
230
228
|
return unless %w[== !=].include?(expr.operator)
|
|
231
229
|
return unless expr.left.is_a?(AST::Identifier) && expr.right.is_a?(AST::Identifier)
|
|
232
230
|
return unless expr.left.name == expr.right.name
|
|
233
|
-
|
|
231
|
+
|
|
234
232
|
line = expression_line(expr) || expr.left.line
|
|
235
233
|
always = expr.operator == "==" ? "always true" : "always false"
|
|
236
234
|
@warnings << Warning.new(
|
|
@@ -244,24 +242,24 @@ module MilkTea
|
|
|
244
242
|
symbol_name: expr.left.name
|
|
245
243
|
)
|
|
246
244
|
end
|
|
247
|
-
|
|
245
|
+
|
|
248
246
|
def check_redundant_bool_compare(expr)
|
|
249
247
|
return unless %w[== !=].include?(expr.operator)
|
|
250
|
-
|
|
248
|
+
|
|
251
249
|
left_bool = expr.left.is_a?(AST::BooleanLiteral)
|
|
252
250
|
right_bool = expr.right.is_a?(AST::BooleanLiteral)
|
|
253
251
|
return unless left_bool ^ right_bool
|
|
254
|
-
|
|
252
|
+
|
|
255
253
|
literal = left_bool ? expr.left : expr.right
|
|
256
254
|
compared = left_bool ? expr.right : expr.left
|
|
257
255
|
return if compared.is_a?(AST::BooleanLiteral)
|
|
258
|
-
|
|
256
|
+
|
|
259
257
|
suggestion = if expr.operator == "=="
|
|
260
258
|
literal.value ? "use the expression directly" : "invert the expression with 'not'"
|
|
261
259
|
else
|
|
262
260
|
literal.value ? "invert the expression with 'not'" : "use the expression directly"
|
|
263
261
|
end
|
|
264
|
-
|
|
262
|
+
|
|
265
263
|
@warnings << Warning.new(
|
|
266
264
|
path: @path,
|
|
267
265
|
line: expression_line(expr),
|
|
@@ -272,38 +270,38 @@ module MilkTea
|
|
|
272
270
|
severity: :hint,
|
|
273
271
|
)
|
|
274
272
|
end
|
|
275
|
-
|
|
273
|
+
|
|
276
274
|
def redundant_bool_compare_length(expr)
|
|
277
275
|
length = expression_length(expr)
|
|
278
276
|
return length if length
|
|
279
|
-
|
|
277
|
+
|
|
280
278
|
line = expression_line(expr)
|
|
281
279
|
column = expression_column(expr)
|
|
282
280
|
return nil unless line && column
|
|
283
|
-
|
|
281
|
+
|
|
284
282
|
text = source_code_line(line)
|
|
285
283
|
return nil unless text
|
|
286
|
-
|
|
284
|
+
|
|
287
285
|
snippet = text[(column - 1)..]
|
|
288
286
|
return nil unless snippet
|
|
289
|
-
|
|
287
|
+
|
|
290
288
|
right_literal = snippet.match(/\A\s*(.+?)\s*(==|!=)\s*(true|false)\b/)
|
|
291
289
|
return right_literal[0].rstrip.length if right_literal
|
|
292
|
-
|
|
290
|
+
|
|
293
291
|
left_literal = snippet.match(/\A\s*(true|false)\s*(==|!=)\s*(.+?)(?=\s*(?::|\)|,|and\b|or\b|$))/)
|
|
294
292
|
return left_literal[0].rstrip.length if left_literal
|
|
295
|
-
|
|
293
|
+
|
|
296
294
|
nil
|
|
297
295
|
end
|
|
298
|
-
|
|
296
|
+
|
|
299
297
|
def check_duplicate_if_conditions(statement)
|
|
300
298
|
return unless statement.is_a?(AST::IfStmt)
|
|
301
|
-
|
|
299
|
+
|
|
302
300
|
seen_signatures = {}
|
|
303
301
|
statement.branches.each do |branch|
|
|
304
302
|
signature = expression_signature(branch.condition)
|
|
305
303
|
next unless signature
|
|
306
|
-
|
|
304
|
+
|
|
307
305
|
existing = seen_signatures[signature]
|
|
308
306
|
if existing
|
|
309
307
|
@warnings << Warning.new(
|
|
@@ -317,11 +315,11 @@ module MilkTea
|
|
|
317
315
|
)
|
|
318
316
|
next
|
|
319
317
|
end
|
|
320
|
-
|
|
318
|
+
|
|
321
319
|
seen_signatures[signature] = branch
|
|
322
320
|
end
|
|
323
321
|
end
|
|
324
|
-
|
|
322
|
+
|
|
325
323
|
def expression_signature(expression)
|
|
326
324
|
case expression
|
|
327
325
|
when AST::Identifier
|
|
@@ -342,20 +340,20 @@ module MilkTea
|
|
|
342
340
|
left = expression_signature(expression.left)
|
|
343
341
|
right = expression_signature(expression.right)
|
|
344
342
|
return nil unless left && right
|
|
345
|
-
|
|
343
|
+
|
|
346
344
|
"binary:(#{left})#{expression.operator}(#{right})"
|
|
347
345
|
else
|
|
348
346
|
nil
|
|
349
347
|
end
|
|
350
348
|
end
|
|
351
|
-
|
|
349
|
+
|
|
352
350
|
def check_noop_compound_assignment(statement)
|
|
353
351
|
return unless statement.is_a?(AST::Assignment)
|
|
354
352
|
return unless %w[+= -= *= /= |= ^= <<= >>=].include?(statement.operator)
|
|
355
|
-
|
|
353
|
+
|
|
356
354
|
identity = noop_compound_identity_value(statement.operator, statement.value)
|
|
357
355
|
return unless identity
|
|
358
|
-
|
|
356
|
+
|
|
359
357
|
@warnings << Warning.new(
|
|
360
358
|
path: @path,
|
|
361
359
|
line: statement.line,
|
|
@@ -366,7 +364,7 @@ module MilkTea
|
|
|
366
364
|
severity: :hint,
|
|
367
365
|
)
|
|
368
366
|
end
|
|
369
|
-
|
|
367
|
+
|
|
370
368
|
def noop_compound_identity_value(operator, value)
|
|
371
369
|
case operator
|
|
372
370
|
when "+=", "-=", "|=", "^=", "<<=", ">>="
|
|
@@ -377,38 +375,38 @@ module MilkTea
|
|
|
377
375
|
false
|
|
378
376
|
end
|
|
379
377
|
end
|
|
380
|
-
|
|
378
|
+
|
|
381
379
|
def integer_literal_zero?(value)
|
|
382
380
|
value.is_a?(AST::IntegerLiteral) && value.lexeme.gsub("_", "") == "0"
|
|
383
381
|
end
|
|
384
|
-
|
|
382
|
+
|
|
385
383
|
def numeric_literal_one?(value)
|
|
386
384
|
return true if value.is_a?(AST::IntegerLiteral) && value.lexeme.gsub("_", "") == "1"
|
|
387
385
|
return true if value.is_a?(AST::FloatLiteral) && %w[1.0 1.].include?(value.lexeme.gsub("_", ""))
|
|
388
|
-
|
|
386
|
+
|
|
389
387
|
false
|
|
390
388
|
end
|
|
391
389
|
# ── prefer-let-else / prefer-var-else ──────────────────────────────
|
|
392
|
-
|
|
390
|
+
|
|
393
391
|
def emit_prefer_let_else_warnings(stmts)
|
|
394
392
|
emit_prefer_else_warnings(stmts, expected_kind: :let, code: "prefer-let-else")
|
|
395
393
|
end
|
|
396
|
-
|
|
394
|
+
|
|
397
395
|
def emit_prefer_var_else_warnings(stmts)
|
|
398
396
|
emit_prefer_else_warnings(stmts, expected_kind: :var, code: "prefer-var-else")
|
|
399
397
|
end
|
|
400
|
-
|
|
398
|
+
|
|
401
399
|
def emit_prefer_else_warnings(stmts, expected_kind:, code:)
|
|
402
400
|
return if stmts.nil? || stmts.empty?
|
|
403
|
-
|
|
401
|
+
|
|
404
402
|
walk_statement_lists(stmts) do |statement_list|
|
|
405
403
|
statement_list.each_with_index do |_statement, index|
|
|
406
404
|
candidate = prefer_else_candidate(statement_list, index, expected_kind:)
|
|
407
405
|
next unless candidate
|
|
408
|
-
|
|
406
|
+
|
|
409
407
|
branch = candidate[:branch]
|
|
410
408
|
line, column, length = condition_span(branch.condition, line: candidate[:if_stmt].line, keyword_pattern: "if")
|
|
411
|
-
|
|
409
|
+
|
|
412
410
|
@warnings << Warning.new(
|
|
413
411
|
path: @path,
|
|
414
412
|
line:,
|
|
@@ -422,7 +420,7 @@ module MilkTea
|
|
|
422
420
|
end
|
|
423
421
|
end
|
|
424
422
|
end
|
|
425
|
-
|
|
423
|
+
|
|
426
424
|
def prefer_else_candidate(stmts, index, expected_kind:)
|
|
427
425
|
declaration = stmts[index]
|
|
428
426
|
if_stmt = stmts[index + 1]
|
|
@@ -433,39 +431,39 @@ module MilkTea
|
|
|
433
431
|
return if ignored_binding_name?(declaration.name)
|
|
434
432
|
return unless if_stmt.is_a?(AST::IfStmt)
|
|
435
433
|
return unless if_stmt.else_body.nil? && if_stmt.branches.length == 1
|
|
436
|
-
|
|
434
|
+
|
|
437
435
|
branch = if_stmt.branches.first
|
|
438
436
|
identifier = null_equality_identifier(branch.condition)
|
|
439
437
|
return unless identifier&.name == declaration.name
|
|
440
438
|
return unless nullable_binding_declaration?(declaration)
|
|
441
439
|
return if expected_kind == :var && !prefer_var_else_binding_mutated?(declaration)
|
|
442
440
|
return unless ControlFlow::Termination.block_always_terminates?(branch.body, ignore_name: method(:ignored_binding_name?), binding_resolution: cfg_binding_resolution)
|
|
443
|
-
|
|
441
|
+
|
|
444
442
|
{ declaration:, if_stmt:, branch: }
|
|
445
443
|
end
|
|
446
|
-
|
|
444
|
+
|
|
447
445
|
def prefer_var_else_binding_mutated?(declaration)
|
|
448
446
|
binding = @scopes.last[declaration.name]
|
|
449
447
|
binding&.mutated == true
|
|
450
448
|
end
|
|
451
|
-
|
|
449
|
+
|
|
452
450
|
def null_equality_identifier(cond)
|
|
453
451
|
return nil unless cond.is_a?(AST::BinaryOp) && cond.operator == "=="
|
|
454
|
-
|
|
452
|
+
|
|
455
453
|
if cond.left.is_a?(AST::Identifier) && cond.right.is_a?(AST::NullLiteral)
|
|
456
454
|
cond.left
|
|
457
455
|
elsif cond.left.is_a?(AST::NullLiteral) && cond.right.is_a?(AST::Identifier)
|
|
458
456
|
cond.right
|
|
459
457
|
end
|
|
460
458
|
end
|
|
461
|
-
|
|
459
|
+
|
|
462
460
|
def nullable_binding_declaration?(statement)
|
|
463
461
|
binding_type = binding_type_for_declaration(statement)
|
|
464
462
|
return binding_type.is_a?(Types::Nullable) if binding_type
|
|
465
|
-
|
|
463
|
+
|
|
466
464
|
nullable_initializer_without_binding_type?(statement.value)
|
|
467
465
|
end
|
|
468
|
-
|
|
466
|
+
|
|
469
467
|
def nullable_initializer_without_binding_type?(expression)
|
|
470
468
|
case expression
|
|
471
469
|
when AST::Identifier, AST::MemberAccess, AST::IndexAccess, AST::Call, AST::IfExpr, AST::MatchExpr
|
|
@@ -476,14 +474,14 @@ module MilkTea
|
|
|
476
474
|
false
|
|
477
475
|
end
|
|
478
476
|
end
|
|
479
|
-
|
|
477
|
+
|
|
480
478
|
def binding_type_for_declaration(statement)
|
|
481
479
|
binding_resolution = @sema_facts&.binding_resolution
|
|
482
480
|
return nil unless binding_resolution&.binding_types
|
|
483
|
-
|
|
481
|
+
|
|
484
482
|
binding_id = binding_resolution.declaration_binding_ids[statement.object_id]
|
|
485
483
|
return nil unless binding_id
|
|
486
|
-
|
|
484
|
+
|
|
487
485
|
binding_resolution.binding_types[binding_id]
|
|
488
486
|
end
|
|
489
487
|
def pointer_like_cast_expression(expression)
|
|
@@ -495,25 +493,25 @@ module MilkTea
|
|
|
495
493
|
|
|
496
494
|
{ target_type:, source: expression.expression }
|
|
497
495
|
end
|
|
498
|
-
|
|
496
|
+
|
|
499
497
|
def pointer_like_type_ref?(type_ref)
|
|
500
498
|
return false if type_ref.nullable
|
|
501
|
-
|
|
499
|
+
|
|
502
500
|
%w[ptr const_ptr ref].include?(type_ref.name.to_s)
|
|
503
501
|
end
|
|
504
502
|
# ── directional-ffi-arg ──────────────────────────────────────────────
|
|
505
|
-
|
|
503
|
+
|
|
506
504
|
def check_directional_ffi_call(expression)
|
|
507
505
|
call = resolve_directional_ffi_call(expression.callee)
|
|
508
506
|
return unless call
|
|
509
|
-
|
|
507
|
+
|
|
510
508
|
call[:params].zip(expression.arguments).each do |parameter, argument|
|
|
511
509
|
next unless parameter && argument
|
|
512
|
-
|
|
510
|
+
|
|
513
511
|
passing_mode = parameter_passing_mode(parameter)
|
|
514
512
|
next unless %i[in out inout].include?(passing_mode)
|
|
515
513
|
next unless legacy_directional_argument_expression?(argument.value)
|
|
516
|
-
|
|
514
|
+
|
|
517
515
|
@warnings << Warning.new(
|
|
518
516
|
path: @path,
|
|
519
517
|
line: expression_line(argument.value),
|
|
@@ -526,7 +524,7 @@ module MilkTea
|
|
|
526
524
|
)
|
|
527
525
|
end
|
|
528
526
|
end
|
|
529
|
-
|
|
527
|
+
|
|
530
528
|
def resolve_directional_ffi_call(callee)
|
|
531
529
|
case callee
|
|
532
530
|
when AST::Specialization
|
|
@@ -535,56 +533,56 @@ module MilkTea
|
|
|
535
533
|
if @sema_facts && (binding = @sema_facts.functions[callee.name]) && directional_ffi_binding?(binding)
|
|
536
534
|
return { name: binding.name, params: binding.type.params }
|
|
537
535
|
end
|
|
538
|
-
|
|
536
|
+
|
|
539
537
|
if (declaration = @declared_directional_functions[callee.name])
|
|
540
538
|
return { name: declaration.name, params: declaration.params }
|
|
541
539
|
end
|
|
542
|
-
|
|
540
|
+
|
|
543
541
|
nil
|
|
544
542
|
when AST::MemberAccess
|
|
545
543
|
return nil unless callee.receiver.is_a?(AST::Identifier)
|
|
546
544
|
return nil unless @sema_facts
|
|
547
|
-
|
|
545
|
+
|
|
548
546
|
imported_module = @sema_facts.imports[callee.receiver.name]
|
|
549
547
|
return nil unless imported_module
|
|
550
|
-
|
|
548
|
+
|
|
551
549
|
binding = imported_module.functions[callee.member]
|
|
552
550
|
return nil unless directional_ffi_binding?(binding)
|
|
553
|
-
|
|
551
|
+
|
|
554
552
|
{ name: binding.name, params: binding.type.params }
|
|
555
553
|
else
|
|
556
554
|
nil
|
|
557
555
|
end
|
|
558
556
|
end
|
|
559
|
-
|
|
557
|
+
|
|
560
558
|
def directional_ffi_binding?(binding)
|
|
561
559
|
return false unless binding
|
|
562
560
|
return false unless binding.respond_to?(:ast) && (binding.ast.is_a?(AST::ExternFunctionDecl) || binding.ast.is_a?(AST::ForeignFunctionDecl))
|
|
563
|
-
|
|
561
|
+
|
|
564
562
|
binding.type.params.any? { |parameter| %i[in out inout].include?(parameter.passing_mode) }
|
|
565
563
|
end
|
|
566
|
-
|
|
564
|
+
|
|
567
565
|
def parameter_passing_mode(parameter)
|
|
568
566
|
parameter.respond_to?(:passing_mode) ? parameter.passing_mode : parameter.mode
|
|
569
567
|
end
|
|
570
|
-
|
|
568
|
+
|
|
571
569
|
def parameter_name(parameter)
|
|
572
570
|
parameter.respond_to?(:name) ? parameter.name : "argument"
|
|
573
571
|
end
|
|
574
|
-
|
|
572
|
+
|
|
575
573
|
def legacy_directional_argument_expression?(expression)
|
|
576
574
|
return true if expression.is_a?(AST::UnaryOp) && %w[in out inout].include?(expression.operator)
|
|
577
|
-
|
|
575
|
+
|
|
578
576
|
if expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && %w[ptr_of ref_of].include?(expression.callee.name)
|
|
579
577
|
return expression.arguments.length == 1
|
|
580
578
|
end
|
|
581
|
-
|
|
579
|
+
|
|
582
580
|
cast = pointer_like_cast_expression(expression)
|
|
583
581
|
return false unless cast
|
|
584
|
-
|
|
582
|
+
|
|
585
583
|
lvalue_expression?(cast[:source]) || legacy_directional_argument_expression?(cast[:source])
|
|
586
584
|
end
|
|
587
|
-
|
|
585
|
+
|
|
588
586
|
def lvalue_expression?(expression)
|
|
589
587
|
expression.is_a?(AST::Identifier) || expression.is_a?(AST::MemberAccess) || expression.is_a?(AST::IndexAccess)
|
|
590
588
|
end
|