GCC Code Coverage Report


Directory: ./
File: src/sema/sema_import.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 97 114 85.1%
Functions: 14 17 82.4%
Branches: 86 109 78.9%

Line Branch Exec Source
1 #if __linux__
2 # define __STDC_WANT_LIB_EXT1__ 1
3 # define __STDC_WANT_SECURE_LIB__ 1
4 #endif
5
6 #include <cstdio>
7 #include <cstdlib>
8
9 #include <filesystem>
10
11 #include "lexer/buffer.h"
12 #include "lexer/lexer.h"
13 #include "parser/parser.h"
14 #include "sema/sema.h"
15 #include "utilities/strings.h"
16
17 // getenv(name)
18 // setenv(name, value, override)
19 // unsetenv(name)
20
21 namespace lython {
22
23 String internal_getenv(String const& name) {
24 234 const char* envname = name.c_str();
25
26 #if (defined __STDC_LIB_EXT1__) || BUILD_WINDOWS
27 size_t size = 0;
28
29 getenv_s(&size, nullptr, 0, envname);
30
31 // an error happened
32 if (size == 0) {
33 return {};
34 }
35
36 String path(size, ' ');
37 int err = getenv_s(&size, path.data(), path.size(), envname);
38
39 if (err != 0) {
40 return String();
41 }
42 #else
43 234 const char* value = getenv(envname);
44
45
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (value == nullptr) {
46 234 return String();
47 }
48
49 auto path = String(value);
50 #endif
51
52 return path;
53 }
54
55 Array<String> python_paths() {
56
2/2
✓ Branch 2 taken 234 times.
✓ Branch 5 taken 234 times.
234 String path = internal_getenv("PYTHONPATH");
57
58
1/1
✓ Branch 1 taken 234 times.
468 return split(':', path);
59 234 }
60
61 String lookup_module(StringRef const& module_path, Array<String> const& paths) {
62 // Look for the module in the path
63 // env/3.9.7/lib/python39.zip
64 // env/3.9.7/lib/python3.9
65 // env/3.9.7/lib/python3.9/lib-dynload
66 // env/3.9.7/lib/python3.9/site-packages
67 // handle PTH files
68 // add the module to the context
69
70 // Check current directory for the module
71 // Check the path from first to last
72
73 namespace fs = std::filesystem;
74
75 kwdebug("{}", str(paths));
76
2/2
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
12 auto module_frags = split('.', str(module_path));
77
78
2/2
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 8 times.
32 for (auto const& path: paths) {
79
2/2
✓ Branch 1 taken 24 times.
✓ Branch 4 taken 24 times.
24 auto stat = fs::status(path);
80
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
24 if (!fs::is_directory(stat)) {
81 kwdebug("Not a directory {}", path);
82 20 continue;
83 12 }
84
85
3/5
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 12 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
24 Array<String> fspath_frags = {path};
86
1/1
✓ Branch 2 taken 12 times.
12 fspath_frags.reserve(module_frags.size());
87
88 // <path>/<module_frags>
89
2/2
✓ Branch 1 taken 12 times.
✓ Branch 6 taken 12 times.
12 std::copy(
90 std::begin(module_frags), std::end(module_frags), std::back_inserter(fspath_frags));
91
92
2/2
✓ Branch 2 taken 12 times.
✓ Branch 5 taken 12 times.
12 auto fspath = join("/", fspath_frags);
93
2/2
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
12 stat = fs::status(fspath);
94
95 // TODO: check for so files
96 //
97
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (fs::is_directory(stat)) {
98 // Load a folder module
99 // import my.module => my/module/__init__.py
100 fspath += "/__init__.py";
101 } else {
102 // Load a file module
103 // import my.module => my/module.py
104
1/1
✓ Branch 1 taken 12 times.
12 fspath += ".py";
105 }
106
107
2/2
✓ Branch 1 taken 12 times.
✓ Branch 4 taken 12 times.
12 stat = fs::status(fspath);
108
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
12 if (!fs::exists(stat)) {
109 kwdebug("not a file {}", fspath);
110 8 continue;
111 8 }
112
113 kwdebug("Found file {}", fspath);
114 4 return fspath;
115 24 }
116
117
1/1
✓ Branch 2 taken 8 times.
8 return "";
118 24 }
119
120 Module* process_file(StringRef const& modulepath, Array<String> const& paths) {
121
1/1
✓ Branch 1 taken 12 times.
12 String filepath = lookup_module(modulepath, paths);
122
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
12 if (filepath.empty()) {
123 8 return nullptr;
124 }
125
126
1/1
✓ Branch 1 taken 4 times.
4 FileBuffer buffer(filepath);
127
1/1
✓ Branch 1 taken 4 times.
4 Lexer lexer(buffer);
128
1/1
✓ Branch 1 taken 4 times.
4 Parser parser(lexer);
129
1/1
✓ Branch 1 taken 4 times.
4 Module* mod = parser.parse_module();
130 4 return mod;
131 12 }
132
133 TypeExpr* SemanticAnalyser::import(Import* n, int depth) {
134 // import datetime, time
135 // import math as m
136
2/2
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 4 times.
12 for (auto& name: n->names) {
137
1/1
✓ Branch 1 taken 8 times.
8 StringRef nm = name.name;
138
1/1
✓ Branch 1 taken 8 times.
8 auto* mod = process_file(name.name, paths);
139
140
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (mod == nullptr) {
141 SEMA_ERROR(n, ModuleNotFoundError, name.name);
142 6 continue;
143 6 }
144
145
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (name.asname.has_value()) {
146
1/1
✓ Branch 2 taken 2 times.
2 nm = name.asname.value();
147 }
148
149 // Check ownership of Module
150 // we could make the import statement the owner
151 // but it could be imported multiple times
152 // in that case we would like to avoid doing SEMA
153 // and reuse the same version
154 // we could also import modules using multiple threads
155 // so we will need a place to manage all those modules
156 // sounds like shared_ptr might the easiest
157 // mod->move(n);
158
159 // TODO: this needs to be kept somewhere
160
1/1
✓ Branch 1 taken 2 times.
2 SemanticAnalyser sema;
161
1/1
✓ Branch 1 taken 2 times.
2 sema.exec(mod, 0);
162
163
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 bindings.add(nm, mod, lython::Module_t());
164 8 }
165 4 return nullptr;
166 }
167
168 StringRef get_name(ExprNode* target) {
169 14 auto* name = cast<Name>(target);
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (name == nullptr) {
171 return StringRef();
172 }
173
174 14 return name->id;
175 }
176
177 StmtNode* find(Array<StmtNode*> const& body, StringRef const& name) {
178
1/2
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
20 for (auto* stmt: body) {
179
4/5
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
20 switch (stmt->kind) {
180 2 case NodeKind::ClassDef: {
181
1/1
✓ Branch 1 taken 2 times.
2 auto* def = cast<ClassDef>(stmt);
182
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (def->name == name) {
183 8 return stmt;
184 }
185 continue;
186 }
187 4 case NodeKind::FunctionDef: {
188
1/1
✓ Branch 1 taken 4 times.
4 auto* def = cast<FunctionDef>(stmt);
189
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (def->name == name) {
190 2 return stmt;
191 }
192 2 continue;
193 2 }
194 8 case NodeKind::Assign: {
195
1/1
✓ Branch 1 taken 8 times.
8 auto* ass = cast<Assign>(stmt);
196
3/3
✓ Branch 2 taken 8 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 6 times.
8 if (get_name(ass->targets[0]) == name) {
197 2 return ass;
198 }
199 6 continue;
200 6 }
201 6 case NodeKind::AnnAssign: {
202
1/1
✓ Branch 1 taken 6 times.
6 auto* ann = cast<AnnAssign>(stmt);
203
3/3
✓ Branch 1 taken 6 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
6 if (get_name(ann->target) == name) {
204 2 return ann;
205 }
206 4 continue;
207 4 }
208 default: continue;
209 }
210 }
211
212 return nullptr;
213 }
214
215 TypeExpr* SemanticAnalyser::importfrom(ImportFrom* n, int depth) {
216 4 Module* mod = nullptr;
217
218 // Regular import using system path
219
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
4 if (n->module.has_value() && !n->level.has_value()) {
220
1/1
✓ Branch 2 taken 4 times.
4 mod = process_file(n->module.value(), paths);
221
222
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (mod == nullptr) {
223 SEMA_ERROR(n, ModuleNotFoundError, n->module.value());
224 2 return nullptr;
225 }
226 } else if (n->level.has_value()) {
227 // relative import using level
228
229 if (mod == nullptr) {
230 SEMA_ERROR(n, ModuleNotFoundError, n->module.value());
231 return nullptr;
232 }
233 }
234
235 // TODO: Someone must be the owner of module
236 // mod->move(n);
237
238 // TODO: this needs to be kept somewhere
239
1/1
✓ Branch 1 taken 2 times.
2 SemanticAnalyser sema;
240
1/1
✓ Branch 1 taken 2 times.
2 sema.exec(mod, 0);
241
242
2/2
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 2 times.
10 for (auto& name: n->names) {
243
1/1
✓ Branch 1 taken 8 times.
8 StringRef nm = name.name;
244
245 // lookup name.name inside module;
246 // functions, classes are stmt
247 // but variable could also be imported which are expressions
248
1/1
✓ Branch 1 taken 8 times.
8 StmtNode* value = find(mod->body, nm);
249
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (value == nullptr) {
251 kwdebug("{} not found", nm);
252 continue;
253 }
254
255
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 auto varid = sema.bindings.get_varid(nm);
256 8 auto* type = sema.bindings.get_type(varid);
257
258 // Did not find the value inside the module
259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (value == nullptr) {
260 SEMA_ERROR(n, ImportError, n->module.value(), nm);
261 continue;
262 }
263
264 //
265
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if (name.asname.has_value()) {
266
1/1
✓ Branch 2 taken 8 times.
8 nm = name.asname.value();
267 }
268
269
1/1
✓ Branch 1 taken 8 times.
8 bindings.add(nm, value, type);
270 8 }
271
272 2 return nullptr;
273 2 }
274
275 } // namespace lython
276