diff --git a/src/org/rascalmpl/interpreter/env/GlobalEnvironment.java b/src/org/rascalmpl/interpreter/env/GlobalEnvironment.java index 7afa17c4565..b7f1bd4ce6b 100644 --- a/src/org/rascalmpl/interpreter/env/GlobalEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/GlobalEnvironment.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.QualifiedName; @@ -34,6 +35,7 @@ import org.rascalmpl.values.IRascalValueFactory; import io.usethesource.capsule.SetMultimap; +import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IList; import io.usethesource.vallang.IString; @@ -329,4 +331,11 @@ public void clearLookupChaches() { public void clearModuleLoadMessage() { moduleEnvironment.values().forEach(ModuleEnvironment::clearLoadMessages); } + + public Stream streamModuleLoadMessages() { + return moduleEnvironment + .values() + .stream() + .flatMap(me -> me.streamLoadMessages()); + } } diff --git a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java index 98a1bf90269..495a090ae6c 100644 --- a/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java +++ b/src/org/rascalmpl/interpreter/env/ModuleEnvironment.java @@ -32,6 +32,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Stream; import org.rascalmpl.ast.AbstractAST; import org.rascalmpl.ast.KeywordFormal; @@ -143,6 +144,10 @@ public void clearLookupCaches() { public void clearLoadMessages() { this.loadMessages.clear(); } + + public Stream streamLoadMessages() { + return loadMessages.stream(); + } public void extend(ModuleEnvironment other) { extendNameFlags(other); diff --git a/src/org/rascalmpl/library/lang/rascal/tests/loading/LoadingErrorModules.rsc b/src/org/rascalmpl/library/lang/rascal/tests/loading/LoadingErrorModules.rsc new file mode 100644 index 00000000000..3a804e6cf12 --- /dev/null +++ b/src/org/rascalmpl/library/lang/rascal/tests/loading/LoadingErrorModules.rsc @@ -0,0 +1,130 @@ +module lang::rascal::tests::loading::LoadingErrorModules + +import IO; +import util::Eval; +import util::PathConfig; +import Message; + +PathConfig init() = pathConfig(srcs=[|memory://LoadingErrorModules/|]); + +loc moduleFile(str name) = |memory://LoadingErrorModules/| + ".rsc"; + +test bool moduleWithParseError() { + exec = createRascalRuntime(pcfg=init()); + + writeFile(moduleFile("A"), "modle A"); + + try { + exec.eval(#void, "import A;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("A"), "module A"); + + return exec.eval(#void, "import A;") == ok(); +} + +test bool moduleWithTransientParseError() { + exec = createRascalRuntime(pcfg=init()); + + writeFile(moduleFile("A"), "module A"); + assert exec.eval(#void, "import A;") == ok(); + writeFile(moduleFile("A"), "modle A"); + + try { + exec.eval(#void, "import A;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("A"), "module A"); + + return exec.eval(#void, "import A;") == ok(); +} + +test bool moduleWithTransitiveParseError() { + exec = createRascalRuntime(pcfg=init()); + + writeFile(moduleFile("A"), "modle A"); + writeFile(moduleFile("B"), "module B import A;"); + + try { + exec.eval(#void, "import B;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("A"), "module A"); + + return exec.eval(#void, "import A;") == ok() + && exec.eval(#void, "import B;") == ok(); +} + +test bool moduleWithStaticError() { + exec = createRascalRuntime(pcfg=init()); + + writeFile(moduleFile("A"), "module A str aap = 42;"); + + try { + exec.eval(#void, "import A;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("A"), "module A str aap = \"42\";"); + + return exec.eval(#void, "import A;") == ok(); +} + +test bool importNonExistingModule() { + exec = createRascalRuntime(pcfg=init()); + + try { + exec.eval(#void, "import Z;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("Z"), "module Z public str aap = \"aap\";"); + + return exec.eval(#void, "import Z;") == ok() + && result("aap") == exec.eval(#str, "aap"); +} + + +test bool importBrokenModuleName() { + exec = createRascalRuntime(pcfg=init()); + + writeFile(moduleFile("AAA"), "module AA public str aap = \"aap\";"); + + try { + exec.eval(#void, "import AAA;"); + return false; + } + catch ModuleLoadMessages([error(_,_)]): { + // that's ok + ; + } + + writeFile(moduleFile("AAA"), "module AAA public str aap = \"aap\";"); + + return exec.eval(#void, "import AAA;") == ok() + && result("aap") == exec.eval(#str, "aap"); +} + diff --git a/src/org/rascalmpl/library/util/Eval.java b/src/org/rascalmpl/library/util/Eval.java index f79f9fd60db..f38d4f439f8 100644 --- a/src/org/rascalmpl/library/util/Eval.java +++ b/src/org/rascalmpl/library/util/Eval.java @@ -20,7 +20,6 @@ import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; - import org.rascalmpl.debug.IRascalMonitor; import org.rascalmpl.exceptions.RuntimeExceptionFactory; import org.rascalmpl.exceptions.Throw; @@ -31,6 +30,7 @@ import org.rascalmpl.interpreter.result.Result; import org.rascalmpl.interpreter.staticErrors.StaticError; import org.rascalmpl.interpreter.staticErrors.UnexpectedType; +import org.rascalmpl.library.Messages; import org.rascalmpl.shell.ShellEvaluatorFactory; import org.rascalmpl.types.RascalTypeFactory; import org.rascalmpl.types.TypeReifier; @@ -40,6 +40,7 @@ import io.usethesource.vallang.IConstructor; import io.usethesource.vallang.IInteger; +import io.usethesource.vallang.IList; import io.usethesource.vallang.IString; import io.usethesource.vallang.IValue; import io.usethesource.vallang.type.Type; @@ -58,7 +59,8 @@ public class Eval { public final Type Result_void = tf.constructor(store, Result, "ok"); public final Type Result_value = tf.constructor(store, Result, "result", param, "val"); public final Type Exception = tf.abstractDataType(store, "Exception"); - public final Type Exception_StaticError = tf.constructor(store, Exception, "StaticError", tf.stringType(), "messages", tf.sourceLocationType(), "location"); + public final Type Exception_StaticError = tf.constructor(store, Exception, "StaticError", tf.stringType(), "message", tf.sourceLocationType(), "location"); + public final Type Exception_LoadMessages = tf.constructor(store, Exception, "ModuleLoadMessages", tf.listType(Messages.Message), "messages"); private final Type resetType = tf.functionType(tf.voidType(), tf.tupleEmpty(), tf.tupleEmpty()); private final Type setTimeoutType = tf.functionType(tf.voidType(), tf.tupleType(tf.integerType()), tf.tupleEmpty()); private final Type evalType = tf.functionType(Result_value, tf.tupleType(TypeTyp, tf.stringType()), tf.tupleEmpty()); @@ -175,6 +177,11 @@ private IFunction buildEvalFunction(RascalRuntime exec) { throw new UnexpectedType(typ, result.getStaticType(), URIUtil.rootLocation("eval")); } + IList loadMessages = exec.moduleLoadMessages(); + if (loadMessages.stream().anyMatch(c -> ((IConstructor) c).getName().equals("error"))) { + throw new Throw(values.constructor(Exception_LoadMessages, loadMessages), null, null); + } + if (result.getStaticType().isBottom()) { return values.constructor(Result_void); } @@ -231,6 +238,10 @@ public void reset() { eval.getHeap().clear(); } + public IList moduleLoadMessages() { + return eval.__getHeap().streamModuleLoadMessages().collect(eval.getValueFactory().listWriter()); + } + public Result eval(IRascalMonitor monitor, String line) throws InterruptedException, IOException { return eval.eval(monitor, line, IRascalValueFactory.getInstance().sourceLocation(URIUtil.assumeCorrect("eval", "", "", "command=" + line))); } diff --git a/src/org/rascalmpl/library/util/Eval.rsc b/src/org/rascalmpl/library/util/Eval.rsc index daaa76da7c5..b828edf21da 100644 --- a/src/org/rascalmpl/library/util/Eval.rsc +++ b/src/org/rascalmpl/library/util/Eval.rsc @@ -13,7 +13,9 @@ module util::Eval extend Exception; extend util::Reflective; + import IO; +import Message; @synopsis{Results encode the output of a call to `eval`} @description{ @@ -31,6 +33,7 @@ data Result[&T] } data RuntimeException = StaticError(str message, loc location) + | ModuleLoadMessages(list[Message] messages) ; @synopsis{A reusable instance of the Rascal runtime system configured by a specific PathConfig.} @@ -96,7 +99,7 @@ This creates a ((RascalRuntime)), uses it to evaluate one command, and then disc } @deprecated{Use ((createRascalRuntime)) for better efficiency/configurability.} Result[&T] eval(type[&T] typ, str command, int duration=-1, PathConfig pcfg=pathConfig()) - throws Timeout, StaticError, ParseError + throws Timeout, StaticError, ParseError, ModuleLoadMessages = eval(typ, [command], pcfg=pcfg, duration=duration); @synopsis{Evaluate a list of command and return the value of the last command.} diff --git a/src/org/rascalmpl/semantics/dynamic/Import.java b/src/org/rascalmpl/semantics/dynamic/Import.java index 276bb4173f2..a42063cd6fb 100644 --- a/src/org/rascalmpl/semantics/dynamic/Import.java +++ b/src/org/rascalmpl/semantics/dynamic/Import.java @@ -344,11 +344,17 @@ public static ModuleEnvironment loadModule(ISourceLocation x, String name, IEval ISourceLocation uri = eval.getRascalResolver().resolveModule(name); + if (uri == null) { + heap.setModuleURI(jobName, URIUtil.correctLocation("not-found", name, "").getURI()); + throw new ModuleImport(name, "can not find in search path", x); + } + else { + heap.setModuleURI(name, uri.getURI()); + } + try { eval.jobTodo(jobName, 1); - if (uri == null) { - throw new ModuleImport(name, "can not find in search path", x); - } + Module module = buildModule(uri, env, eval, jobName); if (isDeprecated(module)) { @@ -360,7 +366,7 @@ public static ModuleEnvironment loadModule(ISourceLocation x, String name, IEval if (!internalName.equals(name)) { throw new ModuleNameMismatch(internalName, name, x); } - heap.setModuleURI(name, module.getLocation().getURI()); + module.interpret(eval); } @@ -434,7 +440,7 @@ private static ASTBuilder getBuilder() { } private static void addImportToCurrentModule(ISourceLocation src, String name, IEvaluator> eval) { - ModuleEnvironment module = eval.getHeap().getModule(name); + ModuleEnvironment module = eval.getHeap().getModule(name); if (module == null) { throw new UndeclaredModule(name, src); }