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
LogTypeError(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & pos)78 void Checker::LogTypeError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
79 {
80 LogTypeError(FormatMsg(list), pos);
81 }
82
LogTypeError(std::string_view message,const lexer::SourcePosition & pos)83 void Checker::LogTypeError(std::string_view message, const lexer::SourcePosition &pos)
84 {
85 lexer::LineIndex index(program_->SourceCode());
86 lexer::SourceLocation loc = index.GetLocation(pos);
87
88 errorLogger_.WriteLog(Error {ErrorType::TYPE, program_->SourceFilePath().Utf8(), message, loc.line, loc.col});
89 }
90
Warning(const std::string_view message,const lexer::SourcePosition & pos) const91 void Checker::Warning(const std::string_view message, const lexer::SourcePosition &pos) const
92 {
93 lexer::LineIndex index(program_->SourceCode());
94 lexer::SourceLocation loc = index.GetLocation(pos);
95
96 // NOTE: This should go to stderr but currently the test system does not handle stderr messages
97 auto fileName = program_->SourceFilePath().Utf8();
98 fileName = fileName.substr(fileName.find_last_of(ark::os::file::File::GetPathDelim()) + 1);
99 std::cout << "Warning: " << message << " [" << fileName << ":" << loc.line << ":" << loc.col << "]" << std::endl;
100 }
101
ReportWarning(std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & pos)102 void Checker::ReportWarning(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
103 {
104 Warning(FormatMsg(list), pos);
105 }
106
IsAllTypesAssignableTo(Type * source,Type * target)107 bool Checker::IsAllTypesAssignableTo(Type *source, Type *target)
108 {
109 if (source->TypeFlags() == TypeFlag::UNION) {
110 auto &types = source->AsUnionType()->ConstituentTypes();
111
112 return std::all_of(types.begin(), types.end(),
113 [this, target](auto *it) { return IsAllTypesAssignableTo(it, target); });
114 }
115
116 return relation_->IsAssignableTo(source, target);
117 }
118
IsTypeIdenticalTo(Type * source,Type * target)119 bool Checker::IsTypeIdenticalTo(Type *source, Type *target)
120 {
121 return relation_->IsIdenticalTo(source, target);
122 }
123
IsTypeIdenticalTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)124 bool Checker::IsTypeIdenticalTo(Type *source, Type *target, const std::string &errMsg,
125 const lexer::SourcePosition &errPos)
126 {
127 if (!IsTypeIdenticalTo(source, target)) {
128 relation_->RaiseError(errMsg, errPos);
129 }
130
131 return true;
132 }
133
IsTypeIdenticalTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)134 bool Checker::IsTypeIdenticalTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
135 const lexer::SourcePosition &errPos)
136 {
137 if (!IsTypeIdenticalTo(source, target)) {
138 relation_->RaiseError(list, errPos);
139 }
140
141 return true;
142 }
143
IsTypeAssignableTo(Type * source,Type * target)144 bool Checker::IsTypeAssignableTo(Type *source, Type *target)
145 {
146 return relation_->IsAssignableTo(source, target);
147 }
148
IsTypeAssignableTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)149 bool Checker::IsTypeAssignableTo(Type *source, Type *target, const std::string &errMsg,
150 const lexer::SourcePosition &errPos)
151 {
152 if (!IsTypeAssignableTo(source, target)) {
153 relation_->RaiseError(errMsg, errPos);
154 }
155
156 return true;
157 }
158
IsTypeAssignableTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)159 bool Checker::IsTypeAssignableTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
160 const lexer::SourcePosition &errPos)
161 {
162 if (!IsTypeAssignableTo(source, target)) {
163 relation_->RaiseError(list, errPos);
164 }
165
166 return true;
167 }
168
IsTypeComparableTo(Type * source,Type * target)169 bool Checker::IsTypeComparableTo(Type *source, Type *target)
170 {
171 return relation_->IsComparableTo(source, target);
172 }
173
IsTypeComparableTo(Type * source,Type * target,const std::string & errMsg,const lexer::SourcePosition & errPos)174 bool Checker::IsTypeComparableTo(Type *source, Type *target, const std::string &errMsg,
175 const lexer::SourcePosition &errPos)
176 {
177 if (!IsTypeComparableTo(source, target)) {
178 relation_->RaiseError(errMsg, errPos);
179 }
180
181 return true;
182 }
183
IsTypeComparableTo(Type * source,Type * target,std::initializer_list<TypeErrorMessageElement> list,const lexer::SourcePosition & errPos)184 bool Checker::IsTypeComparableTo(Type *source, Type *target, std::initializer_list<TypeErrorMessageElement> list,
185 const lexer::SourcePosition &errPos)
186 {
187 if (!IsTypeComparableTo(source, target)) {
188 relation_->RaiseError(list, errPos);
189 }
190
191 return true;
192 }
193
AreTypesComparable(Type * source,Type * target)194 bool Checker::AreTypesComparable(Type *source, Type *target)
195 {
196 return IsTypeComparableTo(source, target) || IsTypeComparableTo(target, source);
197 }
198
IsTypeEqualityComparableTo(Type * source,Type * target)199 bool Checker::IsTypeEqualityComparableTo(Type *source, Type *target)
200 {
201 return IsTypeComparableTo(source, target);
202 }
203
Program() const204 parser::Program *Checker::Program() const
205 {
206 return program_;
207 }
208
SetProgram(parser::Program * program)209 void Checker::SetProgram(parser::Program *program)
210 {
211 program_ = program;
212 }
213
VarBinder() const214 varbinder::VarBinder *Checker::VarBinder() const
215 {
216 return varbinder_;
217 }
218
SetAnalyzer(SemanticAnalyzer * analyzer)219 void Checker::SetAnalyzer(SemanticAnalyzer *analyzer)
220 {
221 analyzer_ = analyzer;
222 }
223
GetAnalyzer() const224 checker::SemanticAnalyzer *Checker::GetAnalyzer() const
225 {
226 return analyzer_;
227 }
228
229 } // namespace ark::es2panda::checker
230