• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ArkScript-lang / Ark / 22823553555

08 Mar 2026 02:59PM UTC coverage: 93.335% (-0.2%) from 93.519%
22823553555

Pull #654

github

web-flow
Merge a9dde3b83 into caed4a158
Pull Request #654: Feat/show deprecation warnings

5 of 30 new or added lines in 5 files covered. (16.67%)

36 existing lines in 3 files now uncovered.

9438 of 10112 relevant lines covered (93.33%)

271685.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.12
/src/arkreactor/Compiler/AST/Optimizer.cpp
1
#include <Ark/Compiler/AST/Optimizer.hpp>
2

3
#include <Ark/Utils/Utils.hpp>
4

5
namespace Ark::internal
6
{
7
    Optimizer::Optimizer(const unsigned debug) noexcept :
910✔
8
        Pass("Optimizer", debug), m_ast()
455✔
9
    {}
1,365✔
10

11
    void Optimizer::process(const Node& ast)
13✔
12
    {
13✔
13
        // do not handle non-list nodes
14
        if (ast.nodeType() != NodeType::List)
13✔
15
            return;
×
16
        m_ast = ast;
13✔
17

18
        m_logger.traceStart("process");
13✔
19
        countAndPruneDeadCode(m_ast);
13✔
20

21
        // logic: remove piece of code with only 1 reference, if they aren't function calls
22
        pruneUnusedGlobalVariables(m_ast);
13✔
23
        m_logger.traceEnd();
13✔
24

25
        m_logger.trace("AST after name pruning nodes");
13✔
26
        if (m_logger.shouldTrace())
13✔
27
            m_ast.debugPrint(std::cout) << '\n';
×
28
    }
13✔
29

30
    const Node& Optimizer::ast() const noexcept
13✔
31
    {
13✔
32
        return m_ast;
13✔
33
    }
34

35
    void Optimizer::countAndPruneDeadCode(Node& node)
673✔
36
    {
673✔
37
        if (node.nodeType() == NodeType::Symbol || node.nodeType() == NodeType::Capture)
673✔
38
        {
39
            auto [element, inserted] = m_sym_appearances.try_emplace(node.string(), 0);
377✔
40
            if (!inserted)
261✔
41
                element->second++;
116✔
42
        }
261✔
43
        else if (node.nodeType() == NodeType::Field)
412✔
44
        {
45
            for (auto& child : node.list())
12✔
46
                countAndPruneDeadCode(child);
8✔
47
        }
4✔
48
        else if (node.nodeType() == NodeType::List)
408✔
49
        {
50
            // FIXME: very primitive removal of (if true/false ...) and (while false ...)
51
            if (node.constList().size() >= 3 && node.constList().front().nodeType() == NodeType::Keyword &&
378✔
52
                (node.constList().front().keyword() == Keyword::If || node.constList().front().keyword() == Keyword::While))
85✔
53
            {
54
                const auto keyword = node.constList().front().keyword();
12✔
55
                const auto condition = node.constList()[1];
12✔
56
                const auto body = node.constList()[2];
12✔
57

58
                if (condition.nodeType() == NodeType::Symbol && condition.string() == "false")
12✔
59
                {
60
                    // replace the node by an Unused, it is either a (while cond block) or (if cond then)
61
                    if (node.constList().size() == 3)
3✔
62
                        node = Node(NodeType::Unused);
2✔
63
                    else  // it is a (if cond then else)
64
                    {
65
                        const auto back = node.constList().back();
1✔
66
                        node = back;
1✔
67
                    }
1✔
68
                }
3✔
69
                // only update '(if true then [else])' to 'then'
70
                else if (keyword == Keyword::If && condition.nodeType() == NodeType::Symbol && condition.string() == "true")
9✔
71
                    node = body;
1✔
72

73
                // do not try to iterate on the child nodes as they do not exist anymore,
74
                // we performed some optimization that squashed them.
75
                if (!node.isListLike())
12✔
76
                    return;
2✔
77
            }
12✔
78

79
            // iterate over children
80
            for (auto& child : node.list())
865✔
81
                countAndPruneDeadCode(child);
650✔
82
        }
215✔
83
        else if (node.nodeType() == NodeType::Namespace)
191✔
84
            countAndPruneDeadCode(*node.arkNamespace().ast);
2✔
85
    }
673✔
86

87
    void Optimizer::pruneUnusedGlobalVariables(Node& node)
15✔
88
    {
15✔
89
        for (auto& child : node.list())
85✔
90
        {
91
            if (child.nodeType() == NodeType::List && !child.constList().empty() &&
121✔
92
                child.constList()[0].nodeType() == NodeType::Keyword)
51✔
93
            {
94
                const Keyword kw = child.constList()[0].keyword();
29✔
95

96
                // eliminate nested begin blocks
97
                if (kw == Keyword::Begin)
29✔
98
                {
99
                    pruneUnusedGlobalVariables(child);
×
100
                    // skip let/ mut detection
101
                    continue;
×
102
                }
103

104
                // check if it's a let/mut declaration and perform removal
105
                if (kw == Keyword::Let || kw == Keyword::Mut)
29✔
106
                {
107
                    const std::string name = child.constList()[1].string();
26✔
108
                    // a variable was only declared and never used
109
                    if (m_sym_appearances.contains(name) && m_sym_appearances[name] < 1)
26✔
110
                    {
111
                        m_logger.debug("Removing unused variable '{}'", name);
7✔
112
                        // erase the node by turning it to an Unused node
113
                        child = Node(NodeType::Unused);
7✔
114
                    }
7✔
115
                    else if (child.comment().find("@deprecated") != std::string::npos)
19✔
116
                    {
NEW
117
                        std::string advice;
×
NEW
118
                        const std::vector<std::string> vec = Utils::splitString(child.comment(), '\n');
×
NEW
119
                        if (auto result = std::ranges::find_if(vec, [](const std::string& line) {
×
NEW
120
                                return line.find("@deprecated") != std::string::npos;
×
121
                            });
NEW
122
                            result != vec.end())
×
123
                        {
NEW
124
                            advice = result->substr(result->find("@deprecated") + 11);
×
NEW
125
                            Utils::trimWhitespace(advice);
×
NEW
126
                        }
×
127

NEW
128
                        m_logger.warn(
×
129
                            "Using a deprecated {} `{}'.{}",
NEW
130
                            child.constList()[2].nodeType() == NodeType::List &&
×
NEW
131
                                    !child.constList()[2].constList().empty() &&
×
NEW
132
                                    child.constList()[2].constList()[0].nodeType() == NodeType::Keyword &&
×
NEW
133
                                    child.constList()[2].constList()[0].keyword() == Keyword::Fun
×
134
                                ? "function"
135
                                : "value",
136
                            name,
NEW
137
                            advice.empty() ? "" : " " + advice);
×
NEW
138
                    }
×
139
                }
26✔
140
            }
29✔
141
            else if (child.nodeType() == NodeType::Namespace)
41✔
142
                pruneUnusedGlobalVariables(*child.arkNamespace().ast);
2✔
143
        }
70✔
144
    }
15✔
145
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc