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