1/*
2 * Copyright (c) 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 "checkInfiniteLoop.h"
17#include "ir/statements/forUpdateStatement.h"
18#include "checker/types/type.h"
19#include "ir/statements/doWhileStatement.h"
20#include "ir/statements/whileStatement.h"
21
22namespace ark::es2panda::compiler::ast_verifier {
23
24[[nodiscard]] CheckResult CheckInfiniteLoop::operator()(CheckContext &ctx, const ir::AstNode *ast)
25{
26    if (ast->IsDoWhileStatement()) {
27        return HandleDoWhileStatement(ctx, ast->AsDoWhileStatement());
28    }
29
30    if (ast->IsWhileStatement()) {
31        return HandleWhileStatement(ctx, ast->AsWhileStatement());
32    }
33
34    if (ast->IsForUpdateStatement()) {
35        return HandleForUpdateStatement(ctx, ast->AsForUpdateStatement());
36    }
37
38    return {CheckDecision::CORRECT, CheckAction::CONTINUE};
39}
40
41bool CheckInfiniteLoop::ConditionIsAlwaysTrue(const ir::Expression *const test) const
42{
43    ASSERT(test);
44    auto const *const type = test->TsType();
45    if (type == nullptr) {
46        return false;
47    }
48
49    if (!type->IsConditionalExprType()) {
50        // Cannot be tested for truthiness
51        return false;
52    }
53
54    const auto [constant, truthy] = type->ResolveConditionExpr();
55    return (constant && truthy);
56}
57
58bool CheckInfiniteLoop::HasBreakOrReturnStatement(const ir::Statement *const body) const
59{
60    ASSERT(body);
61    bool hasExit = body->IsBreakStatement() || body->IsReturnStatement();
62    body->IterateRecursively(
63        [&hasExit](ir::AstNode *child) { hasExit |= child->IsBreakStatement() || child->IsReturnStatement(); });
64
65    return hasExit;
66}
67
68[[nodiscard]] CheckResult CheckInfiniteLoop::HandleWhileStatement(CheckContext &ctx,
69                                                                  const ir::WhileStatement *const stmt) const
70{
71    auto const *body = stmt->Body();
72    auto const *test = stmt->Test();
73    if ((body == nullptr) || (test == nullptr)) {
74        return {CheckDecision::CORRECT, CheckAction::CONTINUE};
75    }
76
77    if (ConditionIsAlwaysTrue(test)) {
78        if (!HasBreakOrReturnStatement(body)) {
79            ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start());
80        }
81    }
82
83    return {CheckDecision::CORRECT, CheckAction::CONTINUE};
84}
85
86[[nodiscard]] CheckResult CheckInfiniteLoop::HandleDoWhileStatement(CheckContext &ctx,
87                                                                    const ir::DoWhileStatement *const stmt) const
88{
89    auto const *body = stmt->Body();
90    auto const *test = stmt->Test();
91    if ((body == nullptr) || (test == nullptr)) {
92        return {CheckDecision::CORRECT, CheckAction::CONTINUE};
93    }
94
95    if (ConditionIsAlwaysTrue(test)) {
96        if (!HasBreakOrReturnStatement(body)) {
97            ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start());
98        }
99    }
100
101    return {CheckDecision::CORRECT, CheckAction::CONTINUE};
102}
103
104[[nodiscard]] CheckResult CheckInfiniteLoop::HandleForUpdateStatement(CheckContext &ctx,
105                                                                      const ir::ForUpdateStatement *const stmt) const
106{
107    auto const *body = stmt->Body();
108    if (body == nullptr) {
109        // Body existence is checked in ForLoopCorrectlyInitialized
110        return {CheckDecision::CORRECT, CheckAction::CONTINUE};
111    }
112
113    // Test can be null for for-update statements
114    auto const *test = stmt->Test();
115    if (test == nullptr || ConditionIsAlwaysTrue(test)) {
116        if (!HasBreakOrReturnStatement(body)) {
117            ctx.AddCheckMessage("INFINITE LOOP", *stmt, stmt->Start());
118            return {CheckDecision::CORRECT, CheckAction::CONTINUE};
119        }
120    }
121
122    return {CheckDecision::CORRECT, CheckAction::CONTINUE};
123}
124}  // namespace ark::es2panda::compiler::ast_verifier
125