Coverage Report

Created: 2026-05-30 06:23

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
737M
{
30
737M
    r.insert(s.begin(), s.end());
31
737M
}
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
673M
{
42
673M
    IdSet r;
43
44
673M
    switch (ast_->type) {
45
60.3M
    case AST_APPLY: {
46
60.3M
        assert(dynamic_cast<Apply *>(ast_));
47
60.3M
        auto* ast = static_cast<Apply *>(ast_);
48
60.3M
        append(r, static_analysis(ast->target, in_object, vars));
49
60.3M
        for (const auto &arg : ast->args)
50
99.1M
            append(r, static_analysis(arg.expr, in_object, vars));
51
60.3M
    } break;
52
0
    case AST_APPLY_BRACE: {
53
0
        assert(dynamic_cast<ApplyBrace *>(ast_));
54
        // Nothing to do.
55
0
    } break;
56
14.1M
    case AST_ARRAY: {
57
14.1M
        assert(dynamic_cast<Array *>(ast_));
58
14.1M
        auto* ast = static_cast<Array *>(ast_);
59
14.1M
        for (auto &el : ast->elements)
60
19.3M
            append(r, static_analysis(el.expr, in_object, vars));
61
14.1M
    } break;
62
61.7M
    case AST_BINARY: {
63
61.7M
        assert(dynamic_cast<Binary *>(ast_));
64
61.7M
        auto* ast = static_cast<Binary *>(ast_);
65
61.7M
        append(r, static_analysis(ast->left, in_object, vars));
66
61.7M
        append(r, static_analysis(ast->right, in_object, vars));
67
61.7M
    } break;
68
897k
    case AST_BUILTIN_FUNCTION: {
69
897k
        assert(dynamic_cast<BuiltinFunction *>(ast_));
70
        // Nothing to do.
71
897k
    } break;
72
24.8M
    case AST_CONDITIONAL: {
73
24.8M
        assert(dynamic_cast<Conditional *>(ast_));
74
24.8M
        auto* ast = static_cast<Conditional *>(ast_);
75
24.8M
        append(r, static_analysis(ast->cond, in_object, vars));
76
24.8M
        append(r, static_analysis(ast->branchTrue, in_object, vars));
77
24.8M
        append(r, static_analysis(ast->branchFalse, in_object, vars));
78
24.8M
    } break;
79
13.0M
    case AST_ERROR: {
80
13.0M
        assert(dynamic_cast<Error *>(ast_));
81
13.0M
        auto* ast = static_cast<Error *>(ast_);
82
13.0M
        append(r, static_analysis(ast->expr, in_object, vars));
83
13.0M
    } 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.1M
        for (const auto &p : ast->params) {
90
28.1M
            if (params.find(p.id) != params.end()) {
91
14
                std::string msg = "Duplicate function parameter: " + encode_utf8(p.id->name);
92
14
                throw StaticError(ast_->location, msg);
93
14
            }
94
28.1M
            params.insert(p.id);
95
28.1M
            new_vars.insert(p.id);
96
28.1M
        }
97
98
15.3M
        auto fv = static_analysis(ast->body, in_object, new_vars);
99
28.1M
        for (const auto &p : ast->params) {
100
28.1M
            if (p.expr != nullptr)
101
454k
                append(fv, static_analysis(p.expr, in_object, new_vars));
102
28.1M
        }
103
15.3M
        for (const auto &p : ast->params)
104
28.1M
            fv.erase(p.id);
105
15.3M
        append(r, fv);
106
15.3M
    } break;
107
769
    case AST_IMPORT: {
108
769
        assert(dynamic_cast<Import *>(ast_));
109
        // Nothing to do.
110
769
    } break;
111
1.95k
    case AST_IMPORTSTR: {
112
1.95k
        assert(dynamic_cast<Importstr *>(ast_));
113
        // Nothing to do.
114
1.95k
    } break;
115
21.3k
    case AST_IMPORTBIN: {
116
21.3k
        assert(dynamic_cast<Importbin *>(ast_));
117
        // Nothing to do.
118
21.3k
    } break;
119
237k
    case AST_IN_SUPER: {
120
237k
        assert(dynamic_cast<const InSuper *>(ast_));
121
237k
        auto* ast = static_cast<const InSuper *>(ast_);
122
237k
        if (!in_object)
123
66
            throw StaticError(ast_->location, "Can't use super outside of an object.");
124
237k
        append(r, static_analysis(ast->element, in_object, vars));
125
237k
    } break;
126
66.0M
    case AST_INDEX: {
127
66.0M
        assert(dynamic_cast<const Index *>(ast_));
128
66.0M
        auto* ast = static_cast<const Index *>(ast_);
129
66.0M
        append(r, static_analysis(ast->target, in_object, vars));
130
66.0M
        append(r, static_analysis(ast->index, in_object, vars));
131
66.0M
    } break;
132
32.0M
    case AST_LOCAL: {
133
32.0M
        assert(dynamic_cast<const Local *>(ast_));
134
32.0M
        auto* ast = static_cast<const Local *>(ast_);
135
32.0M
        IdSet ast_vars;
136
54.4M
        for (const auto &bind : ast->binds) {
137
54.4M
            ast_vars.insert(bind.var);
138
54.4M
        }
139
32.0M
        auto new_vars = vars;
140
32.0M
        append(new_vars, ast_vars);
141
32.0M
        IdSet fvs;
142
54.3M
        for (const auto &bind : ast->binds) {
143
54.3M
            append(fvs, static_analysis(bind.body, in_object, new_vars));
144
54.3M
        }
145
146
32.0M
        append(fvs, static_analysis(ast->body, in_object, new_vars));
147
148
32.0M
        for (const auto &bind : ast->binds)
149
54.2M
            fvs.erase(bind.var);
150
151
32.0M
        append(r, fvs);
152
32.0M
    } break;
153
1.88M
    case AST_LITERAL_BOOLEAN: {
154
1.88M
        assert(dynamic_cast<const LiteralBoolean *>(ast_));
155
        // Nothing to do.
156
1.88M
    } break;
157
43.5M
    case AST_LITERAL_NUMBER: {
158
43.5M
        assert(dynamic_cast<const LiteralNumber *>(ast_));
159
        // Nothing to do.
160
43.5M
    } 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.08M
        assert(dynamic_cast<const LiteralNull *>(ast_));
167
        // Nothing to do.
168
1.08M
    } break;
