1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "checker.h"
17
18 #include "checker/types/type.h"
19 #include "ir/expression.h"
20 #include "ir/statements/blockStatement.h"
21 #include "parser/program/program.h"
22 #include "util/helpers.h"
23 #include "varbinder/varbinder.h"
24 #include "varbinder/scope.h"
25 #include "varbinder/variable.h"
26 #include "es2panda.h"
27 #include "checker/types/globalTypesHolder.h"
28 #include "checker/types/ts/unionType.h"
29 #include "checker/types/signature.h"
30
31 #include <cstdint>
32 #include <initializer_list>
33 #include <memory>
34
35 namespace ark::es2panda::checker {
Checker()36 Checker::Checker()
37 : allocator_(SpaceType::SPACE_TYPE_COMPILER, nullptr, true),
38 context_(this, CheckerStatus::NO_OPTS),
39 globalTypes_(allocator_.New<GlobalTypesHolder>(&allocator_)),
40 relation_(allocator_.New<TypeRelation>(this))
41 {
42 }
43
Initialize(varbinder::VarBinder * varbinder)44 void Checker::Initialize(varbinder::VarBinder *varbinder)
45 {
46 varbinder_ = varbinder;
47 scope_ = varbinder_->TopScope();
48 program_ = varbinder_->Program();
49 }
50
FormatMsg(std::initializer_list<TypeErrorMessageElement> list)51 std::string Checker::FormatMsg(std::initializer_list<TypeErrorMessageElement> list)
52 {
53 std::stringstream ss;
54
55 for (const auto &it : list) {
56 if (std::holds_alternative<char *>(it)) {
57 ss << std::get<char *>(it);
58 } else if (std::holds_alternative<util::StringView>(it)) {
59 ss << std::get<util::StringView>(it);
60 } else if (std::holds_alternative<lexer::TokenType>(it)) {
61 ss << TokenToString(std::get<lexer::TokenType>(it));
62 } else if (std::holds_alternative<const Type *>(it)) {
63 std::get<const Type *>(it)->ToString(ss);
64 } else if (std::holds_alternative<AsSrc>(it)) {
65 std::get<AsSrc>(it).GetType()->ToStringAsSrc(ss);
66 } else if (std::holds_alternative<size_t>(it)) {
67 ss << std::to_string(std::get<size_t>(it));
68 } else if (std::holds_alternative<const Signature *>(it)) {
69 std::get<const Signature *>(it)->ToString(ss, nullptr, true);
70 } else {
71 UNREACHABLE();
72 }
73 }
74
75 return ss.str();
76 }
77
ThrowTypeError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & pos)78 void Checker::ThrowTypeError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
79 {
80 ThrowTypeError(FormatMsg(list), pos);
81 }
82
ThrowTypeError(std::string_view message,const lexer::SourcePosition & pos)83 void Checker::ThrowTypeError(std::string_view message, const lexer::SourcePosition &pos)
84 {
85 lexer::LineIndex index(program_->SourceCode());
86 lexer::SourceLocation loc = index.GetLocation(pos);
87
88 throw Error {ErrorType::TYPE, program_->SourceFilePath().Utf8(), message, loc.line, loc.col};
89 }
90
LogTypeError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & pos)91 void Checker::LogTypeError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
92 {
93 LogTypeError(FormatMsg(list), pos);
94 }
95
LogTypeError(std::string_view message,const lexer::SourcePosition & pos)96 void Checker::LogTypeError(std::string_view message, const lexer::SourcePosition &pos)
97 {
98 lexer::LineIndex index(program_->SourceCode());
99 lexer::SourceLocation loc = index.GetLocation(pos);
100
101 errorLogger_.WriteLog(Error {ErrorType::TYPE, program_->SourceFilePath().Utf8(), message, loc.line, loc.col});
102 }
103
Warning(const std::string_view message,const lexer::SourcePosition & pos) const104 void Checker::Warning(const std::string_view message, const lexer::SourcePosition &pos) const
105 {
106 lexer::LineIndex index(program_->SourceCode());
107 lexer::SourceLocation loc = index.GetLocation(pos);
108
109 // NOTE: This should go to stderr but currently the test system does not handle stderr messages
110 auto fileName = program_->SourceFilePath().Utf8();
111 fileName = fileName.substr(fileName.find_last_of(ark::os::file::File::GetPathDelim()) + 1);
112 std::cout << "Warning: " << message << " [" << fileName << ":" << loc.line << ":" << loc.col << "]" << std::endl;
113 }
114
ReportWarning(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & pos)115 void Checker::ReportWarning(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
116 {
117 Warning(FormatMsg(list), pos);
118 }
119
IsAllTypesAssignableTo(Type * source,Type * target)120 bool Checker::IsAllTypesAssignableTo(Type *source, Type *target)
121 {
122 if (source->TypeFlags() == TypeFlag::UNION) {
123 auto &types = source->AsUnionType()->ConstituentTypes();
124
125 return std::all_of(types.begin(), types.end(),
126 [this, target](auto *it) { return IsAllTypesAssignableTo(it, target); });
127 }
128
129 return relation_->IsAssignableTo(source, target);
130 }
131
IsTypeIdenticalTo(Type * source,Type * target)132 bool Checker::IsTypeIdenticalTo(Type *source, Type *target)
133 {
134 return relation_->IsIdenticalTo(source, target);
135 }
136
IsTypeIdenticalTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)137 bool Checker::IsTypeIdenticalTo(Type *source, Type *target, const std::string &errMsg,
138 const lexer::SourcePosition &errPos)
139 {
140 if (!IsTypeIdenticalTo(source, target)) {
141 relation_->RaiseError(errMsg, errPos);
142 }
143
144 return true;
145 }
146
IsTypeIdenticalTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)147 bool Checker::IsTypeIdenticalTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
148 const lexer::SourcePosition &errPos)
149 {
150 if (!IsTypeIdenticalTo(source, target)) {
151 relation_->RaiseError(list, errPos);
152 }
153
154 return true;
155 }
156
IsTypeAssignableTo(Type * source,Type * target)157 bool Checker::IsTypeAssignableTo(Type *source, Type *target)
158 {
159 return relation_->IsAssignableTo(source, target);
160 }
161
IsTypeAssignableTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)162 bool Checker::IsTypeAssignableTo(Type *source, Type *target, const std::string &errMsg,
163 const lexer::SourcePosition &errPos)
164 {
165 if (!IsTypeAssignableTo(source, target)) {
166 relation_->RaiseError(errMsg, errPos);
167 }
168
169 return true;
170 }
171
IsTypeAssignableTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)172 bool Checker::IsTypeAssignableTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
173 const lexer::SourcePosition &errPos)
174 {
175 if (!IsTypeAssignableTo(source, target)) {
176 relation_->RaiseError(list, errPos);
177 }
178
179 return true;
180 }
181
IsTypeComparableTo(Type * source,Type * target)182 bool Checker::IsTypeComparableTo(Type *source, Type *target)
183 {
184 return relation_->IsComparableTo(source, target);
185 }
186
IsTypeComparableTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)187 bool Checker::IsTypeComparableTo(Type *source, Type *target, const std::string &errMsg,
188 const lexer::SourcePosition &errPos)
189 {
190 if (!IsTypeComparableTo(source, target)) {
191 relation_->RaiseError(errMsg, errPos);
192 }
193
194 return true;
195 }
196
IsTypeComparableTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)197 bool Checker::IsTypeComparableTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
198 const lexer::SourcePosition &errPos)
199 {
200 if (!IsTypeComparableTo(source, target)) {
201 relation_->RaiseError(list, errPos);
202 }
203
204 return true;
205 }
206
AreTypesComparable(Type * source,Type * target)207 bool Checker::AreTypesComparable(Type *source, Type *target)
208 {
209 return IsTypeComparableTo(source, target) || IsTypeComparableTo(target, source);
210 }
211
IsTypeEqualityComparableTo(Type * source,Type * target)212 bool Checker::IsTypeEqualityComparableTo(Type *source, Type *target)
213 {
214 return IsTypeComparableTo(source, target);
215 }
216
Program() const217 parser::Program *Checker::Program() const
218 {
219 return program_;
220 }
221
SetProgram(parser::Program * program)222 void Checker::SetProgram(parser::Program *program)
223 {
224 program_ = program;
225 }
226
VarBinder() const227 varbinder::VarBinder *Checker::VarBinder() const
228 {
229 return varbinder_;
230 }
231
SetAnalyzer(SemanticAnalyzer * analyzer)232 void Checker::SetAnalyzer(SemanticAnalyzer *analyzer)
233 {
234 analyzer_ = analyzer;
235 }
236
GetAnalyzer() const237 checker::SemanticAnalyzer *Checker::GetAnalyzer() const
238 {
239 return analyzer_;
240 }
241
242 } // namespace ark::es2panda::checker
243