forked from sorbet/sorbet
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathClassNew.cc
157 lines (125 loc) · 4.74 KB
/
ClassNew.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include "rewriter/ClassNew.h"
#include "ast/Helpers.h"
#include "ast/ast.h"
#include "core/Context.h"
#include "core/Names.h"
#include "core/core.h"
#include "rewriter/rewriter.h"
using namespace std;
namespace sorbet::rewriter {
vector<ast::ExpressionPtr> rewriteAsClassDef(core::MutableContext ctx, ast::Assign *asgn) {
vector<ast::ExpressionPtr> empty;
auto loc = asgn->loc;
auto send = ast::cast_tree<ast::Send>(asgn->rhs);
if (send == nullptr) {
return empty;
}
auto recv = ast::cast_tree<ast::UnresolvedConstantLit>(send->recv);
if (recv == nullptr) {
return empty;
}
if (!ast::isa_tree<ast::EmptyTree>(recv->scope) || recv->cnst != core::Names::Constants::Class() ||
send->fun != core::Names::new_()) {
return empty;
}
auto argc = send->numPosArgs();
if (argc > 1 || send->hasKwArgs()) {
return empty;
}
if (argc == 1 && !ast::isa_tree<ast::UnresolvedConstantLit>(send->getPosArg(0))) {
return empty;
}
ast::ClassDef::RHS_store body;
auto *block = send->block();
if (block != nullptr && block->args.size() == 1) {
auto blockArg = move(block->args[0]);
body.emplace_back(ast::MK::Assign(blockArg.loc(), move(blockArg), asgn->lhs.deepCopy()));
}
if (block != nullptr) {
// Steal the trees, because the run is going to remove the original send node from the tree anyway.
if (auto insSeq = ast::cast_tree<ast::InsSeq>(block->body)) {
for (auto &&stat : insSeq->stats) {
body.emplace_back(move(stat));
}
body.emplace_back(move(insSeq->expr));
} else {
body.emplace_back(move(block->body));
}
}
ast::ClassDef::ANCESTORS_store ancestors;
if (argc == 1) {
ancestors.emplace_back(move(send->getPosArg(0)));
} else {
ancestors.emplace_back(ast::MK::Constant(send->loc, core::Symbols::todo()));
}
vector<ast::ExpressionPtr> stats;
stats.emplace_back(ast::MK::Class(loc, loc, std::move(asgn->lhs), std::move(ancestors), std::move(body)));
return stats;
}
vector<ast::ExpressionPtr> rewriteWithBind(core::MutableContext ctx, ast::Send *send) {
vector<ast::ExpressionPtr> empty;
auto recv = ast::cast_tree<ast::UnresolvedConstantLit>(send->recv);
if (recv == nullptr) {
return empty;
}
if (!ast::isa_tree<ast::EmptyTree>(recv->scope) || recv->cnst != core::Names::Constants::Class() ||
send->fun != core::Names::new_()) {
return empty;
}
auto argc = send->numPosArgs();
if (argc > 1 || send->hasKwArgs()) {
return empty;
}
if (argc == 1 && !ast::isa_tree<ast::UnresolvedConstantLit>(send->getPosArg(0))) {
return empty;
}
auto *block = send->block();
if (block == nullptr) {
return empty;
}
ast::ExpressionPtr type;
if (argc == 0) {
type = ast::MK::Constant(send->loc, core::Symbols::Class());
} else {
auto target = send->getPosArg(0).deepCopy();
type = ast::MK::ClassOf(send->loc, std::move(target));
}
auto bind = ast::MK::Bind(send->loc, ast::MK::Self(send->loc), std::move(type));
ast::InsSeq::STATS_store blockStats;
blockStats.emplace_back(std::move(bind));
if (auto insSeq = ast::cast_tree<ast::InsSeq>(block->body)) {
for (auto &stat : insSeq->stats) {
blockStats.emplace_back(std::move(stat));
}
block->body = ast::MK::InsSeq(block->loc, std::move(blockStats), std::move(insSeq->expr));
} else {
block->body = ast::MK::InsSeq(block->loc, std::move(blockStats), std::move(block->body));
}
return empty;
}
vector<ast::ExpressionPtr> ClassNew::run(core::MutableContext ctx, ast::Assign *asgn) {
vector<ast::ExpressionPtr> empty;
if (ctx.state.runningUnderAutogen) {
// This is not safe to run under autogen, because we'd be outputing
// autoloader files that predeclare the class and cause "warning:
// already initialized constant" errors
return empty;
}
auto lhs = ast::cast_tree<ast::UnresolvedConstantLit>(asgn->lhs);
if (lhs == nullptr) {
// Case for a non-constant literal such as `c = Class.new(Parent) do ... end`
auto send = ast::cast_tree<ast::Send>(asgn->rhs);
if (send == nullptr) {
return empty;
}
return rewriteWithBind(ctx, send);
} else {
// Case for a constant literal such as `C = Class.new(Parent) do ... end`
return rewriteAsClassDef(ctx, asgn);
}
}
vector<ast::ExpressionPtr> ClassNew::run(core::MutableContext ctx, ast::Send *send) {
// Case for a send such as `Class.new(Parent) do ... end`
return rewriteWithBind(ctx, send);
}
}; // namespace sorbet::rewriter