Coverage Report

Created: 2026-10-17 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jsonnet/core/static_analysis.cpp
Line
Count
Source
1
/*
2
Copyright 2015 Google Inc. All rights reserved.
3
4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7
8
    http://www.apache.org/licenses/LICENSE-2.0
9
10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16
17
#include <set>
18
19
#include "ast.h"
20
#include "static_analysis.h"
21
#include "static_error.h"
22
23
namespace jsonnet::internal {
24
25
typedef std::set<const Identifier *> IdSet;
26
27
/** Inserts all of s into r. */
28
static void append(IdSet &r, const IdSet &s)
29
741M
{
30
741M
    r.insert(s.begin(), s.end());
31
741M
}
32
33
/** Statically analyse the given ast.
34
 *
35
 * \param ast_ The AST.
36
 * \param in_object Whether or not ast_ is within the lexical scope of an object AST.
37
 * \param vars The variables defined within lexical scope of ast_.
38
 * \returns The free variables in ast_.
39
 */
40
static IdSet static_analysis(AST *ast_, bool in_object, const IdSet &vars)
41
677M
{
42
677M
    IdSet r;
43
44
677M
    switch (ast_->type) {
45
60.5M
    case AST_APPLY: {
46
60.5M
        assert(dynamic_cast<Apply *>(ast_));
47
60.5M
        auto* ast = static_cast<Apply *>(ast_);
48
60.5M
        append(r, static_analysis(ast->target, in_object, vars));
49
60.5M
        for (const auto &arg : ast->args)
50
99.5M
            append(r, static_analysis(arg.expr, in_object, vars));
51
60.5M
    } break;
52
0
    case AST_APPLY_BRACE: {
53
0
        assert(dynamic_cast<ApplyBrace *>(ast_));
54
        // Nothing to do.
55
0
    } break;
56
14.2M
    case AST_ARRAY: {
57
14.2M
        assert(dynamic_cast<Array *>(ast_));
58
14.2M
        auto* ast = static_cast<Array *>(ast_);
59
14.2M
        for (auto &el : ast->elements)
60
20.9M
            append(r, static_analysis(el.expr, in_object, vars));
61
14.2M
    } break;
62
61.8M
    case AST_BINARY: {
63
61.8M
        assert(dynamic_cast<Binary *>(ast_));
64
61.8M
        auto* ast = static_cast<Binary *>(ast_);
65
61.8M
        append(r, static_analysis(ast->left, in_object, vars));
66
61.8M
        append(r, static_analysis(ast->right, in_object, vars));
67
61.8M
    } break;
68
898k
    case AST_BUILTIN_FUNCTION: {
69
898k
        assert(dynamic_cast<BuiltinFunction *>(ast_));
70
        // Nothing to do.
71
898k
    } break;
72
24.9M
    case AST_CONDITIONAL: {
73
24.9M
        assert(dynamic_cast<Conditional *>(ast_));
74
24.9M
        auto* ast = static_cast<Conditional *>(ast_);
75
24.9M
        append(r, static_analysis(ast->cond, in_object, vars));
76
24.9M
        append(r, static_analysis(ast->branchTrue, in_object, vars));
77
24.9M
        append(r, static_analysis(ast->branchFalse, in_object, vars));
78
24.9M
    } break;
79
13.2M
    case AST_ERROR: {
80
13.2M
        assert(dynamic_cast<Error *>(ast_));
81
13.2M
        auto* ast = static_cast<Error *>(ast_);
82
13.2M
        append(r, static_analysis(ast->expr, in_object, vars));
83
13.2M
    } break;
84
15.3M
    case AST_FUNCTION: {
85
15.3M
        assert(dynamic_cast<Function *>(ast_));
86
15.3M
        auto* ast = static_cast<Function *>(ast_);
87
15.3M
        auto new_vars = vars;
88
15.3M
        IdSet params;
89
28.2M
        for (const auto &p : ast->params) {
90
28.2M
            if (params.find(p.id) != params.end()) {
91
18
                std::string msg = "Duplicate function parameter: " + encode_utf8(p.id->name);
92
18
                throw StaticError(ast_->location, msg);
93
18
            }
94
28.2M
            params.insert(p.id);
95
28.2M
            new_vars.insert(p.id);
96
28.2M
        }
97
98
15.3M
        auto fv = static_analysis(ast->body, in_object, new_vars);
99
28.2M
        for (const auto &p : ast->params) {
100
28.2M
            if (p.expr != nullptr)
101
457k
                append(fv, static_analysis(p.expr, in_object, new_vars));
102
28.2M
        }
103
15.3M
        for (const auto &p : ast->params)
104
28.2M
            fv.erase(p.id);
105
15.3M
        append(r, fv);
106
15.3M
    } break;
107
2.70k
    case AST_IMPORT: {
108
2.70k
        assert(dynamic_cast<Import *>(ast_));
109
        // Nothing to do.
110
2.70k
    } break;
111
2.70k
    case AST_IMPORTSTR: {
112
2.28k
        assert(dynamic_cast<Importstr *>(ast_));
113
        // Nothing to do.
114
2.28k
    } break;
115
35.5k
    case AST_IMPORTBIN: {
116
35.5k
        assert(dynamic_cast<Importbin *>(ast_));
117
        // Nothing to do.
118
35.5k
    } break;
119
178k
    case AST_IN_SUPER: {
120
178k
        assert(dynamic_cast<const InSuper *>(ast_));
121
178k
        auto* ast = static_cast<const InSuper *>(ast_);
122
178k
        if (!in_object)
123
69
            throw StaticError(ast_->location, "Can't use super outside of an object.");
124
178k
        append(r, static_analysis(ast->element, in_object, vars));
125
178k
    } break;
126
66.3M
    case AST_INDEX: {
127
66.3M
        assert(dynamic_cast<const Index *>(ast_));
128
66.3M
        auto* ast = static_cast<const Index *>(ast_);
129
66.3M
        append(r, static_analysis(ast->target, in_object, vars));
130
66.3M
        append(r, static_analysis(ast->index, in_object, vars));
131
66.3M
    } break;
132
32.1M
    case AST_LOCAL: {
133
32.1M
        assert(dynamic_cast<const Local *>(ast_));
134
32.1M
        auto* ast = static_cast<const Local *>(ast_);
135
32.1M
        IdSet ast_vars;
136
54.0M
        for (const auto &bind : ast->binds) {
137
54.0M
            ast_vars.insert(bind.var);
138
54.0M
        }
139
32.1M
        auto new_vars = vars;
140
32.1M
        append(new_vars, ast_vars);
141
32.1M
        IdSet fvs;
142
53.9M
        for (const auto &bind : ast->binds) {
143
53.9M
            append(fvs, static_analysis(bind.body, in_object, new_vars));
144
53.9M
        }
145
146
32.1M
        append(fvs, static_analysis(ast->body, in_object, new_vars));
147
148
32.1M
        for (const auto &bind : ast->binds)
149
53.9M
            fvs.erase(bind.var);
150
151
32.1M
        append(r, fvs);
152
32.1M
    } break;
153
2.07M
    case AST_LITERAL_BOOLEAN: {
154
2.07M
        assert(dynamic_cast<const LiteralBoolean *>(ast_));
155
        // Nothing to do.
156
2.07M
    } break;
157
45.4M
    case AST_LITERAL_NUMBER: {
158
45.4M
        assert(dynamic_cast<const LiteralNumber *>(ast_));
159
        // Nothing to do.
160
45.4M
    } break;
161
118M
    case AST_LITERAL_STRING: {
162
118M
        assert(dynamic_cast<const LiteralString *>(ast_));
163
        // Nothing to do.
164
118M
    } break;
165
118M
    case AST_LITERAL_NULL: {
166
1.11M
        assert(dynamic_cast<const LiteralNull *>(ast_));
167
        // Nothing to do.
168
1.11M
    } break;
169
4.51M
    case AST_DESUGARED_OBJECT: {
170
4.51M
        assert(dynamic_cast<DesugaredObject *>(ast_));
171
4.51M
        auto* ast = static_cast<DesugaredObject *>(ast_);
172
19.1M
        for (auto &field : ast->fields) {
173
19.1M
            append(r, static_analysis(field.name, in_object, vars));
174
19.1M
            append(r, static_analysis(field.body, true, vars));
175
19.1M
        }
176
4.51M
        for (AST *assert : ast->asserts) {
177
342k
            append(r, static_analysis(assert, true, vars));
178
342k
        }
179
4.51M
    } break;
180
2.64M
    case AST_OBJECT_COMPREHENSION_SIMPLE: {
181
2.64M
        assert(dynamic_cast<ObjectComprehensionSimple *>(ast_));
182
2.64M
        auto* ast = static_cast<ObjectComprehensionSimple *>(ast_);
183
2.64M
        auto new_vars = vars;
184
2.64M
        new_vars.insert(ast->id);
185
2.64M
        append(r, static_analysis(ast->field, false, new_vars));
186
2.64M
        append(r, static_analysis(ast->value, true, new_vars));
187
2.64M
        r.erase(ast->id);
188
2.64M
        append(r, static_analysis(ast->array, in_object, vars));
189
2.64M
    } break;
190
5.42M
    case AST_SELF: {
191
5.42M
        assert(dynamic_cast<const Self *>(ast_));
192
5.42M
        if (!in_object)
193
36
            throw StaticError(ast_->location, "Can't use self outside of an object.");
194
5.42M
    } break;
195
5.42M
    case AST_SUPER_INDEX: {
196
178k
        assert(dynamic_cast<const SuperIndex *>(ast_));
197
178k
        auto* ast = static_cast<const SuperIndex *>(ast_);
198
178k
        if (!in_object)
199
37
            throw StaticError(ast_->location, "Can't use super outside of an object.");
200
178k
        append(r, static_analysis(ast->index, in_object, vars));
201
178k
    } break;
202
2.90M
    case AST_UNARY: {
203
2.90M
        assert(dynamic_cast<const Unary *>(ast_));
204
2.90M
        auto* ast = static_cast<const Unary *>(ast_);
205
2.90M
        append(r, static_analysis(ast->expr, in_object, vars));
206
2.90M
    } break;
207
204M
    case AST_VAR: {
208
204M
        assert(dynamic_cast<const Var *>(ast_));
209
204M
        auto* ast = static_cast<const Var *>(ast_);
210
204M
        if (vars.find(ast->id) == vars.end()) {
211
1.74k
            throw StaticError(ast->location, "Unknown variable: " + encode_utf8(ast->id->name));
212
1.74k
        }
213
204M
        r.insert(ast->id);
214
204M
    } break;
215
0
    default:
216
0
        std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl;
217
0
        std::abort();
218
0
        break;
219
677M
    }
220
221
677M
    for (auto *id : r)
222
921M
        ast_->freeVariables.push_back(id);
223
224
677M
    return r;
225
677M
}
226
227
void jsonnet_static_analysis(AST *ast)
228
21.3k
{
229
21.3k
    static_analysis(ast, false, IdSet{});
230
21.3k
}
231
232
}  // namespace jsonnet::internal