169
4.66M
    case AST_DESUGARED_OBJECT: {
170
4.66M
        assert(dynamic_cast<DesugaredObject *>(ast_));
171
4.66M
        auto* ast = static_cast<DesugaredObject *>(ast_);
172
19.0M
        for (auto &field : ast->fields) {
173
19.0M
            append(r, static_analysis(field.name, in_object, vars));
174
19.0M
            append(r, static_analysis(field.body, true, vars));
175
19.0M
        }
176
4.66M
        for (AST *assert : ast->asserts) {
177
156k
            append(r, static_analysis(assert, true, vars));
178
156k
        }
179
4.66M
    } break;
180
2.63M
    case AST_OBJECT_COMPREHENSION_SIMPLE: {
181
2.63M
        assert(dynamic_cast<ObjectComprehensionSimple *>(ast_));
182
2.63M
        auto* ast = static_cast<ObjectComprehensionSimple *>(ast_);
183
2.63M
        auto new_vars = vars;
184
2.63M
        new_vars.insert(ast->id);
185
2.63M
        append(r, static_analysis(ast->field, false, new_vars));
186
2.63M
        append(r, static_analysis(ast->value, true, new_vars));
187
2.63M
        r.erase(ast->id);
188
2.63M
        append(r, static_analysis(ast->array, in_object, vars));
189
2.63M
    } break;
190
5.30M
    case AST_SELF: {
191
5.30M
        assert(dynamic_cast<const Self *>(ast_));
192
5.30M
        if (!in_object)
193
31
            throw StaticError(ast_->location, "Can't use self outside of an object.");
194
5.30M
    } break;
195
5.30M
    case AST_SUPER_INDEX: {
196
237k
        assert(dynamic_cast<const SuperIndex *>(ast_));
197
237k
        auto* ast = static_cast<const SuperIndex *>(ast_);
198
237k
        if (!in_object)
199
41
            throw StaticError(ast_->location, "Can't use super outside of an object.");
200
237k
        append(r, static_analysis(ast->index, in_object, vars));
201
237k
    } break;
202
2.80M
    case AST_UNARY: {
203
2.80M
        assert(dynamic_cast<const Unary *>(ast_));
204
2.80M
        auto* ast = static_cast<const Unary *>(ast_);
205
2.80M
        append(r, static_analysis(ast->expr, in_object, vars));
206
2.80M
    } 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.68k
            throw StaticError(ast->location, "Unknown variable: " + encode_utf8(ast->id->name));
212
1.68k
        }
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
673M
    }
220
221
673M
    for (auto *id : r)
222
923M
        ast_->freeVariables.push_back(id);
223
224
673M
    return r;
225
673M
}
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