1
+ # -----------------------------------------------------------------------
2
+ # This file is part of MoonScript
3
+ #
4
+ # MoonSript is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # MoonSript is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with MoonSript. If not, see <https://www.gnu.org/licenses/>.
16
+ #
17
+ # Copyright (C) 2025 Krisna Pranav, MoonScript Developers
18
+ # -----------------------------------------------------------------------
19
+
20
+ module MoonScript
21
+ class TypeChecker
22
+ def check (node : Ast ::Call )
23
+ function_type =
24
+ resolve node.expression
25
+
26
+ check_call(node, function_type)
27
+ end
28
+
29
+ def check_call (node, function_type) : Checkable
30
+ return error! :call_not_a_function do
31
+ snippet " The entity you called is not a function:" , function_type
32
+ snippet " The call in question:" , node
33
+ end unless function_type.name == " Function"
34
+
35
+ argument_size =
36
+ function_type.parameters.size - 1
37
+
38
+ required_argument_size =
39
+ case function_type
40
+ when TypeChecker ::Type
41
+ argument_size - function_type.optional_count
42
+ else
43
+ argument_size
44
+ end
45
+
46
+ parameters =
47
+ [] of Checkable
48
+
49
+ error! :call_argument_size_mismatch do
50
+ block do
51
+ text " The function called"
52
+ bold argument_size.to_s
53
+ text " arguments, while you tried to call it with"
54
+ bold " #{ node.arguments.size } ."
55
+ end
56
+
57
+ snippet " The type of the function is:" , function_type
58
+ snippet " The call in question is here:" , node
59
+ end if node.arguments.size > argument_size ||
60
+ node.arguments.size < required_argument_size
61
+
62
+ args =
63
+ if node.arguments.all?(& .key.nil?)
64
+ node.arguments
65
+ elsif node.arguments.all?(& .key.!= (nil ))
66
+ node.arguments.sort_by do |argument |
67
+ index =
68
+ function_type
69
+ .parameters
70
+ .index { |param | param.label == argument.key.try(& .value) }
71
+
72
+ error! :call_not_found_argument do
73
+ snippet(
74
+ " looking for a named argument, can't find it:" ,
75
+ argument.key.try(& .value).to_s)
76
+
77
+ snippet " The type of the function is:" , function_type
78
+ snippet " The call in question is here:" , node
79
+ end unless index
80
+
81
+ index
82
+ end
83
+ else
84
+ error! :call_with_mixed_arguments do
85
+ block " A call cannot have named and unamed arguments at the same " \
86
+ " time because in specific cases I cannot pair the arguments " \
87
+ " with the values."
88
+
89
+ snippet " The call in question is here:" , node
90
+ end
91
+ end
92
+
93
+ args.each_with_index do |argument , index |
94
+ argument_type =
95
+ resolve argument
96
+
97
+ function_argument_type =
98
+ function_type.parameters[index]
99
+
100
+ error! :call_argument_type_mismatch do
101
+ ordinal =
102
+ ordinal(index + 1 )
103
+
104
+ block do
105
+ text " The"
106
+ bold " #{ ordinal } argument"
107
+ text " to a function is causing a mismatch."
108
+ end
109
+
110
+ snippet " The function is expecting the #{ ordinal } argument to be:" , function_argument_type
111
+ snippet " Instead it is:" , argument_type
112
+ snippet " The call in question is here:" , node
113
+ end unless res = Comparer .compare(function_argument_type, argument_type)
114
+
115
+ parameters << res
116
+ end
117
+
118
+ if (optional_param_count = argument_size - args.size) > 0
119
+ parameters.concat(function_type.parameters[args.size, optional_param_count])
120
+ end
121
+
122
+ call_type =
123
+ Type .new(" Function" , parameters + [function_type.parameters.last])
124
+
125
+ result =
126
+ Comparer .compare(function_type, call_type)
127
+
128
+ error! :impossible_error do
129
+ block " You have run into an impossible error. Please file an issue " \
130
+ " with a reproducible example in the Github repository."
131
+
132
+ snippet " Call type:" , call_type
133
+ snippet " Function type:" , function_type
134
+ snippet node
135
+ end unless result
136
+
137
+ final = resolve_type(result.parameters.last)
138
+
139
+ if node.await && final.name == " Promise"
140
+ final.parameters.first
141
+ else
142
+ final
143
+ end
144
+ end
145
+ end
146
+ end
0 commit comments