Commit 4e7933aa authored by Kevin Modzelewski's avatar Kevin Modzelewski

Inherit future flags during parsing

ie exec and eval statements are supposed to start with the future
flags of the scope in which they are called.  Our flags currently
get applied after parsing, which for the most part is fine, except
print_statement which actually changes the result of parsing.

ie `exec "print(1, 2)"` could either be parsed as a function call,
which will print "1 2", or as a print statement of a tuple, which
prints out "(1, 2)".  So thread the future flags through the parsing
stages as well.
parent e18f82ad
Subproject commit 1f62a285c57da50c00249a704deac00dae3c247b Subproject commit 728c4bc8c99d09da224ff3a2be82c98a1b026e82
...@@ -370,12 +370,12 @@ static CLFunction* compileForEvalOrExec(AST* source, std::vector<AST_stmt*> body ...@@ -370,12 +370,12 @@ static CLFunction* compileForEvalOrExec(AST* source, std::vector<AST_stmt*> body
return cl_f; return cl_f;
} }
static AST_Module* parseExec(llvm::StringRef source, bool interactive = false) { static AST_Module* parseExec(llvm::StringRef source, FutureFlags future_flags, bool interactive = false) {
// TODO error message if parse fails or if it isn't an expr // TODO error message if parse fails or if it isn't an expr
// TODO should have a cleaner interface that can parse the Expression directly // TODO should have a cleaner interface that can parse the Expression directly
// TODO this memory leaks // TODO this memory leaks
const char* code = source.data(); const char* code = source.data();
AST_Module* parsedModule = parse_string(code); AST_Module* parsedModule = parse_string(code, future_flags);
if (interactive) { if (interactive) {
for (int i = 0; i < parsedModule->body.size(); ++i) { for (int i = 0; i < parsedModule->body.size(); ++i) {
...@@ -397,7 +397,7 @@ static CLFunction* compileExec(AST_Module* parsedModule, BoxedString* fn, PyComp ...@@ -397,7 +397,7 @@ static CLFunction* compileExec(AST_Module* parsedModule, BoxedString* fn, PyComp
return compileForEvalOrExec(parsedModule, parsedModule->body, fn, flags); return compileForEvalOrExec(parsedModule, parsedModule->body, fn, flags);
} }
static AST_Expression* parseEval(llvm::StringRef source) { static AST_Expression* parseEval(llvm::StringRef source, FutureFlags future_flags) {
const char* code = source.data(); const char* code = source.data();
// TODO error message if parse fails or if it isn't an expr // TODO error message if parse fails or if it isn't an expr
...@@ -409,7 +409,7 @@ static AST_Expression* parseEval(llvm::StringRef source) { ...@@ -409,7 +409,7 @@ static AST_Expression* parseEval(llvm::StringRef source) {
while (*code == ' ' || *code == '\t' || *code == '\n' || *code == '\r') while (*code == ' ' || *code == '\t' || *code == '\n' || *code == '\r')
code++; code++;
AST_Module* parsedModule = parse_string(code); AST_Module* parsedModule = parse_string(code, future_flags);
if (parsedModule->body.size() == 0) if (parsedModule->body.size() == 0)
raiseSyntaxError("unexpected EOF while parsing", 0, 0, "<string>", ""); raiseSyntaxError("unexpected EOF while parsing", 0, 0, "<string>", "");
...@@ -496,11 +496,11 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) { ...@@ -496,11 +496,11 @@ Box* compile(Box* source, Box* fn, Box* type, Box** _args) {
llvm::StringRef source_str = static_cast<BoxedString*>(source)->s(); llvm::StringRef source_str = static_cast<BoxedString*>(source)->s();
if (type_str->s() == "exec") { if (type_str->s() == "exec") {
parsed = parseExec(source_str); parsed = parseExec(source_str, future_flags);
} else if (type_str->s() == "eval") { } else if (type_str->s() == "eval") {
parsed = parseEval(source_str); parsed = parseEval(source_str, future_flags);
} else if (type_str->s() == "single") { } else if (type_str->s() == "single") {
parsed = parseExec(source_str, true); parsed = parseExec(source_str, future_flags, true);
} else { } else {
raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'"); raiseExcHelper(ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'");
} }
...@@ -578,7 +578,11 @@ static Box* evalMain(Box* boxedCode, Box* globals, Box* locals, PyCompilerFlags* ...@@ -578,7 +578,11 @@ static Box* evalMain(Box* boxedCode, Box* globals, Box* locals, PyCompilerFlags*
CLFunction* cl; CLFunction* cl;
if (boxedCode->cls == str_cls) { if (boxedCode->cls == str_cls) {
AST_Expression* parsed = parseEval(static_cast<BoxedString*>(boxedCode)->s()); CLFunction* caller_cl = getTopPythonFunction();
assert(caller_cl != NULL);
assert(caller_cl->source != NULL);
AST_Expression* parsed = parseEval(static_cast<BoxedString*>(boxedCode)->s(), caller_cl->source->future_flags);
static BoxedString* string_string = internStringImmortal("<string>"); static BoxedString* string_string = internStringImmortal("<string>");
cl = compileEval(parsed, string_string, flags); cl = compileEval(parsed, string_string, flags);
} else if (boxedCode->cls == code_cls) { } else if (boxedCode->cls == code_cls) {
...@@ -627,7 +631,7 @@ Box* execfile(Box* _fn, Box* globals, Box* locals) { ...@@ -627,7 +631,7 @@ Box* execfile(Box* _fn, Box* globals, Box* locals) {
if (!exists) if (!exists)
raiseExcHelper(IOError, "No such file or directory: '%s'", fn->s().data()); raiseExcHelper(IOError, "No such file or directory: '%s'", fn->s().data());
AST_Module* parsed = caching_parse_file(fn->s().data()); AST_Module* parsed = caching_parse_file(fn->s().data(), /* future_flags = */ 0);
assert(parsed); assert(parsed);
CLFunction* caller_cl = getTopPythonFunction(); CLFunction* caller_cl = getTopPythonFunction();
...@@ -667,7 +671,11 @@ Box* execMain(Box* boxedCode, Box* globals, Box* locals, PyCompilerFlags* flags) ...@@ -667,7 +671,11 @@ Box* execMain(Box* boxedCode, Box* globals, Box* locals, PyCompilerFlags* flags)
CLFunction* cl; CLFunction* cl;
if (boxedCode->cls == str_cls) { if (boxedCode->cls == str_cls) {
auto parsed = parseExec(static_cast<BoxedString*>(boxedCode)->s()); CLFunction* caller_cl = getTopPythonFunction();
assert(caller_cl != NULL);
assert(caller_cl->source != NULL);
auto parsed = parseExec(static_cast<BoxedString*>(boxedCode)->s(), caller_cl->source->future_flags);
static BoxedString* string_string = internStringImmortal("<string>"); static BoxedString* string_string = internStringImmortal("<string>");
cl = compileExec(parsed, string_string, flags); cl = compileExec(parsed, string_string, flags);
} else if (boxedCode->cls == code_cls) { } else if (boxedCode->cls == code_cls) {
......
...@@ -1012,13 +1012,17 @@ static std::string getParserCommandLine(const char* fn) { ...@@ -1012,13 +1012,17 @@ static std::string getParserCommandLine(const char* fn) {
return std::string("/usr/bin/python -S ") + parse_ast_fn.str().str() + " " + fn; return std::string("/usr/bin/python -S ") + parse_ast_fn.str().str() + " " + fn;
} }
AST_Module* parse_string(const char* code) { AST_Module* parse_string(const char* code, FutureFlags inherited_flags) {
if (ENABLE_PYPA_PARSER) { inherited_flags &= ~(CO_NESTED | CO_FUTURE_DIVISION);
AST_Module* rtn = pypa_parse_string(code);
if (ENABLE_PYPA_PARSER || inherited_flags) {
AST_Module* rtn = pypa_parse_string(code, inherited_flags);
RELEASE_ASSERT(rtn, "unknown parse error (possibly: '%s'?)", strerror(errno)); RELEASE_ASSERT(rtn, "unknown parse error (possibly: '%s'?)", strerror(errno));
return rtn; return rtn;
} }
ASSERT(!inherited_flags, "the old cpython parser doesn't support specifying initial future flags");
int size = strlen(code); int size = strlen(code);
char buf[] = "pystontmp_XXXXXX"; char buf[] = "pystontmp_XXXXXX";
char* tmpdir = mkdtemp(buf); char* tmpdir = mkdtemp(buf);
...@@ -1033,17 +1037,17 @@ AST_Module* parse_string(const char* code) { ...@@ -1033,17 +1037,17 @@ AST_Module* parse_string(const char* code) {
fputc('\n', f); fputc('\n', f);
fclose(f); fclose(f);
AST_Module* m = parse_file(tmp.c_str()); AST_Module* m = parse_file(tmp.c_str(), inherited_flags);
removeDirectoryIfExists(tmpdir); removeDirectoryIfExists(tmpdir);
return m; return m;
} }
AST_Module* parse_file(const char* fn) { AST_Module* parse_file(const char* fn, FutureFlags inherited_flags) {
Timer _t("parsing"); Timer _t("parsing");
if (ENABLE_PYPA_PARSER) { if (ENABLE_PYPA_PARSER) {
AST_Module* rtn = pypa_parse(fn); AST_Module* rtn = pypa_parse(fn, inherited_flags);
RELEASE_ASSERT(rtn, "unknown parse error (possibly: '%s'?)", strerror(errno)); RELEASE_ASSERT(rtn, "unknown parse error (possibly: '%s'?)", strerror(errno));
return rtn; return rtn;
} }
...@@ -1070,9 +1074,9 @@ AST_Module* parse_file(const char* fn) { ...@@ -1070,9 +1074,9 @@ AST_Module* parse_file(const char* fn) {
const char* getMagic() { const char* getMagic() {
if (ENABLE_PYPA_PARSER) if (ENABLE_PYPA_PARSER)
return "a\ncM"; return "a\ncN";
else else
return "a\ncm"; return "a\ncn";
} }
#define MAGIC_STRING_LENGTH 4 #define MAGIC_STRING_LENGTH 4
...@@ -1080,7 +1084,9 @@ const char* getMagic() { ...@@ -1080,7 +1084,9 @@ const char* getMagic() {
#define CHECKSUM_LENGTH 1 #define CHECKSUM_LENGTH 1
// Does at least one of: returns a valid file_data vector, or fills in 'module' // Does at least one of: returns a valid file_data vector, or fills in 'module'
static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, AST_Module*& module) { static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, AST_Module*& module,
FutureFlags inherited_flags) {
inherited_flags &= ~(CO_NESTED | CO_FUTURE_DIVISION);
FILE* cache_fp = fopen(cache_fn.c_str(), "w"); FILE* cache_fp = fopen(cache_fn.c_str(), "w");
if (DEBUG_PARSING) { if (DEBUG_PARSING) {
...@@ -1110,8 +1116,8 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A ...@@ -1110,8 +1116,8 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A
file_data.insert(file_data.end(), (char*)&checksum, (char*)&checksum + CHECKSUM_LENGTH); file_data.insert(file_data.end(), (char*)&checksum, (char*)&checksum + CHECKSUM_LENGTH);
checksum = 0; checksum = 0;
if (ENABLE_PYPA_PARSER) { if (ENABLE_PYPA_PARSER || inherited_flags) {
module = pypa_parse(fn); module = pypa_parse(fn, inherited_flags);
RELEASE_ASSERT(module, "unknown parse error"); RELEASE_ASSERT(module, "unknown parse error");
if (!cache_fp) if (!cache_fp)
...@@ -1121,6 +1127,7 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A ...@@ -1121,6 +1127,7 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A
checksum = p.second; checksum = p.second;
bytes_written += p.first; bytes_written += p.first;
} else { } else {
ASSERT(!inherited_flags, "the old cpython parser doesn't support specifying initial future flags");
FILE* parser = popen(getParserCommandLine(fn).c_str(), "r"); FILE* parser = popen(getParserCommandLine(fn).c_str(), "r");
char buf[80]; char buf[80];
while (true) { while (true) {
...@@ -1157,7 +1164,7 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A ...@@ -1157,7 +1164,7 @@ static std::vector<char> _reparse(const char* fn, const std::string& cache_fn, A
// Parsing the file is somewhat expensive since we have to shell out to cpython; // Parsing the file is somewhat expensive since we have to shell out to cpython;
// it's not a huge deal right now, but this caching version can significantly cut down // it's not a huge deal right now, but this caching version can significantly cut down
// on the startup time (40ms -> 10ms). // on the startup time (40ms -> 10ms).
AST_Module* caching_parse_file(const char* fn) { AST_Module* caching_parse_file(const char* fn, FutureFlags inherited_flags) {
std::ostringstream oss; std::ostringstream oss;
if (DEBUG_PARSING) { if (DEBUG_PARSING) {
oss << "caching_parse_file() on " << fn << '\n'; oss << "caching_parse_file() on " << fn << '\n';
...@@ -1192,7 +1199,7 @@ AST_Module* caching_parse_file(const char* fn) { ...@@ -1192,7 +1199,7 @@ AST_Module* caching_parse_file(const char* fn) {
if (ferror(cache_fp)) { if (ferror(cache_fp)) {
oss << "encountered io error reading from the file\n"; oss << "encountered io error reading from the file\n";
AST_Module* mod = 0; AST_Module* mod = 0;
file_data = _reparse(fn, cache_fn, mod); file_data = _reparse(fn, cache_fn, mod, inherited_flags);
if (mod) if (mod)
return mod; return mod;
assert(file_data.size()); assert(file_data.size());
...@@ -1295,7 +1302,7 @@ AST_Module* caching_parse_file(const char* fn) { ...@@ -1295,7 +1302,7 @@ AST_Module* caching_parse_file(const char* fn) {
file_data.clear(); file_data.clear();
AST_Module* mod = 0; AST_Module* mod = 0;
file_data = _reparse(fn, cache_fn, mod); file_data = _reparse(fn, cache_fn, mod, inherited_flags);
if (mod) if (mod)
return mod; return mod;
assert(file_data.size()); assert(file_data.size());
......
...@@ -15,14 +15,15 @@ ...@@ -15,14 +15,15 @@
#ifndef PYSTON_CODEGEN_PARSER_H #ifndef PYSTON_CODEGEN_PARSER_H
#define PYSTON_CODEGEN_PARSER_H #define PYSTON_CODEGEN_PARSER_H
#include "core/types.h"
namespace pyston { namespace pyston {
class AST_Module; class AST_Module;
AST_Module* parse_string(const char* code); AST_Module* parse_string(const char* code, FutureFlags inherited_flags);
AST_Module* parse_file(const char* fn, FutureFlags inherited_flags);
AST_Module* parse_file(const char* fn); AST_Module* caching_parse_file(const char* fn, FutureFlags inherited_flags);
AST_Module* caching_parse_file(const char* fn);
} }
#endif #endif
...@@ -1163,7 +1163,7 @@ private: ...@@ -1163,7 +1163,7 @@ private:
int position; int position;
}; };
static AST_Module* parse_with_reader(std::unique_ptr<pypa::Reader> reader) { static AST_Module* parse_with_reader(std::unique_ptr<pypa::Reader> reader, FutureFlags future_flags) {
pypa::Lexer lexer(std::move(reader)); pypa::Lexer lexer(std::move(reader));
pypa::SymbolTablePtr symbols; pypa::SymbolTablePtr symbols;
pypa::AstModulePtr module; pypa::AstModulePtr module;
...@@ -1176,6 +1176,35 @@ static AST_Module* parse_with_reader(std::unique_ptr<pypa::Reader> reader) { ...@@ -1176,6 +1176,35 @@ static AST_Module* parse_with_reader(std::unique_ptr<pypa::Reader> reader) {
options.error_handler = pypaErrorHandler; options.error_handler = pypaErrorHandler;
options.escape_handler = pypaEscapeDecoder; options.escape_handler = pypaEscapeDecoder;
if (future_flags & CO_FUTURE_PRINT_FUNCTION) {
future_flags &= ~CO_FUTURE_PRINT_FUNCTION;
options.initial_future_features.print_function = true;
}
if (future_flags & CO_FUTURE_DIVISION) {
future_flags &= ~CO_FUTURE_DIVISION;
options.initial_future_features.division = true;
}
if (future_flags & CO_FUTURE_ABSOLUTE_IMPORT) {
future_flags &= ~CO_FUTURE_ABSOLUTE_IMPORT;
options.initial_future_features.absolute_imports = true;
}
if (future_flags & CO_FUTURE_WITH_STATEMENT) {
future_flags &= ~CO_FUTURE_WITH_STATEMENT;
options.initial_future_features.with_statement = true;
}
if (future_flags & CO_FUTURE_UNICODE_LITERALS) {
future_flags &= ~CO_FUTURE_UNICODE_LITERALS;
options.initial_future_features.unicode_literals = true;
}
// Strip out some flags:
future_flags &= ~(CO_NESTED);
RELEASE_ASSERT(!future_flags, "0x%x", future_flags);
if (pypa::parse(lexer, module, symbols, options) && module) { if (pypa::parse(lexer, module, symbols, options) && module) {
return readModule(*module); return readModule(*module);
} }
...@@ -1199,17 +1228,17 @@ PyObject* PystonStringReader::open_python_file() noexcept { ...@@ -1199,17 +1228,17 @@ PyObject* PystonStringReader::open_python_file() noexcept {
return PycStringIO->NewInput(s); return PycStringIO->NewInput(s);
} }
AST_Module* pypa_parse(char const* file_path) { AST_Module* pypa_parse(char const* file_path, FutureFlags future_flags) {
auto reader = PystonFileReader::create(file_path); auto reader = PystonFileReader::create(file_path);
if (!reader) if (!reader)
return nullptr; return nullptr;
return parse_with_reader(std::move(reader)); return parse_with_reader(std::move(reader), future_flags);
} }
AST_Module* pypa_parse_string(char const* str) { AST_Module* pypa_parse_string(char const* str, FutureFlags future_flags) {
auto reader = llvm::make_unique<PystonStringReader>(str); auto reader = llvm::make_unique<PystonStringReader>(str);
return parse_with_reader(std::move(reader)); return parse_with_reader(std::move(reader), future_flags);
} }
} }
...@@ -17,10 +17,12 @@ ...@@ -17,10 +17,12 @@
#include <cstdio> #include <cstdio>
#include "core/types.h"
namespace pyston { namespace pyston {
class AST_Module; class AST_Module;
AST_Module* pypa_parse(char const* file_path); AST_Module* pypa_parse(char const* file_path, FutureFlags future_flags);
AST_Module* pypa_parse_string(char const* str); AST_Module* pypa_parse_string(char const* str, FutureFlags future_flags);
} }
#endif // PYSTON_CODEGEN_PYPAPARSER_H #endif // PYSTON_CODEGEN_PYPAPARSER_H
...@@ -418,7 +418,7 @@ static int main(int argc, char** argv) { ...@@ -418,7 +418,7 @@ static int main(int argc, char** argv) {
if (command != NULL) { if (command != NULL) {
try { try {
main_module = createModule(boxString("__main__"), "<string>"); main_module = createModule(boxString("__main__"), "<string>");
AST_Module* m = parse_string(command); AST_Module* m = parse_string(command, /* future_flags = */ 0);
compileAndRunModule(m, main_module); compileAndRunModule(m, main_module);
rtncode = 0; rtncode = 0;
} catch (ExcInfo e) { } catch (ExcInfo e) {
...@@ -457,7 +457,7 @@ static int main(int argc, char** argv) { ...@@ -457,7 +457,7 @@ static int main(int argc, char** argv) {
main_module = createModule(boxString("__main__"), fn); main_module = createModule(boxString("__main__"), fn);
try { try {
AST_Module* ast = caching_parse_file(fn); AST_Module* ast = caching_parse_file(fn, /* future_flags = */ 0);
compileAndRunModule(ast, main_module); compileAndRunModule(ast, main_module);
} catch (ExcInfo e) { } catch (ExcInfo e) {
setCAPIException(e); setCAPIException(e);
...@@ -487,7 +487,7 @@ static int main(int argc, char** argv) { ...@@ -487,7 +487,7 @@ static int main(int argc, char** argv) {
add_history(line); add_history(line);
try { try {
AST_Module* m = parse_string(line); AST_Module* m = parse_string(line, /* future_flags = */ 0);
Timer _t("repl"); Timer _t("repl");
......
...@@ -43,7 +43,7 @@ static void removeModule(BoxedString* name) { ...@@ -43,7 +43,7 @@ static void removeModule(BoxedString* name) {
Box* createAndRunModule(BoxedString* name, const std::string& fn) { Box* createAndRunModule(BoxedString* name, const std::string& fn) {
BoxedModule* module = createModule(name, fn.c_str()); BoxedModule* module = createModule(name, fn.c_str());
AST_Module* ast = caching_parse_file(fn.c_str()); AST_Module* ast = caching_parse_file(fn.c_str(), /* future_flags = */ 0);
assert(ast); assert(ast);
try { try {
compileAndRunModule(ast, module); compileAndRunModule(ast, module);
...@@ -69,7 +69,7 @@ static Box* createAndRunModule(BoxedString* name, const std::string& fn, const s ...@@ -69,7 +69,7 @@ static Box* createAndRunModule(BoxedString* name, const std::string& fn, const s
static BoxedString* path_str = internStringImmortal("__path__"); static BoxedString* path_str = internStringImmortal("__path__");
module->setattr(path_str, path_list, NULL); module->setattr(path_str, path_list, NULL);
AST_Module* ast = caching_parse_file(fn.c_str()); AST_Module* ast = caching_parse_file(fn.c_str(), /* future_flags = */ 0);
assert(ast); assert(ast);
try { try {
compileAndRunModule(ast, module); compileAndRunModule(ast, module);
...@@ -646,7 +646,7 @@ extern "C" PyObject* PyImport_ExecCodeModuleEx(char* name, PyObject* co, char* p ...@@ -646,7 +646,7 @@ extern "C" PyObject* PyImport_ExecCodeModuleEx(char* name, PyObject* co, char* p
static BoxedString* file_str = internStringImmortal("__file__"); static BoxedString* file_str = internStringImmortal("__file__");
module->setattr(file_str, boxString(pathname), NULL); module->setattr(file_str, boxString(pathname), NULL);
AST_Module* ast = parse_string(code->data()); AST_Module* ast = parse_string(code->data(), /* future_flags = */ 0);
compileAndRunModule(ast, module); compileAndRunModule(ast, module);
return module; return module;
} catch (ExcInfo e) { } catch (ExcInfo e) {
......
...@@ -52,3 +52,9 @@ except TypeError: ...@@ -52,3 +52,9 @@ except TypeError:
for i in xrange(10): for i in xrange(10):
print() print()
# These two should output "1 2"
exec """print(1, 2)"""
eval("print(1, 2)")
# This one should output "(1, 2)"
exec compile("print(1, 2)", "<string>", "exec", dont_inherit=True)
...@@ -26,7 +26,7 @@ protected: ...@@ -26,7 +26,7 @@ protected:
TEST_F(AnalysisTest, augassign) { TEST_F(AnalysisTest, augassign) {
const std::string fn("test/unittests/analysis_listcomp.py"); const std::string fn("test/unittests/analysis_listcomp.py");
AST_Module* module = caching_parse_file(fn.c_str()); AST_Module* module = caching_parse_file(fn.c_str(), 0);
assert(module); assert(module);
ScopingAnalysis *scoping = new ScopingAnalysis(module, true); ScopingAnalysis *scoping = new ScopingAnalysis(module, true);
...@@ -59,7 +59,7 @@ TEST_F(AnalysisTest, augassign) { ...@@ -59,7 +59,7 @@ TEST_F(AnalysisTest, augassign) {
void doOsrTest(bool is_osr, bool i_maybe_undefined) { void doOsrTest(bool is_osr, bool i_maybe_undefined) {
const std::string fn("test/unittests/analysis_osr.py"); const std::string fn("test/unittests/analysis_osr.py");
AST_Module* module = caching_parse_file(fn.c_str()); AST_Module* module = caching_parse_file(fn.c_str(), 0);
assert(module); assert(module);
ScopingAnalysis *scoping = new ScopingAnalysis(module, true); ScopingAnalysis *scoping = new ScopingAnalysis(module, true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment