Coverage Report

Created: 2025-08-29 06:46

/src/jsonnet/core/formatter.cpp
Line
Count
Source (jump to first uncovered line)
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 <algorithm>
18
#include <set>
19
#include <typeinfo>
20
21
#include "formatter.h"
22
#include "lexer.h"
23
#include "pass.h"
24
#include "string_utils.h"
25
#include "unicode.h"
26
27
namespace jsonnet::internal {
28
29
static std::string unparse_id(const Identifier *id)
30
0
{
31
0
    return encode_utf8(id->name);
32
0
}
33
34
/** If left recursive, return the left hand side, else return nullptr. */
35
static AST *left_recursive(AST *ast_)
36
0
{
37
0
    if (auto *ast = dynamic_cast<Apply *>(ast_))
38
0
        return ast->target;
39
0
    if (auto *ast = dynamic_cast<ApplyBrace *>(ast_))
40
0
        return ast->left;
41
0
    if (auto *ast = dynamic_cast<Binary *>(ast_))
42
0
        return ast->left;
43
0
    if (auto *ast = dynamic_cast<Index *>(ast_))
44
0
        return ast->target;
45
0
    if (auto *ast = dynamic_cast<InSuper *>(ast_))
46
0
        return ast->element;
47
0
    return nullptr;
48
0
}
49
static const AST *left_recursive(const AST *ast_)
50
0
{
51
0
    return left_recursive(const_cast<AST *>(ast_));
52
0
}
53
54
/** The transitive closure of left_recursive). */
55
static AST *left_recursive_deep(AST *ast_)
56
0
{
57
0
    AST *last = ast_;
58
0
    AST *left = left_recursive(ast_);
59
60
0
    while (left != nullptr) {
61
0
        last = left;
62
0
        left = left_recursive(last);
63
0
    }
64
0
    return last;
65
0
}
66
67
/** Pretty-print fodder.
68
 *
69
 * \param fodder The fodder to print
70
 * \param space_before Whether a space should be printed before any other output.
71
 * \param separate_token If the last fodder was an interstitial, whether a space should follow it.
72
 * \param final Whether fodder is the last one in
73
 */
74
void fodder_fill(std::ostream &o, const Fodder &fodder, bool space_before, bool separate_token, bool final)
75
0
{
76
0
    unsigned last_indent = 0;
77
0
    size_t index = 0;
78
0
    for (const auto &fod : fodder) {
79
0
        bool skip_trailing = final && (index == (fodder.size() - 1));
80
0
        switch (fod.kind) {
81
0
            case FodderElement::LINE_END:
82
0
                if (fod.comment.size() > 0)
83
0
                    o << "  " << fod.comment[0];
84
0
                o << '\n';
85
0
                if (!skip_trailing) {
86
0
                    o << std::string(fod.blanks, '\n');
87
0
                    o << std::string(fod.indent, ' ');
88
0
                }
89
0
                last_indent = fod.indent;
90
0
                space_before = false;
91
0
                break;
92
93
0
            case FodderElement::INTERSTITIAL:
94
0
                if (space_before)
95
0
                    o << ' ';
96
0
                o << fod.comment[0];
97
0
                space_before = true;
98
0
                break;
99
100
0
            case FodderElement::PARAGRAPH: {
101
0
                bool first = true;
102
0
                for (const std::string &l : fod.comment) {
103
                    // Do not indent empty lines (note: first line is never empty).
104
0
                    if (l.length() > 0) {
105
                        // First line is already indented by previous fod.
106
0
                        if (!first)
107
0
                            o << std::string(last_indent, ' ');
108
0
                        o << l;
109
0
                    }
110
0
                    o << '\n';
111
0
                    first = false;
112
0
                }
113
0
                if (!skip_trailing) {
114
0
                    o << std::string(fod.blanks, '\n');
115
0
                    o << std::string(fod.indent, ' ');
116
0
                }
117
0
                last_indent = fod.indent;
118
0
                space_before = false;
119
0
            } break;
120
0
        }
121
0
        ++index;
122
0
    }
123
0
    if (separate_token && space_before)
124
0
        o << ' ';
125
0
}
126
127
/** A model of fodder_fill that just keeps track of the column counter. */
128
static void fodder_count(unsigned &column, const Fodder &fodder, bool space_before,
129
                         bool separate_token)
130
0
{
131
0
    for (const auto &fod : fodder) {
132
0
        switch (fod.kind) {
133
0
            case FodderElement::PARAGRAPH:
134
0
            case FodderElement::LINE_END:
135
0
                column = fod.indent;
136
0
                space_before = false;
137
0
                break;
138
139
0
            case FodderElement::INTERSTITIAL:
140
0
                if (space_before)
141
0
                    column++;
142
0
                column += fod.comment[0].length();
143
0
                space_before = true;
144
0
                break;
145
0
        }
146
0
    }
147
0
    if (separate_token && space_before)
148
0
        column++;
149
0
}
150
151
class Unparser {
152
   public:
153
   private:
154
    std::ostream &o;
155
    FmtOpts opts;
156
157
   public:
158
0
    Unparser(std::ostream &o, const FmtOpts &opts) : o(o), opts(opts) {}
159
160
    void unparseSpecs(const std::vector<ComprehensionSpec> &specs)
161
0
    {
162
0
        for (const auto &spec : specs) {
163
0
            fill(spec.openFodder, true, true);
164
0
            switch (spec.kind) {
165
0
                case ComprehensionSpec::FOR:
166
0
                    o << "for";
167
0
                    fill(spec.varFodder, true, true);
168
0
                    o << unparse_id(spec.var);
169
0
                    fill(spec.inFodder, true, true);
170
0
                    o << "in";
171
0
                    unparse(spec.expr, true);
172
0
                    break;
173
0
                case ComprehensionSpec::IF:
174
0
                    o << "if";
175
0
                    unparse(spec.expr, true);
176
0
                    break;
177
0
            }
178
0
        }
179
0
    }
180
181
    void fill(const Fodder &fodder, bool space_before, bool separate_token)
182
0
    {
183
0
        fodder_fill(o, fodder, space_before, separate_token, false);
184
0
    }
185
186
    void fill_final(const Fodder &fodder, bool space_before, bool separate_token)
187
0
    {
188
0
        fodder_fill(o, fodder, space_before, separate_token, true);
189
0
    }
190
191
    void unparseParams(const Fodder &fodder_l, const ArgParams &params, bool trailing_comma,
192
                       const Fodder &fodder_r)
193
0
    {
194
0
        fill(fodder_l, false, false);
195
0
        o << "(";
196
0
        bool first = true;
197
0
        for (const auto &param : params) {
198
0
            if (!first)
199
0
                o << ",";
200
0
            fill(param.idFodder, !first, true);
201
0
            o << unparse_id(param.id);
202
0
            if (param.expr != nullptr) {
203
                // default arg, no spacing: x=e
204
0
                fill(param.eqFodder, false, false);
205
0
                o << "=";
206
0
                unparse(param.expr, false);
207
0
            }
208
0
            fill(param.commaFodder, false, false);
209
0
            first = false;
210
0
        }
211
0
        if (trailing_comma)
212
0
            o << ",";
213
0
        fill(fodder_r, false, false);
214
0
        o << ")";
215
0
    }
216
217
    void unparseFieldParams(const ObjectField &field)
218
0
    {
219
0
        if (field.methodSugar) {
220
0
            unparseParams(field.fodderL, field.params, field.trailingComma, field.fodderR);
221
0
        }
222
0
    }
223
224
    void unparseFields(const ObjectFields &fields, bool space_before)
225
0
    {
226
0
        bool first = true;
227
0
        for (const auto &field : fields) {
228
0
            if (!first)
229
0
                o << ',';
230
231
0
            switch (field.kind) {
232
0
                case ObjectField::LOCAL: {
233
0
                    fill(field.fodder1, !first || space_before, true);
234
0
                    o << "local";
235
0
                    fill(field.fodder2, true, true);
236
0
                    o << unparse_id(field.id);
237
0
                    unparseFieldParams(field);
238
0
                    fill(field.opFodder, true, true);
239
0
                    o << "=";
240
0
                    unparse(field.expr2, true);
241
0
                } break;
242
243
0
                case ObjectField::FIELD_ID:
244
0
                case ObjectField::FIELD_STR:
245
0
                case ObjectField::FIELD_EXPR: {
246
0
                    if (field.kind == ObjectField::FIELD_ID) {
247
0
                        fill(field.fodder1, !first || space_before, true);
248
0
                        o << unparse_id(field.id);
249
250
0
                    } else if (field.kind == ObjectField::FIELD_STR) {
251
0
                        unparse(field.expr1, !first || space_before);
252
253
0
                    } else if (field.kind == ObjectField::FIELD_EXPR) {
254
0
                        fill(field.fodder1, !first || space_before, true);
255
0
                        o << "[";
256
0
                        unparse(field.expr1, false);
257
0
                        fill(field.fodder2, false, false);
258
0
                        o << "]";
259
0
                    }
260
0
                    unparseFieldParams(field);
261
262
0
                    fill(field.opFodder, false, false);
263
264
0
                    if (field.superSugar)
265
0
                        o << "+";
266
0
                    switch (field.hide) {
267
0
                        case ObjectField::INHERIT: o << ":"; break;
268
0
                        case ObjectField::HIDDEN: o << "::"; break;
269
0
                        case ObjectField::VISIBLE: o << ":::"; break;
270
0
                    }
271
0
                    unparse(field.expr2, true);
272
273
0
                } break;
274
275
0
                case ObjectField::ASSERT: {
276
0
                    fill(field.fodder1, !first || space_before, true);
277
0
                    o << "assert";
278
0
                    unparse(field.expr2, true);
279
0
                    if (field.expr3 != nullptr) {
280
0
                        fill(field.opFodder, true, true);
281
0
                        o << ":";
282
0
                        unparse(field.expr3, true);
283
0
                    }
284
0
                } break;
285
0
            }
286
287
0
            first = false;
288
0
            fill(field.commaFodder, false, false);
289
0
        }
290
0
    }
291
292
    /** Unparse the given AST.
293
     *
294
     * \param ast_ The AST to be unparsed.
295
     *
296
     * \param precedence The precedence of the enclosing AST.  If this is greater than the current
297
     * precedence, parens are not needed.
298
     */
299
    void unparse(const AST *ast_, bool space_before)
300
0
    {
301
0
        bool separate_token = !left_recursive(ast_);
302
303
0
        fill(ast_->openFodder, space_before, separate_token);
304
305
0
        if (auto *ast = dynamic_cast<const Apply *>(ast_)) {
306
0
            unparse(ast->target, space_before);
307
0
            fill(ast->fodderL, false, false);
308
0
            o << "(";
309
0
            bool first = true;
310
0
            for (const auto &arg : ast->args) {
311
0
                if (!first)
312
0
                    o << ',';
313
0
                bool space = !first;
314
0
                if (arg.id != nullptr) {
315
0
                    fill(arg.idFodder, space, true);
316
0
                    o << unparse_id(arg.id);
317
0
                    space = false;
318
0
                    o << "=";
319
0
                }
320
0
                unparse(arg.expr, space);
321
0
                fill(arg.commaFodder, false, false);
322
0
                first = false;
323
0
            }
324
0
            if (ast->trailingComma)
325
0
                o << ",";
326
0
            fill(ast->fodderR, false, false);
327
0
            o << ")";
328
0
            if (ast->tailstrict) {
329
0
                fill(ast->tailstrictFodder, true, true);
330
0
                o << "tailstrict";
331
0
            }
332
333
0
        } else if (auto *ast = dynamic_cast<const ApplyBrace *>(ast_)) {
334
0
            unparse(ast->left, space_before);
335
0
            unparse(ast->right, true);
336
337
0
        } else if (auto *ast = dynamic_cast<const Array *>(ast_)) {
338
0
            o << "[";
339
0
            bool first = true;
340
0
            for (const auto &element : ast->elements) {
341
0
                if (!first)
342
0
                    o << ',';
343
0
                unparse(element.expr, !first || opts.padArrays);
344
0
                fill(element.commaFodder, false, false);
345
0
                first = false;
346
0
            }
347
0
            if (ast->trailingComma)
348
0
                o << ",";
349
0
            fill(ast->closeFodder, ast->elements.size() > 0, opts.padArrays);
350
0
            o << "]";
351
352
0
        } else if (auto *ast = dynamic_cast<const ArrayComprehension *>(ast_)) {
353
0
            o << "[";
354
0
            unparse(ast->body, opts.padArrays);
355
0
            fill(ast->commaFodder, false, false);
356
0
            if (ast->trailingComma)
357
0
                o << ",";
358
0
            unparseSpecs(ast->specs);
359
0
            fill(ast->closeFodder, true, opts.padArrays);
360
0
            o << "]";
361
362
0
        } else if (auto *ast = dynamic_cast<const Assert *>(ast_)) {
363
0
            o << "assert";
364
0
            unparse(ast->cond, true);
365
0
            if (ast->message != nullptr) {
366
0
                fill(ast->colonFodder, true, true);
367
0
                o << ":";
368
0
                unparse(ast->message, true);
369
0
            }
370
0
            fill(ast->semicolonFodder, false, false);
371
0
            o << ";";
372
0
            unparse(ast->rest, true);
373
374
0
        } else if (auto *ast = dynamic_cast<const Binary *>(ast_)) {
375
0
            unparse(ast->left, space_before);
376
0
            fill(ast->opFodder, true, true);
377
0
            o << bop_string(ast->op);
378
            // The - 1 is for left associativity.
379
0
            unparse(ast->right, true);
380
381
0
        } else if (auto *ast = dynamic_cast<const BuiltinFunction *>(ast_)) {
382
0
            o << "/* builtin " << ast->name << " */ null";
383
384
0
        } else if (auto *ast = dynamic_cast<const Conditional *>(ast_)) {
385
0
            o << "if";
386
0
            unparse(ast->cond, true);
387
0
            fill(ast->thenFodder, true, true);
388
0
            o << "then";
389
0
            if (ast->branchFalse != nullptr) {
390
0
                unparse(ast->branchTrue, true);
391
0
                fill(ast->elseFodder, true, true);
392
0
                o << "else";
393
0
                unparse(ast->branchFalse, true);
394
0
            } else {
395
0
                unparse(ast->branchTrue, true);
396
0
            }
397
398
0
        } else if (dynamic_cast<const Dollar *>(ast_)) {
399
0
            o << "$";
400
401
0
        } else if (auto *ast = dynamic_cast<const Error *>(ast_)) {
402
0
            o << "error";
403
0
            unparse(ast->expr, true);
404
405
0
        } else if (auto *ast = dynamic_cast<const Function *>(ast_)) {
406
0
            o << "function";
407
0
            unparseParams(
408
0
                ast->parenLeftFodder, ast->params, ast->trailingComma, ast->parenRightFodder);
409
0
            unparse(ast->body, true);
410
411
0
        } else if (auto *ast = dynamic_cast<const Import *>(ast_)) {
412
0
            o << "import";
413
0
            unparse(ast->file, true);
414
415
0
        } else if (auto *ast = dynamic_cast<const Importstr *>(ast_)) {
416
0
            o << "importstr";
417
0
            unparse(ast->file, true);
418
419
0
        } else if (auto *ast = dynamic_cast<const Importbin *>(ast_)) {
420
0
            o << "importbin";
421
0
            unparse(ast->file, true);
422
423
0
        } else if (auto *ast = dynamic_cast<const InSuper *>(ast_)) {
424
0
            unparse(ast->element, true);
425
0
            fill(ast->inFodder, true, true);
426
0
            o << "in";
427
0
            fill(ast->superFodder, true, true);
428
0
            o << "super";
429
430
0
        } else if (auto *ast = dynamic_cast<const Index *>(ast_)) {
431
0
            unparse(ast->target, space_before);
432
0
            fill(ast->dotFodder, false, false);
433
0
            if (ast->id != nullptr) {
434
0
                o << ".";
435
0
                fill(ast->idFodder, false, false);
436
0
                o << unparse_id(ast->id);
437
0
            } else {
438
0
                o << "[";
439
0
                if (ast->isSlice) {
440
0
                    if (ast->index != nullptr) {
441
0
                        unparse(ast->index, false);
442
0
                    }
443
0
                    fill(ast->endColonFodder, false, false);
444
0
                    o << ":";
445
0
                    if (ast->end != nullptr) {
446
0
                        unparse(ast->end, false);
447
0
                    }
448
0
                    if (ast->step != nullptr || ast->stepColonFodder.size() > 0) {
449
0
                        fill(ast->stepColonFodder, false, false);
450
0
                        o << ":";
451
0
                        if (ast->step != nullptr) {
452
0
                            unparse(ast->step, false);
453
0
                        }
454
0
                    }
455
0
                } else {
456
0
                    unparse(ast->index, false);
457
0
                }
458
0
                fill(ast->idFodder, false, false);
459
0
                o << "]";
460
0
            }
461
462
0
        } else if (auto *ast = dynamic_cast<const Local *>(ast_)) {
463
0
            o << "local";
464
0
            assert(ast->binds.size() > 0);
465
0
            bool first = true;
466
0
            for (const auto &bind : ast->binds) {
467
0
                if (!first)
468
0
                    o << ",";
469
0
                first = false;
470
0
                fill(bind.varFodder, true, true);
471
0
                o << unparse_id(bind.var);
472
0
                if (bind.functionSugar) {
473
0
                    unparseParams(bind.parenLeftFodder,
474
0
                                  bind.params,
475
0
                                  bind.trailingComma,
476
0
                                  bind.parenRightFodder);
477
0
                }
478
0
                fill(bind.opFodder, true, true);
479
0
                o << "=";
480
0
                unparse(bind.body, true);
481
0
                fill(bind.closeFodder, false, false);
482
0
            }
483
0
            o << ";";
484
0
            unparse(ast->body, true);
485
486
0
        } else if (auto *ast = dynamic_cast<const LiteralBoolean *>(ast_)) {
487
0
            o << (ast->value ? "true" : "false");
488
489
0
        } else if (auto *ast = dynamic_cast<const LiteralNumber *>(ast_)) {
490
0
            o << ast->originalString;
491
492
0
        } else if (auto *ast = dynamic_cast<const LiteralString *>(ast_)) {
493
0
            assert(ast->tokenKind != LiteralString::RAW_DESUGARED);
494
0
            if (ast->tokenKind == LiteralString::DOUBLE) {
495
0
                o << "\"";
496
0
                o << encode_utf8(ast->value);
497
0
                o << "\"";
498
0
            } else if (ast->tokenKind == LiteralString::SINGLE) {
499
0
                o << "'";
500
0
                o << encode_utf8(ast->value);
501
0
                o << "'";
502
0
            } else if (ast->tokenKind == LiteralString::BLOCK) {
503
0
                o << "|||";
504
0
                if (ast->value.back() != U'\n') {
505
0
                    o << "-";
506
0
                }
507
0
                o << "\n";
508
0
                if (ast->value.c_str()[0] != U'\n')
509
0
                    o << ast->blockIndent;
510
0
                for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
511
                    // Formatter always outputs in unix mode.
512
0
                    if (*cp == '\r') continue;
513
0
                    std::string utf8;
514
0
                    encode_utf8(*cp, utf8);
515
0
                    o << utf8;
516
0
                    if (*cp == U'\n' && *(cp + 1) != U'\n' && *(cp + 1) != U'\0') {
517
0
                        o << ast->blockIndent;
518
0
                    }
519
0
                }
520
0
                if (ast->value.back() != U'\n') {
521
0
                    o << "\n";
522
0
                }
523
0
                o << ast->blockTermIndent << "|||";
524
0
            } else if (ast->tokenKind == LiteralString::VERBATIM_DOUBLE) {
525
0
                o << "@\"";
526
0
                for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
527
0
                    if (*cp == U'"') {
528
0
                        o << "\"\"";
529
0
                    } else {
530
0
                        std::string utf8;
531
0
                        encode_utf8(*cp, utf8);
532
0
                        o << utf8;
533
0
                    }
534
0
                }
535
0
                o << "\"";
536
0
            } else if (ast->tokenKind == LiteralString::VERBATIM_SINGLE) {
537
0
                o << "@'";
538
0
                for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
539
0
                    if (*cp == U'\'') {
540
0
                        o << "''";
541
0
                    } else {
542
0
                        std::string utf8;
543
0
                        encode_utf8(*cp, utf8);
544
0
                        o << utf8;
545
0
                    }
546
0
                }
547
0
                o << "'";
548
0
            }
549
550
0
        } else if (dynamic_cast<const LiteralNull *>(ast_)) {
551
0
            o << "null";
552
553
0
        } else if (auto *ast = dynamic_cast<const Object *>(ast_)) {
554
0
            o << "{";
555
0
            unparseFields(ast->fields, opts.padObjects);
556
0
            if (ast->trailingComma)
557
0
                o << ",";
558
0
            fill(ast->closeFodder, ast->fields.size() > 0, opts.padObjects);
559
0
            o << "}";
560
561
0
        } else if (auto *ast = dynamic_cast<const DesugaredObject *>(ast_)) {
562
0
            o << "{";
563
0
            for (AST *assert : ast->asserts) {
564
0
                o << "assert";
565
0
                unparse(assert, true);
566
0
                o << ",";
567
0
            }
568
0
            for (auto &field : ast->fields) {
569
0
                o << "[";
570
0
                unparse(field.name, false);
571
0
                o << "]";
572
0
                switch (field.hide) {
573
0
                    case ObjectField::INHERIT: o << ":"; break;
574
0
                    case ObjectField::HIDDEN: o << "::"; break;
575
0
                    case ObjectField::VISIBLE: o << ":::"; break;
576
0
                }
577
0
                unparse(field.body, true);
578
0
                o << ",";
579
0
            }
580
0
            o << "}";
581
582
0
        } else if (auto *ast = dynamic_cast<const ObjectComprehension *>(ast_)) {
583
0
            o << "{";
584
0
            unparseFields(ast->fields, opts.padObjects);
585
0
            if (ast->trailingComma)
586
0
                o << ",";
587
0
            unparseSpecs(ast->specs);
588
0
            fill(ast->closeFodder, true, opts.padObjects);
589
0
            o << "}";
590
591
0
        } else if (auto *ast = dynamic_cast<const ObjectComprehensionSimple *>(ast_)) {
592
0
            o << "{[";
593
0
            unparse(ast->field, false);
594
0
            o << "]:";
595
0
            unparse(ast->value, true);
596
0
            o << " for " << unparse_id(ast->id) << " in";
597
0
            unparse(ast->array, true);
598
0
            o << "}";
599
600
0
        } else if (auto *ast = dynamic_cast<const Parens *>(ast_)) {
601
0
            o << "(";
602
0
            unparse(ast->expr, false);
603
0
            fill(ast->closeFodder, false, false);
604
0
            o << ")";
605
606
0
        } else if (dynamic_cast<const Self *>(ast_)) {
607
0
            o << "self";
608
609
0
        } else if (auto *ast = dynamic_cast<const SuperIndex *>(ast_)) {
610
0
            o << "super";
611
0
            fill(ast->dotFodder, false, false);
612
0
            if (ast->id != nullptr) {
613
0
                o << ".";
614
0
                fill(ast->idFodder, false, false);
615
0
                o << unparse_id(ast->id);
616
0
            } else {
617
0
                o << "[";
618
0
                unparse(ast->index, false);
619
0
                fill(ast->idFodder, false, false);
620
0
                o << "]";
621
0
            }
622
623
0
        } else if (auto *ast = dynamic_cast<const Unary *>(ast_)) {
624
0
            o << uop_string(ast->op);
625
0
            unparse(ast->expr, false);
626
627
0
        } else if (auto *ast = dynamic_cast<const Var *>(ast_)) {
628
0
            o << encode_utf8(ast->id->name);
629
630
0
        } else {
631
0
            std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl;
632
0
            std::abort();
633
0
        }
634
0
    }
635
};
636
637
/********************************************************************************
638
 * The rest of this file contains transformations on the ASTs before unparsing. *
639
 ********************************************************************************/
640
641
/** A generic Pass that does nothing but can be extended to easily define real passes.
642
 */
643
class FmtPass : public CompilerPass {
644
   protected:
645
    FmtOpts opts;
646
647
   public:
648
0
    FmtPass(Allocator &alloc, const FmtOpts &opts) : CompilerPass(alloc), opts(opts) {}
649
};
650
651
class EnforceStringStyle : public FmtPass {
652
    using FmtPass::visit;
653
654
   public:
655
0
    EnforceStringStyle(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
656
    void visit(LiteralString *lit)
657
0
    {
658
0
        assert(lit->tokenKind != LiteralString::RAW_DESUGARED);
659
0
        if (lit->tokenKind == LiteralString::BLOCK)
660
0
            return;
661
0
        if (lit->tokenKind == LiteralString::VERBATIM_DOUBLE)
662
0
            return;
663
0
        if (lit->tokenKind == LiteralString::VERBATIM_SINGLE)
664
0
            return;
665
0
        UString canonical = jsonnet_string_unescape(lit->location, lit->value);
666
0
        unsigned num_single = 0, num_double = 0;
667
0
        for (char32_t c : canonical) {
668
0
            if (c == '\'')
669
0
                num_single++;
670
0
            if (c == '"')
671
0
                num_double++;
672
0
        }
673
0
        if (num_single > 0 && num_double > 0)
674
0
            return;  // Don't change it.
675
0
        bool use_single = opts.stringStyle == 's';
676
0
        if (num_single > 0)
677
0
            use_single = false;
678
0
        if (num_double > 0)
679
0
            use_single = true;
680
681
        // Change it.
682
0
        lit->value = jsonnet_string_escape(canonical, use_single);
683
0
        lit->tokenKind = use_single ? LiteralString::SINGLE : LiteralString::DOUBLE;
684
0
    }
685
};
686
687
class EnforceCommentStyle : public FmtPass {
688
   public:
689
    bool firstFodder;
690
    EnforceCommentStyle(Allocator &alloc, const FmtOpts &opts)
691
0
        : FmtPass(alloc, opts), firstFodder(true)
692
0
    {
693
0
    }
694
    /** Change the comment to match the given style, but don't break she-bang.
695
     *
696
     * If preserve_hash is true, do not touch a comment that starts with #!.
697
     */
698
    void fixComment(std::string &s, bool preserve_hash)
699
0
    {
700
0
        if (opts.commentStyle == 'h' && s[0] == '/') {
701
0
            s = "#" + s.substr(2);
702
0
        }
703
0
        if (opts.commentStyle == 's' && s[0] == '#') {
704
0
            if (preserve_hash && s[1] == '!')
705
0
                return;
706
0
            s = "//" + s.substr(1);
707
0
        }
708
0
    }
709
    void fodder(Fodder &fodder)
710
0
    {
711
0
        for (auto &f : fodder) {
712
0
            switch (f.kind) {
713
0
                case FodderElement::LINE_END:
714
0
                case FodderElement::PARAGRAPH:
715
0
                    if (f.comment.size() == 1) {
716
0
                        fixComment(f.comment[0], firstFodder);
717
0
                    }
718
0
                    break;
719
720
0
                case FodderElement::INTERSTITIAL: break;
721
0
            }
722
0
            firstFodder = false;
723
0
        }
724
0
    }
725
};
726
727
class EnforceMaximumBlankLines : public FmtPass {
728
   public:
729
0
    EnforceMaximumBlankLines(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
730
    void fodderElement(FodderElement &f)
731
0
    {
732
0
        if (f.kind != FodderElement::INTERSTITIAL)
733
0
            if (f.blanks > opts.maxBlankLines)
734
0
                f.blanks = opts.maxBlankLines;
735
0
    }
736
};
737
738
class StripComments : public FmtPass {
739
   public:
740
0
    StripComments(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
741
    void fodder(Fodder &fodder)
742
0
    {
743
0
        Fodder copy = fodder;
744
0
        fodder.clear();
745
0
        for (auto &f : copy) {
746
0
            if (f.kind == FodderElement::LINE_END)
747
0
                fodder.push_back(f);
748
0
        }
749
0
    }
750
};
751
752
class StripEverything : public FmtPass {
753
   public:
754
0
    StripEverything(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
755
    void fodder(Fodder &fodder)
756
0
    {
757
0
        fodder.clear();
758
0
    }
759
};
760
761
class StripAllButComments : public FmtPass {
762
   public:
763
0
    StripAllButComments(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
764
    Fodder comments;
765
    void fodder(Fodder &fodder)
766
0
    {
767
0
        for (auto &f : fodder) {
768
0
            if (f.kind == FodderElement::PARAGRAPH) {
769
0
                comments.emplace_back(FodderElement::PARAGRAPH, 0, 0, f.comment);
770
0
            } else if (f.kind == FodderElement::INTERSTITIAL) {
771
0
                comments.push_back(f);
772
0
                comments.emplace_back(FodderElement::LINE_END, 0, 0, std::vector<std::string>{});
773
0
            }
774
0
        }
775
0
        fodder.clear();
776
0
    }
777
    virtual void file(AST *&body, Fodder &final_fodder)
778
0
    {
779
0
        expr(body);
780
0
        fodder(final_fodder);
781
0
        body = alloc.make<LiteralNull>(body->location, comments);
782
0
        final_fodder.clear();
783
0
    }
784
};
785
786
/** These cases are infix so we descend on the left to find the fodder. */
787
static Fodder &open_fodder(AST *ast_)
788
0
{
789
0
    return left_recursive_deep(ast_)->openFodder;
790
0
}
791
792
/** Strip blank lines from the top of the file. */
793
void remove_initial_newlines(AST *ast)
794
0
{
795
0
    Fodder &f = open_fodder(ast);
796
0
    while (f.size() > 0 && f[0].kind == FodderElement::LINE_END)
797
0
        f.erase(f.begin());
798
0
}
799
800
0
void remove_extra_trailing_newlines(Fodder &final_fodder) {
801
0
    if (!final_fodder.empty()) {
802
0
        final_fodder.back().blanks = 0;
803
0
    }
804
0
}
805
806
bool contains_newline(const Fodder &fodder)
807
0
{
808
0
    for (const auto &f : fodder) {
809
0
        if (f.kind != FodderElement::INTERSTITIAL)
810
0
            return true;
811
0
    }
812
0
    return false;
813
0
}
814
815
/* Commas should appear at the end of an object/array only if the closing token is on a new line. */
816
class FixTrailingCommas : public FmtPass {
817
    using FmtPass::visit;
818
819
   public:
820
0
    FixTrailingCommas(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
821
    Fodder comments;
822
823
    // Generalized fix that works across a range of ASTs.
824
    void fix_comma(Fodder &last_comma_fodder, bool &trailing_comma, Fodder &close_fodder)
825
0
    {
826
0
        bool need_comma = contains_newline(close_fodder) || contains_newline(last_comma_fodder);
827
0
        if (trailing_comma) {
828
0
            if (!need_comma) {
829
                // Remove it but keep fodder.
830
0
                trailing_comma = false;
831
0
                fodder_move_front(close_fodder, last_comma_fodder);
832
0
            } else if (contains_newline(last_comma_fodder)) {
833
                // The comma is needed but currently is separated by a newline.
834
0
                fodder_move_front(close_fodder, last_comma_fodder);
835
0
            }
836
0
        } else {
837
0
            if (need_comma) {
838
                // There was no comma, but there was a newline before the ] so add a comma.
839
0
                trailing_comma = true;
840
0
            }
841
0
        }
842
0
    }
843
844
    void remove_comma(Fodder &last_comma_fodder, bool &trailing_comma, Fodder &close_fodder)
845
0
    {
846
0
        if (trailing_comma) {
847
            // Remove it but keep fodder.
848
0
            trailing_comma = false;
849
0
            close_fodder = concat_fodder(last_comma_fodder, close_fodder);
850
0
            last_comma_fodder.clear();
851
0
        }
852
0
    }
853
854
    void visit(Array *expr)
855
0
    {
856
0
        if (expr->elements.size() == 0) {
857
            // No comma present and none can be added.
858
0
            return;
859
0
        }
860
861
0
        fix_comma(expr->elements.back().commaFodder, expr->trailingComma, expr->closeFodder);
862
0
        FmtPass::visit(expr);
863
0
    }
864
865
    void visit(ArrayComprehension *expr)
866
0
    {
867
0
        remove_comma(expr->commaFodder, expr->trailingComma, expr->specs[0].openFodder);
868
0
        FmtPass::visit(expr);
869
0
    }
870
871
    void visit(Object *expr)
872
0
    {
873
0
        if (expr->fields.size() == 0) {
874
            // No comma present and none can be added.
875
0
            return;
876
0
        }
877
878
0
        fix_comma(expr->fields.back().commaFodder, expr->trailingComma, expr->closeFodder);
879
0
        FmtPass::visit(expr);
880
0
    }
881
882
    void visit(ObjectComprehension *expr)
883
0
    {
884
0
        remove_comma(expr->fields.back().commaFodder, expr->trailingComma, expr->closeFodder);
885
0
        FmtPass::visit(expr);
886
0
    }
887
};
888
889
/* Remove nested parens. */
890
class FixParens : public FmtPass {
891
    using FmtPass::visit;
892
893
   public:
894
0
    FixParens(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
895
    void visit(Parens *expr)
896
0
    {
897
0
        if (auto *body = dynamic_cast<Parens *>(expr->expr)) {
898
            // Deal with fodder.
899
0
            expr->expr = body->expr;
900
0
            fodder_move_front(open_fodder(body->expr), body->openFodder);
901
0
            fodder_move_front(expr->closeFodder, body->closeFodder);
902
0
        }
903
0
        FmtPass::visit(expr);
904
0
    }
905
};
906
907
/* Ensure ApplyBrace syntax sugar is used in the case of A + { }. */
908
class FixPlusObject : public FmtPass {
909
    using FmtPass::visit;
910
911
   public:
912
0
    FixPlusObject(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
913
    void visitExpr(AST *&expr)
914
0
    {
915
0
        if (auto *bin_op = dynamic_cast<Binary *>(expr)) {
916
            // Could relax this to allow more ASTs on the LHS but this seems OK for now.
917
0
            if (dynamic_cast<Var *>(bin_op->left) || dynamic_cast<Index *>(bin_op->left)) {
918
0
                if (AST *rhs = dynamic_cast<Object *>(bin_op->right)) {
919
0
                    if (bin_op->op == BOP_PLUS) {
920
0
                        fodder_move_front(rhs->openFodder, bin_op->opFodder);
921
0
                        expr = alloc.make<ApplyBrace>(
922
0
                            bin_op->location, bin_op->openFodder, bin_op->left, rhs);
923
0
                    }
924
0
                }
925
0
            }
926
0
        }
927
0
        FmtPass::visitExpr(expr);
928
0
    }
929
};
930
931
/* Remove final colon in slices. */
932
class NoRedundantSliceColon : public FmtPass {
933
    using FmtPass::visit;
934
935
   public:
936
0
    NoRedundantSliceColon(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
937
938
    void visit(Index *expr)
939
0
    {
940
0
        if (expr->isSlice) {
941
0
            if (expr->step == nullptr) {
942
0
                if (expr->stepColonFodder.size() > 0) {
943
0
                    fodder_move_front(expr->idFodder, expr->stepColonFodder);
944
0
                }
945
0
            }
946
0
        }
947
0
        FmtPass::visit(expr);
948
0
    }
949
};
950
951
/* Ensure syntax sugar is used where possible. */
952
class PrettyFieldNames : public FmtPass {
953
    using FmtPass::visit;
954
955
   public:
956
0
    PrettyFieldNames(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
957
958
    bool isIdentifier(const UString &str)
959
0
    {
960
        // Identifiers cannot be zero-length.
961
0
        if (str.length() == 0) return false;
962
963
0
        bool first = true;
964
0
        for (char32_t c : str) {
965
0
            if (!first && c >= '0' && c <= '9')
966
0
                continue;
967
0
            first = false;
968
0
            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_'))
969
0
                continue;
970
0
            return false;
971
0
        }
972
        // Filter out keywords.
973
0
        if (lex_get_keyword_kind(encode_utf8(str)) != Token::IDENTIFIER)
974
0
            return false;
975
0
        return true;
976
0
    }
977
978
    void visit(Index *expr)
979
0
    {
980
0
        if (!expr->isSlice && expr->index != nullptr) {
981
            // Maybe we can use an id instead.
982
0
            if (auto *lit = dynamic_cast<LiteralString *>(expr->index)) {
983
0
                if (isIdentifier(lit->value)) {
984
0
                    expr->id = alloc.makeIdentifier(lit->value);
985
0
                    expr->idFodder = lit->openFodder;
986
0
                    expr->index = nullptr;
987
0
                }
988
0
            }
989
0
        }
990
0
        FmtPass::visit(expr);
991
0
    }
992
993
    void visit(Object *expr)
994
0
    {
995
0
        for (auto &field : expr->fields) {
996
            // First try ["foo"] -> "foo".
997
0
            if (field.kind == ObjectField::FIELD_EXPR) {
998
0
                if (auto *field_expr = dynamic_cast<LiteralString *>(field.expr1)) {
999
0
                    field.kind = ObjectField::FIELD_STR;
1000
0
                    fodder_move_front(field_expr->openFodder, field.fodder1);
1001
0
                    if (field.methodSugar) {
1002
0
                        fodder_move_front(field.fodderL, field.fodder2);
1003
0
                    } else {
1004
0
                        fodder_move_front(field.opFodder, field.fodder2);
1005
0
                    }
1006
0
                }
1007
0
            }
1008
            // Then try "foo" -> foo.
1009
0
            if (field.kind == ObjectField::FIELD_STR) {
1010
0
                if (auto *lit = dynamic_cast<LiteralString *>(field.expr1)) {
1011
0
                    if (isIdentifier(lit->value)) {
1012
0
                        field.kind = ObjectField::FIELD_ID;
1013
0
                        field.id = alloc.makeIdentifier(lit->value);
1014
0
                        field.fodder1 = lit->openFodder;
1015
0
                        field.expr1 = nullptr;
1016
0
                    }
1017
0
                }
1018
0
            }
1019
0
        }
1020
0
        FmtPass::visit(expr);
1021
0
    }
1022
};
1023
1024
/// Add newlines inside complex structures (arrays, objects etc.).
1025
///
1026
/// The main principle is that a structure can either be:
1027
/// * expanded and contain newlines in all the designated places
1028
/// * unexpanded and contain newlines in none of the designated places
1029
///
1030
/// It only looks shallowly at the AST nodes, so there may be some newlines deeper that
1031
/// don't affect expanding. For example:
1032
/// [{
1033
///     'a': 'b',
1034
///     'c': 'd',
1035
/// }]
1036
/// The outer array can stay unexpanded, because there are no newlines between
1037
/// the square brackets and the braces.
1038
class FixNewlines : public FmtPass {
1039
    using FmtPass::visit;
1040
1041
    bool shouldExpand(const Array *array)
1042
0
    {
1043
0
        for (const auto &elem : array->elements) {
1044
0
            if (countNewlines(open_fodder(elem.expr)) > 0) {
1045
0
                return true;
1046
0
            }
1047
0
        }
1048
0
        if (countNewlines(array->closeFodder) > 0) {
1049
0
            return true;
1050
0
        }
1051
0
        return false;
1052
0
    }
1053
1054
    void expand(Array *array)
1055
0
    {
1056
0
        for (auto &elem : array->elements) {
1057
0
            ensureCleanNewline(open_fodder(elem.expr));
1058
0
        }
1059
0
        ensureCleanNewline(array->closeFodder);
1060
0
    }
1061
1062
    Fodder &objectFieldOpenFodder(ObjectField &field)
1063
0
    {
1064
0
        if (field.kind == ObjectField::Kind::FIELD_STR) {
1065
0
            return field.expr1->openFodder;
1066
0
        }
1067
0
        return field.fodder1;
1068
0
    }
1069
1070
    bool shouldExpand(Object *object)
1071
0
    {
1072
0
        for (auto &field : object->fields) {
1073
0
            if (countNewlines(objectFieldOpenFodder(field)) > 0) {
1074
0
                return true;
1075
0
            }
1076
0
        }
1077
0
        if (countNewlines(object->closeFodder) > 0) {
1078
0
            return true;
1079
0
        }
1080
0
        return false;
1081
0
    }
1082
1083
    void expand(Object *object)
1084
0
    {
1085
0
        for (auto &field : object->fields) {
1086
0
            ensureCleanNewline(objectFieldOpenFodder(field));
1087
0
        }
1088
0
        ensureCleanNewline(object->closeFodder);
1089
0
    }
1090
1091
    bool shouldExpand(Local *local)
1092
0
    {
1093
0
        for (auto &bind : local->binds) {
1094
0
            if (countNewlines(bind.varFodder) > 0) {
1095
0
                return true;
1096
0
            }
1097
0
        }
1098
0
        return false;
1099
0
    }
1100
1101
    void expand(Local *local)
1102
0
    {
1103
0
        bool first = true;
1104
0
        for (auto &bind : local->binds) {
1105
0
            if (!first) {
1106
0
                ensureCleanNewline(bind.varFodder);
1107
0
            }
1108
0
            first = false;
1109
0
        }
1110
0
    }
1111
1112
    bool shouldExpand(ArrayComprehension *comp)
1113
0
    {
1114
0
        if (countNewlines(open_fodder(comp->body)) > 0) {
1115
0
            return true;
1116
0
        }
1117
0
        for (auto &spec : comp->specs) {
1118
0
            if (countNewlines(spec.openFodder) > 0) {
1119
0
                return true;
1120
0
            }
1121
0
        }
1122
0
        if (countNewlines(comp->closeFodder) > 0) {
1123
0
            return true;
1124
0
        }
1125
0
        return false;
1126
0
    }
1127
1128
    void expand(ArrayComprehension *comp)
1129
0
    {
1130
0
        ensureCleanNewline(open_fodder(comp->body));
1131
0
        for (auto &spec : comp->specs) {
1132
0
            ensureCleanNewline(spec.openFodder);
1133
0
        }
1134
0
        ensureCleanNewline(comp->closeFodder);
1135
0
    }
1136
1137
    bool shouldExpand(ObjectComprehension *comp)
1138
0
    {
1139
0
        for (auto &field : comp->fields) {
1140
0
            if (countNewlines(objectFieldOpenFodder(field)) > 0) {
1141
0
                return true;
1142
0
            }
1143
0
        }
1144
0
        for (auto &spec : comp->specs) {
1145
0
            if (countNewlines(spec.openFodder) > 0) {
1146
0
                return true;
1147
0
            }
1148
0
        }
1149
0
        if (countNewlines(comp->closeFodder) > 0) {
1150
0
            return true;
1151
0
        }
1152
0
        return false;
1153
0
    }
1154
1155
    void expand(ObjectComprehension *comp)
1156
0
    {
1157
0
        for (auto &field : comp->fields) {
1158
0
            ensureCleanNewline(objectFieldOpenFodder(field));
1159
0
        }
1160
0
        for (auto &spec : comp->specs) {
1161
0
            ensureCleanNewline(spec.openFodder);
1162
0
        }
1163
0
        ensureCleanNewline(comp->closeFodder);
1164
0
    }
1165
1166
    bool shouldExpand(Parens *parens)
1167
0
    {
1168
0
        return countNewlines(open_fodder(parens->expr)) > 0 ||
1169
0
               countNewlines(parens->closeFodder) > 0;
1170
0
    }
1171
1172
    void expand(Parens *parens)
1173
0
    {
1174
0
        ensureCleanNewline(open_fodder(parens->expr));
1175
0
        ensureCleanNewline(parens->closeFodder);
1176
0
    }
1177
1178
    Fodder &argParamOpenFodder(ArgParam &param)
1179
0
    {
1180
0
        if (param.id != nullptr) {
1181
0
            return param.idFodder;
1182
0
        } else if (param.expr != nullptr) {
1183
0
            return open_fodder(param.expr);
1184
0
        } else {
1185
0
            std::cerr << "Invalid ArgParam" << std::endl;
1186
0
            abort();
1187
0
        }
1188
0
    }
1189
1190
    // Example:
1191
    // f(1, 2,
1192
    //   3)
1193
    // Should be expanded to:
1194
    // f(1,
1195
    //   2,
1196
    //   3)
1197
    bool shouldExpandBetween(ArgParams &params)
1198
0
    {
1199
0
        bool first = true;
1200
0
        for (auto &param : params) {
1201
0
            if (!first && countNewlines(argParamOpenFodder(param)) > 0) {
1202
0
                return true;
1203
0
            }
1204
0
            first = false;
1205
0
        }
1206
0
        return false;
1207
0
    }
1208
1209
    void expandBetween(ArgParams &params)
1210
0
    {
1211
0
        bool first = true;
1212
0
        for (auto &param : params) {
1213
0
            if (!first) {
1214
0
                ensureCleanNewline(argParamOpenFodder(param));
1215
0
            }
1216
0
            first = false;
1217
0
        }
1218
0
    }
1219
1220
    // Example:
1221
    // foo(
1222
    //     1, 2, 3)
1223
    // Should be expanded to:
1224
    // foo(
1225
    //     1, 2, 3
1226
    // )
1227
    bool shouldExpandNearParens(ArgParams &params, Fodder &fodder_r)
1228
0
    {
1229
0
        if (params.empty()) {
1230
0
            return false;
1231
0
        }
1232
0
        auto &argFodder = argParamOpenFodder(params.front());
1233
0
        return countNewlines(fodder_r) > 0 || countNewlines(argFodder) > 0;
1234
0
    }
1235
1236
    void expandNearParens(ArgParams &params, Fodder &fodder_r)
1237
0
    {
1238
0
        if (!params.empty()) {
1239
0
            ensureCleanNewline(argParamOpenFodder(params.front()));
1240
0
        }
1241
0
        ensureCleanNewline(fodder_r);
1242
0
    }
1243
1244
   public:
1245
0
    FixNewlines(Allocator &alloc, const FmtOpts &opts) : FmtPass(alloc, opts) {}
1246
1247
    template <class T>
1248
    void simpleExpandingVisit(T *expr)
1249
0
    {
1250
0
        if (shouldExpand(expr)) {
1251
0
            expand(expr);
1252
0
        }
1253
0
        FmtPass::visit(expr);
1254
0
    }
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::Array>(jsonnet::internal::Array*)
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::ArrayComprehension>(jsonnet::internal::ArrayComprehension*)
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::Local>(jsonnet::internal::Local*)
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::Object>(jsonnet::internal::Object*)
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::ObjectComprehension>(jsonnet::internal::ObjectComprehension*)
Unexecuted instantiation: void jsonnet::internal::FixNewlines::simpleExpandingVisit<jsonnet::internal::Parens>(jsonnet::internal::Parens*)
1255
1256
    void visit(Array *array)
1257
0
    {
1258
0
        simpleExpandingVisit(array);
1259
0
    }
1260
1261
    void visit(Object *object)
1262
0
    {
1263
0
        simpleExpandingVisit(object);
1264
0
    }
1265
1266
    void visit(Local *local)
1267
0
    {
1268
0
        simpleExpandingVisit(local);
1269
0
    }
1270
1271
    void visit(ArrayComprehension *comp)
1272
0
    {
1273
0
        simpleExpandingVisit(comp);
1274
0
    }
1275
1276
    void visit(ObjectComprehension *comp)
1277
0
    {
1278
0
        simpleExpandingVisit(comp);
1279
0
    }
1280
1281
    void visit(Parens *parens)
1282
0
    {
1283
0
        simpleExpandingVisit(parens);
1284
0
    }
1285
1286
    void params(Fodder &fodder_l, ArgParams &params, Fodder &fodder_r)
1287
0
    {
1288
0
        if (shouldExpandBetween(params)) {
1289
0
            expandBetween(params);
1290
0
        }
1291
1292
0
        if (shouldExpandNearParens(params, fodder_r)) {
1293
0
            expandNearParens(params, fodder_r);
1294
0
        }
1295
1296
0
        FmtPass::params(fodder_l, params, fodder_r);
1297
0
    }
1298
};
1299
1300
class FixIndentation {
1301
    FmtOpts opts;
1302
    unsigned column;
1303
1304
   public:
1305
0
    FixIndentation(const FmtOpts &opts) : opts(opts), column(0) {}
1306
1307
    /* Set the indentation on the fodder elements, adjust column counter as if it was printed.
1308
     * \param fodder The fodder to pretend to print.
1309
     * \param space_before Whether a space should be printed before any other output.
1310
     * \param separate_token If the last fodder was an interstitial, whether a space should follow
1311
     * it.
1312
     * \param all_but_last_indent New indentation value for all but final fodder element.
1313
     * \param last_indent New indentation value for the final fodder element.
1314
     */
1315
    void fill(Fodder &fodder, bool space_before, bool separate_token, unsigned all_but_last_indent,
1316
              unsigned last_indent)
1317
0
    {
1318
0
        setIndents(fodder, all_but_last_indent, last_indent);
1319
0
        fodder_count(column, fodder, space_before, separate_token);
1320
0
    }
1321
1322
    void fill(Fodder &fodder, bool space_before, bool separate_token, unsigned indent)
1323
0
    {
1324
0
        fill(fodder, space_before, separate_token, indent, indent);
1325
0
    }
1326
1327
    /* This struct is the representation of the indentation level.  The field lineUp is what is
1328
     * generally used to indent after a new line.  The field base is used to help derive a new
1329
     * Indent struct when the indentation level increases.  lineUp is generally > base.
1330
     *
1331
     * In the following case (where spaces are replaced with underscores):
1332
     * ____foobar(1,
1333
     * ___________2)
1334
     *
1335
     * At the AST representing the 2, the indent has base == 4 and lineUp == 11.
1336
     */
1337
    struct Indent {
1338
        unsigned base;
1339
        unsigned lineUp;
1340
0
        Indent(unsigned base, unsigned line_up) : base(base), lineUp(line_up) {}
1341
    };
1342
1343
    /** Calculate the indentation of sub-expressions.
1344
     *
1345
     * If the first sub-expression is on the same line as the current node, then subsequent
1346
     * ones will be lined up, otherwise subsequent ones will be on the next line indented
1347
     * by 'indent'.
1348
     */
1349
    Indent newIndent(const Fodder &first_fodder, const Indent &old, unsigned line_up)
1350
0
    {
1351
0
        if (first_fodder.size() == 0 || first_fodder[0].kind == FodderElement::INTERSTITIAL) {
1352
0
            return Indent(old.base, line_up);
1353
0
        } else {
1354
            // Reset
1355
0
            return Indent(old.base + opts.indent, old.base + opts.indent);
1356
0
        }
1357
0
    }
1358
1359
    /** Calculate the indentation of sub-expressions.
1360
     *
1361
     * If the first sub-expression is on the same line as the current node, then subsequent
1362
     * ones will be lined up and further indentations in their subexpressions will be based from
1363
     * this column.
1364
     */
1365
    Indent newIndentStrong(const Fodder &first_fodder, const Indent &old, unsigned line_up)
1366
0
    {
1367
0
        if (first_fodder.size() == 0 || first_fodder[0].kind == FodderElement::INTERSTITIAL) {
1368
0
            return Indent(line_up, line_up);
1369
0
        } else {
1370
            // Reset
1371
0
            return Indent(old.base + opts.indent, old.base + opts.indent);
1372
0
        }
1373
0
    }
1374
1375
    /** Calculate the indentation of sub-expressions.
1376
     *
1377
     * If the first sub-expression is on the same line as the current node, then subsequent
1378
     * ones will be lined up, otherwise subseqeuent ones will be on the next line with no
1379
     * additional indent.
1380
     */
1381
    Indent align(const Fodder &first_fodder, const Indent &old, unsigned line_up)
1382
0
    {
1383
0
        if (first_fodder.size() == 0 || first_fodder[0].kind == FodderElement::INTERSTITIAL) {
1384
0
            return Indent(old.base, line_up);
1385
0
        } else {
1386
            // Reset
1387
0
            return old;
1388
0
        }
1389
0
    }
1390
1391
    /** Calculate the indentation of sub-expressions.
1392
     *
1393
     * If the first sub-expression is on the same line as the current node, then subsequent
1394
     * ones will be lined up and further indentations in their subexpressions will be based from
1395
     * this column.  Otherwise, subseqeuent ones will be on the next line with no
1396
     * additional indent.
1397
     */
1398
    Indent alignStrong(const Fodder &first_fodder, const Indent &old, unsigned line_up)
1399
0
    {
1400
0
        if (first_fodder.size() == 0 || first_fodder[0].kind == FodderElement::INTERSTITIAL) {
1401
0
            return Indent(line_up, line_up);
1402
0
        } else {
1403
            // Reset
1404
0
            return old;
1405
0
        }
1406
0
    }
1407
1408
    /* Set indentation values within the fodder elements.
1409
     *
1410
     * The last one gets a special indentation value, all the others are set to the same thing.
1411
     */
1412
    void setIndents(Fodder &fodder, unsigned all_but_last_indent, unsigned last_indent)
1413
0
    {
1414
        // First count how many there are.
1415
0
        unsigned count = 0;
1416
0
        for (const auto &f : fodder) {
1417
0
            if (f.kind != FodderElement::INTERSTITIAL)
1418
0
                count++;
1419
0
        }
1420
        // Now set the indents.
1421
0
        unsigned i = 0;
1422
0
        for (auto &f : fodder) {
1423
0
            if (f.kind != FodderElement::INTERSTITIAL) {
1424
0
                if (i + 1 < count) {
1425
0
                    f.indent = all_but_last_indent;
1426
0
                } else {
1427
0
                    assert(i == count - 1);
1428
0
                    f.indent = last_indent;
1429
0
                }
1430
0
                i++;
1431
0
            }
1432
0
        }
1433
0
    }
1434
1435
    /** Indent comprehension specs.
1436
     * \param indent The indentation level.
1437
     */
1438
    void specs(std::vector<ComprehensionSpec> &specs, const Indent &indent)
1439
0
    {
1440
0
        for (auto &spec : specs) {
1441
0
            fill(spec.openFodder, true, true, indent.lineUp);
1442
0
            switch (spec.kind) {
1443
0
                case ComprehensionSpec::FOR: {
1444
0
                    column += 3;  // for
1445
0
                    fill(spec.varFodder, true, true, indent.lineUp);
1446
0
                    column += spec.var->name.length();
1447
0
                    fill(spec.inFodder, true, true, indent.lineUp);
1448
0
                    column += 2;  // in
1449
0
                    Indent new_indent = newIndent(open_fodder(spec.expr), indent, column);
1450
0
                    expr(spec.expr, new_indent, true);
1451
0
                } break;
1452
1453
0
                case ComprehensionSpec::IF: {
1454
0
                    column += 2;  // if
1455
0
                    Indent new_indent = newIndent(open_fodder(spec.expr), indent, column);
1456
0
                    expr(spec.expr, new_indent, true);
1457
0
                } break;
1458
0
            }
1459
0
        }
1460
0
    }
1461
1462
    void params(Fodder &fodder_l, ArgParams &params, bool trailing_comma, Fodder &fodder_r,
1463
                const Indent &indent)
1464
0
    {
1465
0
        fill(fodder_l, false, false, indent.lineUp, indent.lineUp);
1466
0
        column++;  // (
1467
0
        const Fodder &first_inside = params.size() == 0 ? fodder_r : params[0].idFodder;
1468
1469
0
        Indent new_indent = newIndent(first_inside, indent, column);
1470
0
        bool first = true;
1471
0
        for (auto &param : params) {
1472
0
            if (!first)
1473
0
                column++;  // ','
1474
0
            fill(param.idFodder, !first, true, new_indent.lineUp);
1475
0
            column += param.id->name.length();
1476
0
            if (param.expr != nullptr) {
1477
                // default arg, no spacing: x=e
1478
0
                fill(param.eqFodder, false, false, new_indent.lineUp);
1479
0
                column++;
1480
0
                expr(param.expr, new_indent, false);
1481
0
            }
1482
0
            fill(param.commaFodder, false, false, new_indent.lineUp);
1483
0
            first = false;
1484
0
        }
1485
0
        if (trailing_comma)
1486
0
            column++;
1487
0
        fill(fodder_r, false, false, new_indent.lineUp, indent.lineUp);
1488
0
        column++;  // )
1489
0
    }
1490
1491
    void fieldParams(ObjectField &field, const Indent &indent)
1492
0
    {
1493
0
        if (field.methodSugar) {
1494
0
            params(field.fodderL, field.params, field.trailingComma, field.fodderR, indent);
1495
0
        }
1496
0
    }
1497
1498
    /** Indent fields within an object.
1499
     *
1500
     * \params fields
1501
     * \param indent Indent of the first field.
1502
     * \param space_before
1503
     */
1504
    void fields(ObjectFields &fields, const Indent &indent, bool space_before)
1505
0
    {
1506
0
        unsigned new_indent = indent.lineUp;
1507
0
        bool first = true;
1508
0
        for (auto &field : fields) {
1509
0
            if (!first)
1510
0
                column++;  // ','
1511
1512
0
            switch (field.kind) {
1513
0
                case ObjectField::LOCAL: {
1514
0
                    fill(field.fodder1, !first || space_before, true, indent.lineUp);
1515
0
                    column += 5;  // local
1516
0
                    fill(field.fodder2, true, true, indent.lineUp);
1517
0
                    column += field.id->name.length();
1518
0
                    fieldParams(field, indent);
1519
0
                    fill(field.opFodder, true, true, indent.lineUp);
1520
0
                    column++;  // =
1521
0
                    Indent new_indent2 = newIndent(open_fodder(field.expr2), indent, column);
1522
0
                    expr(field.expr2, new_indent2, true);
1523
0
                } break;
1524
1525
0
                case ObjectField::FIELD_ID:
1526
0
                case ObjectField::FIELD_STR:
1527
0
                case ObjectField::FIELD_EXPR: {
1528
0
                    if (field.kind == ObjectField::FIELD_ID) {
1529
0
                        fill(field.fodder1, !first || space_before, true, new_indent);
1530
0
                        column += field.id->name.length();
1531
1532
0
                    } else if (field.kind == ObjectField::FIELD_STR) {
1533
0
                        expr(field.expr1, indent, !first || space_before);
1534
1535
0
                    } else if (field.kind == ObjectField::FIELD_EXPR) {
1536
0
                        fill(field.fodder1, !first || space_before, true, new_indent);
1537
0
                        column++;  // [
1538
0
                        expr(field.expr1, indent, false);
1539
0
                        fill(field.fodder2, false, false, new_indent);
1540
0
                        column++;  // ]
1541
0
                    }
1542
1543
0
                    fieldParams(field, indent);
1544
1545
0
                    fill(field.opFodder, false, false, new_indent);
1546
1547
0
                    if (field.superSugar)
1548
0
                        column++;
1549
0
                    switch (field.hide) {
1550
0
                        case ObjectField::INHERIT: column += 1; break;
1551
0
                        case ObjectField::HIDDEN: column += 2; break;
1552
0
                        case ObjectField::VISIBLE: column += 3; break;
1553
0
                    }
1554
0
                    Indent new_indent2 = newIndent(open_fodder(field.expr2), indent, column);
1555
0
                    expr(field.expr2, new_indent2, true);
1556
1557
0
                } break;
1558
1559
0
                case ObjectField::ASSERT: {
1560
0
                    fill(field.fodder1, !first || space_before, true, new_indent);
1561
0
                    column += 6;  // assert
1562
                    // + 1 for the space after the assert
1563
0
                    Indent new_indent2 = newIndent(open_fodder(field.expr2), indent, column + 1);
1564
0
                    expr(field.expr2, indent, true);
1565
0
                    if (field.expr3 != nullptr) {
1566
0
                        fill(field.opFodder, true, true, new_indent2.lineUp);
1567
0
                        column++;  // ":"
1568
0
                        expr(field.expr3, new_indent2, true);
1569
0
                    }
1570
0
                } break;
1571
0
            }
1572
1573
0
            first = false;
1574
0
            fill(field.commaFodder, false, false, new_indent);
1575
0
        }
1576
0
    }
1577
1578
    /** Does the given fodder contain at least one new line? */
1579
    bool hasNewLines(const Fodder &fodder)
1580
0
    {
1581
0
        for (const auto &f : fodder) {
1582
0
            if (f.kind != FodderElement::INTERSTITIAL)
1583
0
                return true;
1584
0
        }
1585
0
        return false;
1586
0
    }
1587
1588
    /** Get the first fodder from an ArgParam. */
1589
    const Fodder &argParamFirstFodder(const ArgParam &ap)
1590
0
    {
1591
0
        if (ap.id != nullptr)
1592
0
            return ap.idFodder;
1593
0
        return open_fodder(ap.expr);
1594
0
    }
1595
1596
    /** Reindent an expression.
1597
     *
1598
     * \param ast_ The ast to reindent.
1599
     * \param indent Beginning of the line.
1600
     * \param space_before As defined in the pretty-printer.
1601
     */
1602
    void expr(AST *ast_, const Indent &indent, bool space_before)
1603
0
    {
1604
0
        bool separate_token = !left_recursive(ast_);
1605
1606
0
        fill(ast_->openFodder, space_before, separate_token, indent.lineUp);
1607
1608
0
        if (auto *ast = dynamic_cast<Apply *>(ast_)) {
1609
0
            const Fodder &init_fodder = open_fodder(ast->target);
1610
0
            Indent new_indent = align(init_fodder, indent, column + (space_before ? 1 : 0));
1611
0
            expr(ast->target, new_indent, space_before);
1612
0
            fill(ast->fodderL, false, false, new_indent.lineUp);
1613
0
            column++;  // (
1614
0
            const Fodder &first_fodder =
1615
0
                ast->args.size() == 0 ? ast->fodderR : argParamFirstFodder(ast->args[0]);
1616
0
            bool strong_indent = false;
1617
            // Need to use strong indent if any of the
1618
            // arguments (except the first) are preceded by newlines.
1619
0
            bool first = true;
1620
0
            for (auto &arg : ast->args) {
1621
0
                if (first) {
1622
                    // Skip first element.
1623
0
                    first = false;
1624
0
                    continue;
1625
0
                }
1626
0
                if (hasNewLines(argParamFirstFodder(arg)))
1627
0
                    strong_indent = true;
1628
0
            }
1629
1630
0
            Indent arg_indent = strong_indent ? newIndentStrong(first_fodder, indent, column)
1631
0
                                              : newIndent(first_fodder, indent, column);
1632
0
            first = true;
1633
0
            for (auto &arg : ast->args) {
1634
0
                if (!first)
1635
0
                    column++;  // ","
1636
1637
0
                bool space = !first;
1638
0
                if (arg.id != nullptr) {
1639
0
                    fill(arg.idFodder, space, false, arg_indent.lineUp);
1640
0
                    column += arg.id->name.length();
1641
0
                    space = false;
1642
0
                    column++;  // "="
1643
0
                }
1644
0
                expr(arg.expr, arg_indent, space);
1645
0
                fill(arg.commaFodder, false, false, arg_indent.lineUp);
1646
0
                first = false;
1647
0
            }
1648
0
            if (ast->trailingComma)
1649
0
                column++;  // ","
1650
0
            fill(ast->fodderR, false, false, arg_indent.lineUp, indent.base);
1651
0
            column++;  // )
1652
0
            if (ast->tailstrict) {
1653
0
                fill(ast->tailstrictFodder, true, true, indent.base);
1654
0
                column += 10;  // tailstrict
1655
0
            }
1656
1657
0
        } else if (auto *ast = dynamic_cast<ApplyBrace *>(ast_)) {
1658
0
            const Fodder &init_fodder = open_fodder(ast->left);
1659
0
            Indent new_indent = align(init_fodder, indent, column + (space_before ? 1 : 0));
1660
0
            expr(ast->left, new_indent, space_before);
1661
0
            expr(ast->right, new_indent, true);
1662
1663
0
        } else if (auto *ast = dynamic_cast<Array *>(ast_)) {
1664
0
            column++;  // '['
1665
            // First fodder element exists and is a newline
1666
0
            const Fodder &first_fodder =
1667
0
                ast->elements.size() > 0 ? open_fodder(ast->elements[0].expr) : ast->closeFodder;
1668
0
            unsigned new_column = column + (opts.padArrays ? 1 : 0);
1669
0
            bool strong_indent = false;
1670
            // Need to use strong indent if there are not newlines before any of the sub-expressions
1671
0
            bool first = true;
1672
0
            for (auto &el : ast->elements) {
1673
0
                if (first) {
1674
0
                    first = false;
1675
0
                    continue;
1676
0
                }
1677
0
                if (hasNewLines(open_fodder(el.expr)))
1678
0
                    strong_indent = true;
1679
0
            }
1680
1681
0
            Indent new_indent = strong_indent ? newIndentStrong(first_fodder, indent, new_column)
1682
0
                                              : newIndent(first_fodder, indent, new_column);
1683
1684
0
            first = true;
1685
0
            for (auto &element : ast->elements) {
1686
0
                if (!first)
1687
0
                    column++;
1688
0
                expr(element.expr, new_indent, !first || opts.padArrays);
1689
0
                fill(element.commaFodder, false, false, new_indent.lineUp, new_indent.lineUp);
1690
0
                first = false;
1691
0
            }
1692
0
            if (ast->trailingComma)
1693
0
                column++;
1694
1695
            // Handle penultimate newlines from expr.close_fodder if there are any.
1696
0
            fill(ast->closeFodder,
1697
0
                 ast->elements.size() > 0,
1698
0
                 opts.padArrays,
1699
0
                 new_indent.lineUp,
1700
0
                 indent.base);
1701
0
            column++;  // ']'
1702
1703
0
        } else if (auto *ast = dynamic_cast<ArrayComprehension *>(ast_)) {
1704
0
            column++;  // [
1705
0
            Indent new_indent =
1706
0
                newIndent(open_fodder(ast->body), indent, column + (opts.padArrays ? 1 : 0));
1707
0
            expr(ast->body, new_indent, opts.padArrays);
1708
0
            fill(ast->commaFodder, false, false, new_indent.lineUp);
1709
0
            if (ast->trailingComma)
1710
0
                column++;  // ','
1711
0
            specs(ast->specs, new_indent);
1712
0
            fill(ast->closeFodder, true, opts.padArrays, new_indent.lineUp, indent.base);
1713
0
            column++;  // ]
1714
1715
0
        } else if (auto *ast = dynamic_cast<Assert *>(ast_)) {
1716
0
            column += 6;  // assert
1717
            // + 1 for the space after the assert
1718
0
            Indent new_indent = newIndent(open_fodder(ast->cond), indent, column + 1);
1719
0
            expr(ast->cond, new_indent, true);
1720
0
            if (ast->message != nullptr) {
1721
0
                fill(ast->colonFodder, true, true, new_indent.lineUp);
1722
0
                column++;  // ":"
1723
0
                expr(ast->message, new_indent, true);
1724
0
            }
1725
0
            fill(ast->semicolonFodder, false, false, new_indent.lineUp);
1726
0
            column++;  // ";"
1727
0
            expr(ast->rest, indent, true);
1728
1729
0
        } else if (auto *ast = dynamic_cast<Binary *>(ast_)) {
1730
0
            const Fodder &first_fodder = open_fodder(ast->left);
1731
1732
            // Need to use strong indent in the case of
1733
            /*
1734
            A
1735
            + B
1736
            or
1737
            A +
1738
            B
1739
            */
1740
0
            bool strong_indent = hasNewLines(ast->opFodder) || hasNewLines(open_fodder(ast->right));
1741
1742
0
            unsigned inner_column = column + (space_before ? 1 : 0);
1743
0
            Indent new_indent = strong_indent ? alignStrong(first_fodder, indent, inner_column)
1744
0
                                              : align(first_fodder, indent, inner_column);
1745
0
            expr(ast->left, new_indent, space_before);
1746
0
            fill(ast->opFodder, true, true, new_indent.lineUp);
1747
0
            column += bop_string(ast->op).length();
1748
            // Don't calculate a new indent for here, because we like being able to do:
1749
            // true &&
1750
            // true &&
1751
            // true
1752
0
            expr(ast->right, new_indent, true);
1753
1754
0
        } else if (auto *ast = dynamic_cast<BuiltinFunction *>(ast_)) {
1755
0
            column += 11;  // "/* builtin "
1756
0
            column += ast->name.length();
1757
0
            column += 8;  // " */ null"
1758
1759
0
        } else if (auto *ast = dynamic_cast<Conditional *>(ast_)) {
1760
0
            column += 2;  // if
1761
0
            Indent cond_indent = newIndent(open_fodder(ast->cond), indent, column + 1);
1762
0
            expr(ast->cond, cond_indent, true);
1763
0
            fill(ast->thenFodder, true, true, indent.base);
1764
0
            column += 4;  // then
1765
0
            Indent true_indent = newIndent(open_fodder(ast->branchTrue), indent, column + 1);
1766
0
            expr(ast->branchTrue, true_indent, true);
1767
0
            if (ast->branchFalse != nullptr) {
1768
0
                fill(ast->elseFodder, true, true, indent.base);
1769
0
                column += 4;  // else
1770
0
                Indent false_indent = newIndent(open_fodder(ast->branchFalse), indent, column + 1);
1771
0
                expr(ast->branchFalse, false_indent, true);
1772
0
            }
1773
1774
0
        } else if (dynamic_cast<Dollar *>(ast_)) {
1775
0
            column++;  // $
1776
1777
0
        } else if (auto *ast = dynamic_cast<Error *>(ast_)) {
1778
0
            column += 5;  // error
1779
0
            Indent new_indent = newIndent(open_fodder(ast->expr), indent, column + 1);
1780
0
            expr(ast->expr, new_indent, true);
1781
1782
0
        } else if (auto *ast = dynamic_cast<Function *>(ast_)) {
1783
0
            column += 8;  // function
1784
0
            params(ast->parenLeftFodder,
1785
0
                   ast->params,
1786
0
                   ast->trailingComma,
1787
0
                   ast->parenRightFodder,
1788
0
                   indent);
1789
0
            Indent new_indent = newIndent(open_fodder(ast->body), indent, column + 1);
1790
0
            expr(ast->body, new_indent, true);
1791
1792
0
        } else if (auto *ast = dynamic_cast<Import *>(ast_)) {
1793
0
            column += 6;  // import
1794
0
            Indent new_indent = newIndent(open_fodder(ast->file), indent, column + 1);
1795
0
            expr(ast->file, new_indent, true);
1796
1797
0
        } else if (auto *ast = dynamic_cast<Importstr *>(ast_)) {
1798
0
            column += 9;  // importstr
1799
0
            Indent new_indent = newIndent(open_fodder(ast->file), indent, column + 1);
1800
0
            expr(ast->file, new_indent, true);
1801
1802
0
        } else if (auto *ast = dynamic_cast<Importbin *>(ast_)) {
1803
0
            column += 9;  // importbin
1804
0
            Indent new_indent = newIndent(open_fodder(ast->file), indent, column + 1);
1805
0
            expr(ast->file, new_indent, true);
1806
1807
0
        } else if (auto *ast = dynamic_cast<InSuper *>(ast_)) {
1808
0
            expr(ast->element, indent, space_before);
1809
0
            fill(ast->inFodder, true, true, indent.lineUp);
1810
0
            column += 2;  // in
1811
0
            fill(ast->superFodder, true, true, indent.lineUp);
1812
0
            column += 5;  // super
1813
1814
0
        } else if (auto *ast = dynamic_cast<Index *>(ast_)) {
1815
0
            expr(ast->target, indent, space_before);
1816
0
            fill(ast->dotFodder, false, false, indent.lineUp);
1817
0
            if (ast->id != nullptr) {
1818
0
                Indent new_indent = newIndent(ast->idFodder, indent, column);
1819
0
                column++;  // "."
1820
0
                fill(ast->idFodder, false, false, new_indent.lineUp);
1821
0
                column += ast->id->name.length();
1822
0
            } else {
1823
0
                column++;  // "["
1824
0
                if (ast->isSlice) {
1825
0
                    Indent new_indent(0, 0);
1826
0
                    if (ast->index != nullptr) {
1827
0
                        new_indent = newIndent(open_fodder(ast->index), indent, column);
1828
0
                        expr(ast->index, new_indent, false);
1829
0
                    }
1830
0
                    if (ast->end != nullptr) {
1831
0
                        new_indent = newIndent(ast->endColonFodder, indent, column);
1832
0
                        fill(ast->endColonFodder, false, false, new_indent.lineUp);
1833
0
                        column++;  // ":"
1834
0
                        expr(ast->end, new_indent, false);
1835
0
                    }
1836
0
                    if (ast->step != nullptr) {
1837
0
                        if (ast->end == nullptr) {
1838
0
                            new_indent = newIndent(ast->endColonFodder, indent, column);
1839
0
                            fill(ast->endColonFodder, false, false, new_indent.lineUp);
1840
0
                            column++;  // ":"
1841
0
                        }
1842
0
                        fill(ast->stepColonFodder, false, false, new_indent.lineUp);
1843
0
                        column++;  // ":"
1844
0
                        expr(ast->step, new_indent, false);
1845
0
                    }
1846
0
                    if (ast->index == nullptr && ast->end == nullptr && ast->step == nullptr) {
1847
0
                        new_indent = newIndent(ast->endColonFodder, indent, column);
1848
0
                        fill(ast->endColonFodder, false, false, new_indent.lineUp);
1849
0
                        column++;  // ":"
1850
0
                    }
1851
0
                } else {
1852
0
                    Indent new_indent = newIndent(open_fodder(ast->index), indent, column);
1853
0
                    expr(ast->index, new_indent, false);
1854
0
                    fill(ast->idFodder, false, false, new_indent.lineUp, indent.base);
1855
0
                }
1856
0
                column++;  // "]"
1857
0
            }
1858
1859
0
        } else if (auto *ast = dynamic_cast<Local *>(ast_)) {
1860
0
            column += 5;  // local
1861
0
            assert(ast->binds.size() > 0);
1862
0
            bool first = true;
1863
0
            Indent new_indent = newIndent(ast->binds[0].varFodder, indent, column + 1);
1864
0
            for (auto &bind : ast->binds) {
1865
0
                if (!first)
1866
0
                    column++;  // ','
1867
0
                first = false;
1868
0
                fill(bind.varFodder, true, true, new_indent.lineUp);
1869
0
                column += bind.var->name.length();
1870
0
                if (bind.functionSugar) {
1871
0
                    params(bind.parenLeftFodder,
1872
0
                           bind.params,
1873
0
                           bind.trailingComma,
1874
0
                           bind.parenRightFodder,
1875
0
                           new_indent);
1876
0
                }
1877
0
                fill(bind.opFodder, true, true, new_indent.lineUp);
1878
0
                column++;  // '='
1879
0
                Indent new_indent2 = newIndent(open_fodder(bind.body), new_indent, column + 1);
1880
0
                expr(bind.body, new_indent2, true);
1881
0
                fill(bind.closeFodder, false, false, new_indent2.lineUp, indent.base);
1882
0
            }
1883
0
            column++;  // ';'
1884
0
            expr(ast->body, indent, true);
1885
1886
0
        } else if (auto *ast = dynamic_cast<LiteralBoolean *>(ast_)) {
1887
0
            column += (ast->value ? 4 : 5);
1888
1889
0
        } else if (auto *ast = dynamic_cast<LiteralNumber *>(ast_)) {
1890
0
            column += ast->originalString.length();
1891
1892
0
        } else if (auto *ast = dynamic_cast<LiteralString *>(ast_)) {
1893
0
            assert(ast->tokenKind != LiteralString::RAW_DESUGARED);
1894
0
            if (ast->tokenKind == LiteralString::DOUBLE) {
1895
0
                column += 2 + ast->value.length();  // Include quotes
1896
0
            } else if (ast->tokenKind == LiteralString::SINGLE) {
1897
0
                column += 2 + ast->value.length();  // Include quotes
1898
0
            } else if (ast->tokenKind == LiteralString::BLOCK) {
1899
0
                ast->blockIndent = std::string(indent.base + opts.indent, ' ');
1900
0
                ast->blockTermIndent = std::string(indent.base, ' ');
1901
0
                column = indent.base;  // blockTermIndent
1902
0
                column += 3;           // "|||"
1903
0
            } else if (ast->tokenKind == LiteralString::VERBATIM_SINGLE) {
1904
0
                column += 3;  // Include @, start and end quotes
1905
0
                for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
1906
0
                    if (*cp == U'\'') {
1907
0
                        column += 2;
1908
0
                    } else {
1909
0
                        column += 1;
1910
0
                    }
1911
0
                }
1912
0
            } else if (ast->tokenKind == LiteralString::VERBATIM_DOUBLE) {
1913
0
                column += 3;  // Include @, start and end quotes
1914
0
                for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
1915
0
                    if (*cp == U'"') {
1916
0
                        column += 2;
1917
0
                    } else {
1918
0
                        column += 1;
1919
0
                    }
1920
0
                }
1921
0
            }
1922
1923
0
        } else if (dynamic_cast<LiteralNull *>(ast_)) {
1924
0
            column += 4;  // null
1925
1926
0
        } else if (auto *ast = dynamic_cast<Object *>(ast_)) {
1927
0
            column++;  // '{'
1928
0
            const Fodder &first_fodder = ast->fields.size() == 0
1929
0
                                             ? ast->closeFodder
1930
0
                                             : ast->fields[0].kind == ObjectField::FIELD_STR
1931
0
                                                   ? open_fodder(ast->fields[0].expr1)
1932
0
                                                   : ast->fields[0].fodder1;
1933
0
            Indent new_indent = newIndent(first_fodder, indent, column + (opts.padObjects ? 1 : 0));
1934
1935
0
            fields(ast->fields, new_indent, opts.padObjects);
1936
0
            if (ast->trailingComma)
1937
0
                column++;
1938
0
            fill(ast->closeFodder,
1939
0
                 ast->fields.size() > 0,
1940
0
                 opts.padObjects,
1941
0
                 new_indent.lineUp,
1942
0
                 indent.base);
1943
0
            column++;  // '}'
1944
1945
0
        } else if (auto *ast = dynamic_cast<DesugaredObject *>(ast_)) {
1946
            // No fodder but need to recurse and maintain column counter.
1947
0
            column++;  // '{'
1948
0
            for (AST *assert : ast->asserts) {
1949
0
                column += 6;  // assert
1950
0
                expr(assert, indent, true);
1951
0
                column++;  // ','
1952
0
            }
1953
0
            for (auto &field : ast->fields) {
1954
0
                column++;  // '['
1955
0
                expr(field.name, indent, false);
1956
0
                column++;  // ']'
1957
0
                switch (field.hide) {
1958
0
                    case ObjectField::INHERIT: column += 1; break;
1959
0
                    case ObjectField::HIDDEN: column += 2; break;
1960
0
                    case ObjectField::VISIBLE: column += 3; break;
1961
0
                }
1962
0
                expr(field.body, indent, true);
1963
0
            }
1964
0
            column++;  // '}'
1965
1966
0
        } else if (auto *ast = dynamic_cast<ObjectComprehension *>(ast_)) {
1967
0
            column++;  // '{'
1968
0
            unsigned start_column = column;
1969
0
            const Fodder &first_fodder = ast->fields.size() == 0
1970
0
                                             ? ast->closeFodder
1971
0
                                             : ast->fields[0].kind == ObjectField::FIELD_STR
1972
0
                                                   ? open_fodder(ast->fields[0].expr1)
1973
0
                                                   : ast->fields[0].fodder1;
1974
0
            Indent new_indent =
1975
0
                newIndent(first_fodder, indent, start_column + (opts.padObjects ? 1 : 0));
1976
1977
0
            fields(ast->fields, new_indent, opts.padObjects);
1978
0
            if (ast->trailingComma)
1979
0
                column++;  // ','
1980
0
            specs(ast->specs, new_indent);
1981
0
            fill(ast->closeFodder, true, opts.padObjects, new_indent.lineUp, indent.base);
1982
0
            column++;  // '}'
1983
1984
0
        } else if (auto *ast = dynamic_cast<ObjectComprehensionSimple *>(ast_)) {
1985
0
            column++;  // '{'
1986
0
            column++;  // '['
1987
0
            expr(ast->field, indent, false);
1988
0
            column++;  // ']'
1989
0
            column++;  // ':'
1990
0
            expr(ast->value, indent, true);
1991
0
            column += 5;  // " for "
1992
0
            column += ast->id->name.length();
1993
0
            column += 3;  // " in"
1994
0
            expr(ast->array, indent, true);
1995
0
            column++;  // '}'
1996
1997
0
        } else if (auto *ast = dynamic_cast<Parens *>(ast_)) {
1998
0
            column++;  // (
1999
0
            Indent new_indent = newIndentStrong(open_fodder(ast->expr), indent, column);
2000
0
            expr(ast->expr, new_indent, false);
2001
0
            fill(ast->closeFodder, false, false, new_indent.lineUp, indent.base);
2002
0
            column++;  // )
2003
2004
0
        } else if (dynamic_cast<const Self *>(ast_)) {
2005
0
            column += 4;  // self
2006
2007
0
        } else if (auto *ast = dynamic_cast<SuperIndex *>(ast_)) {
2008
0
            column += 5;  // super
2009
0
            fill(ast->dotFodder, false, false, indent.lineUp);
2010
0
            if (ast->id != nullptr) {
2011
0
                column++;  // ".";
2012
0
                Indent new_indent = newIndent(ast->idFodder, indent, column);
2013
0
                fill(ast->idFodder, false, false, new_indent.lineUp);
2014
0
                column += ast->id->name.length();
2015
0
            } else {
2016
0
                column++;  // "[";
2017
0
                Indent new_indent = newIndent(open_fodder(ast->index), indent, column);
2018
0
                expr(ast->index, new_indent, false);
2019
0
                fill(ast->idFodder, false, false, new_indent.lineUp, indent.base);
2020
0
                column++;  // "]";
2021
0
            }
2022
2023
0
        } else if (auto *ast = dynamic_cast<Unary *>(ast_)) {
2024
0
            column += uop_string(ast->op).length();
2025
0
            Indent new_indent = newIndent(open_fodder(ast->expr), indent, column);
2026
0
            if (dynamic_cast<const Dollar *>(left_recursive_deep(ast->expr))) {
2027
0
                expr(ast->expr, new_indent, true);
2028
0
            } else {
2029
0
                expr(ast->expr, new_indent, false);
2030
0
            }
2031
2032
0
        } else if (auto *ast = dynamic_cast<Var *>(ast_)) {
2033
0
            column += ast->id->name.length();
2034
2035
0
        } else {
2036
0
            std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_ << std::endl;
2037
0
            std::abort();
2038
0
        }
2039
0
    }
2040
    virtual void file(AST *body, Fodder &final_fodder)
2041
0
    {
2042
0
        expr(body, Indent(0, 0), false);
2043
0
        setIndents(final_fodder, 0, 0);
2044
0
    }
2045
};
2046
2047
/** Sort top-level imports.
2048
 *
2049
 * Top-level imports are `local x = import 'xxx.jsonnet` expressions
2050
 * that go before anything else in the file (more precisely all such imports
2051
 * that are either the root of AST or a direct child (body) of a top-level
2052
 * import.
2053
 *
2054
 * Grouping of imports is preserved. Groups of imports are separated by blank
2055
 * lines or lines containing comments.
2056
 */
2057
class SortImports {
2058
    /// Internal representation of an import
2059
    struct ImportElem {
2060
        ImportElem(UString key, Fodder adjacentFodder, Local::Bind bind)
2061
0
            : key(key), adjacentFodder(adjacentFodder), bind(bind)
2062
0
        {
2063
0
        }
2064
2065
        // A key by which the imports should be sorted.
2066
        // It's a file path that is imported, represented as UTF-32 codepoints without case folding.
2067
        // In particular "Z" < "a", because 'Z' == 90 and 'a' == 97.
2068
        UString key;
2069
2070
        // Comments adjacent to the import that go after it and that should stay attached
2071
        // when imports are reordered.
2072
        Fodder adjacentFodder;
2073
2074
        // The bind that contains the import
2075
        // Satisfies: bind.functionSugar == false && bind.body->type == AST_IMPORT
2076
        Local::Bind bind;
2077
        bool operator<(const ImportElem &elem) const
2078
0
        {
2079
0
            return key < elem.key;
2080
0
        }
2081
    };
2082
2083
    typedef std::vector<ImportElem> ImportElems;
2084
2085
    Allocator &alloc;
2086
2087
   public:
2088
0
    SortImports(Allocator &alloc) : alloc(alloc) {}
2089
2090
    /// Get the value by which the imports should be sorted.
2091
    UString sortingKey(Import *import)
2092
0
    {
2093
0
        return import->file->value;
2094
0
    }
2095
2096
    /// Check if `local` expression is used for importing,
2097
    bool isGoodLocal(Local *local)
2098
0
    {
2099
0
        for (const auto &bind : local->binds) {
2100
0
            if (bind.body->type != AST_IMPORT || bind.functionSugar) {
2101
0
                return false;
2102
0
            }
2103
0
        }
2104
0
        return true;
2105
0
    }
2106
2107
    Local *goodLocalOrNull(AST *expr)
2108
0
    {
2109
0
        if (auto *local = dynamic_cast<Local *>(expr)) {
2110
0
            return isGoodLocal(local) ? local : nullptr;
2111
0
        } else {
2112
0
            return nullptr;
2113
0
        }
2114
0
    }
2115
2116
    /** Split fodder after the first new line / paragraph fodder,
2117
     * leaving blank lines after the newline in the second half.
2118
     *
2119
     * The two returned fodders can be concatenated using concat_fodder to get the original fodder.
2120
     *
2121
     * It's a heuristic that given two consecutive tokens `prev_token`, `next_token`
2122
     * with some fodder between them, decides which part of the fodder logically belongs
2123
     * to `prev_token` and which part belongs to the `next_token`.
2124
     *
2125
     * Example:
2126
     * prev_token // prev_token is awesome!
2127
     *
2128
     * // blah blah
2129
     * next_token
2130
     *
2131
     * In such case "// prev_token is awesome!\n" part of the fodder belongs
2132
     * to the `prev_token` and "\n//blah blah\n" to the `next_token`.
2133
     */
2134
    std::pair<Fodder, Fodder> splitFodder(const Fodder &fodder)
2135
0
    {
2136
0
        Fodder afterPrev, beforeNext;
2137
0
        bool inSecondPart = false;
2138
0
        for (const auto &fodderElem : fodder) {
2139
0
            if (inSecondPart) {
2140
0
                fodder_push_back(beforeNext, fodderElem);
2141
0
            } else {
2142
0
                afterPrev.push_back(fodderElem);
2143
0
            }
2144
0
            if (fodderElem.kind != FodderElement::Kind::INTERSTITIAL && !inSecondPart) {
2145
0
                inSecondPart = true;
2146
0
                if (fodderElem.blanks > 0) {
2147
                    // If there are any blank lines at the end of afterPrev, move them
2148
                    // to beforeNext.
2149
0
                    afterPrev.back().blanks = 0;
2150
0
                    assert(beforeNext.empty());
2151
0
                    beforeNext.emplace_back(FodderElement::Kind::LINE_END,
2152
0
                                            fodderElem.blanks,
2153
0
                                            fodderElem.indent,
2154
0
                                            std::vector<std::string>());
2155
0
                }
2156
0
            }
2157
0
        }
2158
0
        return {afterPrev, beforeNext};
2159
0
    }
2160
2161
    void sortGroup(ImportElems &imports)
2162
0
    {
2163
        // We don't want to change behaviour in such case:
2164
        // local foo = "b.jsonnet";
2165
        // local foo = "a.jsonnet";
2166
        // So we don't change the order when there are shadowed variables.
2167
0
        if (!duplicatedVariables(imports)) {
2168
0
            std::sort(imports.begin(), imports.end());
2169
0
        }
2170
0
    }
2171
2172
    ImportElems extractImportElems(const Local::Binds &binds, Fodder after)
2173
0
    {
2174
0
        ImportElems result;
2175
0
        Fodder before = binds.front().varFodder;
2176
0
        for (int i = 0; i < int(binds.size()); ++i) {
2177
0
            const auto &bind = binds[i];
2178
0
            bool last = i == int(binds.size() - 1);
2179
0
            Fodder adjacent, beforeNext;
2180
0
            if (!last) {
2181
0
                auto &next = binds[i + 1];
2182
0
                std::tie(adjacent, beforeNext) = splitFodder(next.varFodder);
2183
0
            } else {
2184
0
                adjacent = after;
2185
0
            }
2186
0
            ensureCleanNewline(adjacent);
2187
0
            Local::Bind newBind = bind;
2188
0
            newBind.varFodder = before;
2189
0
            Import *import = dynamic_cast<Import *>(bind.body);
2190
0
            assert(import != nullptr);
2191
0
            result.emplace_back(sortingKey(import), adjacent, newBind);
2192
0
            before = beforeNext;
2193
0
        }
2194
0
        return result;
2195
0
    }
2196
2197
    AST *buildGroupAST(ImportElems &imports, AST *body, const Fodder &groupOpenFodder)
2198
0
    {
2199
0
        for (int i = imports.size() - 1; i >= 0; --i) {
2200
0
            auto &import = imports[i];
2201
0
            Fodder fodder;
2202
0
            if (i == 0) {
2203
0
                fodder = groupOpenFodder;
2204
0
            } else {
2205
0
                fodder = imports[i - 1].adjacentFodder;
2206
0
            }
2207
0
            auto *local =
2208
0
                alloc.make<Local>(LocationRange(), fodder, Local::Binds({import.bind}), body);
2209
0
            body = local;
2210
0
        }
2211
2212
0
        return body;
2213
0
    }
2214
2215
    bool duplicatedVariables(const ImportElems &elems)
2216
0
    {
2217
0
        std::set<UString> idents;
2218
0
        for (const auto &elem : elems) {
2219
0
            idents.insert(elem.bind.var->name);
2220
0
        }
2221
0
        return idents.size() < elems.size();
2222
0
    }
2223
2224
    /// Check if the import group ends after this local
2225
    bool groupEndsAfter(Local *local)
2226
0
    {
2227
0
        Local *next = goodLocalOrNull(local->body);
2228
0
        if (!next) {
2229
0
            return true;
2230
0
        }
2231
2232
0
        bool newlineReached = false;
2233
0
        for (const auto &fodderElem : open_fodder(next)) {
2234
0
            if (newlineReached || fodderElem.blanks > 0) {
2235
0
                return true;
2236
0
            }
2237
0
            if (fodderElem.kind != FodderElement::Kind::INTERSTITIAL) {
2238
0
                newlineReached = true;
2239
0
            }
2240
0
        }
2241
0
        return false;
2242
0
    }
2243
2244
    AST *toplevelImport(Local *local, ImportElems &imports, const Fodder &groupOpenFodder)
2245
0
    {
2246
0
        assert(isGoodLocal(local));
2247
2248
0
        Fodder adjacentCommentFodder, beforeNextFodder;
2249
0
        std::tie(adjacentCommentFodder, beforeNextFodder) = splitFodder(open_fodder(local->body));
2250
2251
0
        ensureCleanNewline(adjacentCommentFodder);
2252
2253
0
        ImportElems newImports = extractImportElems(local->binds, adjacentCommentFodder);
2254
0
        imports.insert(imports.end(), newImports.begin(), newImports.end());
2255
2256
0
        if (groupEndsAfter(local)) {
2257
0
            sortGroup(imports);
2258
2259
0
            Fodder afterGroup = imports.back().adjacentFodder;
2260
0
            ensureCleanNewline(beforeNextFodder);
2261
0
            auto nextOpenFodder = concat_fodder(afterGroup, beforeNextFodder);
2262
2263
            // Process the code after the current group:
2264
0
            AST *bodyAfterGroup;
2265
0
            Local *next = goodLocalOrNull(local->body);
2266
0
            if (next) {
2267
                // Another group of imports
2268
0
                ImportElems nextImports;
2269
0
                bodyAfterGroup = toplevelImport(next, nextImports, nextOpenFodder);
2270
0
            } else {
2271
                // Something else
2272
0
                bodyAfterGroup = local->body;
2273
0
                open_fodder(bodyAfterGroup) = nextOpenFodder;
2274
0
            }
2275
2276
0
            return buildGroupAST(imports, bodyAfterGroup, groupOpenFodder);
2277
0
        } else {
2278
0
            assert(beforeNextFodder.empty());
2279
0
            return toplevelImport(dynamic_cast<Local *>(local->body), imports, groupOpenFodder);
2280
0
        }
2281
0
    }
2282
2283
    void file(AST *&body)
2284
0
    {
2285
0
        ImportElems imports;
2286
0
        Local *local = goodLocalOrNull(body);
2287
0
        if (local) {
2288
0
            body = toplevelImport(local, imports, open_fodder(local));
2289
0
        }
2290
0
    }
2291
};
2292
2293
std::string jsonnet_fmt(AST *ast, Fodder &final_fodder, const FmtOpts &opts)
2294
0
{
2295
0
    Allocator alloc;
2296
2297
    // Passes to enforce style on the AST.
2298
0
    if (opts.sortImports)
2299
0
        SortImports(alloc).file(ast);
2300
0
    remove_initial_newlines(ast);
2301
0
    if (opts.maxBlankLines > 0)
2302
0
        EnforceMaximumBlankLines(alloc, opts).file(ast, final_fodder);
2303
0
    FixNewlines(alloc, opts).file(ast, final_fodder);
2304
0
    FixTrailingCommas(alloc, opts).file(ast, final_fodder);
2305
0
    FixParens(alloc, opts).file(ast, final_fodder);
2306
0
    FixPlusObject(alloc, opts).file(ast, final_fodder);
2307
0
    NoRedundantSliceColon(alloc, opts).file(ast, final_fodder);
2308
0
    if (opts.stripComments)
2309
0
        StripComments(alloc, opts).file(ast, final_fodder);
2310
0
    else if (opts.stripAllButComments)
2311
0
        StripAllButComments(alloc, opts).file(ast, final_fodder);
2312
0
    else if (opts.stripEverything)
2313
0
        StripEverything(alloc, opts).file(ast, final_fodder);
2314
0
    if (opts.prettyFieldNames)
2315
0
        PrettyFieldNames(alloc, opts).file(ast, final_fodder);
2316
0
    if (opts.stringStyle != 'l')
2317
0
        EnforceStringStyle(alloc, opts).file(ast, final_fodder);
2318
0
    if (opts.commentStyle != 'l')
2319
0
        EnforceCommentStyle(alloc, opts).file(ast, final_fodder);
2320
0
    if (opts.indent > 0)
2321
0
        FixIndentation(opts).file(ast, final_fodder);
2322
0
    remove_extra_trailing_newlines(final_fodder);
2323
2324
0
    std::stringstream ss;
2325
0
    Unparser unparser(ss, opts);
2326
0
    unparser.unparse(ast, false);
2327
0
    unparser.fill_final(final_fodder, true, false);
2328
0
    if (final_fodder.empty() || final_fodder.back().kind == FodderElement::INTERSTITIAL) {
2329
0
        ss << "\n";
2330
0
    }
2331
0
    return ss.str();
2332
0
}
2333
2334
}  // namespace jsonnet::internal