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
35namespace ark::es2panda::checker {
36Checker::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
44void Checker::Initialize(varbinder::VarBinder *varbinder)
45{
46    varbinder_ = varbinder;
47    scope_ = varbinder_->TopScope();
48    program_ = varbinder_->Program();
49}
50
51std::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
78void Checker::ThrowTypeError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
79{
80    ThrowTypeError(FormatMsg(list), pos);
81}
82
83void 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
91void Checker::LogTypeError(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
92{
93    LogTypeError(FormatMsg(list), pos);
94}
95
96void 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
104void 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
115void Checker::ReportWarning(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
116{
117    Warning(FormatMsg(list), pos);
118}
119
120bool 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
132bool Checker::IsTypeIdenticalTo(Type *source, Type *target)
133{
134    return relation_->IsIdenticalTo(source, target);
135}
136
137bool 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
147bool 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
157bool Checker::IsTypeAssignableTo(Type *source, Type *target)
158{
159    return relation_->IsAssignableTo(source, target);
160}
161
162bool 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
172bool 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
182bool Checker::IsTypeComparableTo(Type *source, Type *target)
183{
184    return relation_->IsComparableTo(source, target);
185}
186
187bool 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
197bool 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
207bool Checker::AreTypesComparable(Type *source, Type *target)
208{
209    return IsTypeComparableTo(source, target) || IsTypeComparableTo(target, source);
210}
211
212bool Checker::IsTypeEqualityComparableTo(Type *source, Type *target)
213{
214    return IsTypeComparableTo(source, target);
215}
216
217parser::Program *Checker::Program() const
218{
219    return program_;
220}
221
222void Checker::SetProgram(parser::Program *program)
223{
224    program_ = program;
225}
226
227varbinder::VarBinder *Checker::VarBinder() const
228{
229    return varbinder_;
230}
231
232void Checker::SetAnalyzer(SemanticAnalyzer *analyzer)
233{
234    analyzer_ = analyzer;
235}
236
237checker::SemanticAnalyzer *Checker::GetAnalyzer() const
238{
239    return analyzer_;
240}
241
242}  // namespace ark::es2panda::checker
243