diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java index 0b69721bd..193f8cbb4 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java @@ -167,7 +167,8 @@ IFunction makeParseTreeGetter(Evaluator e) { return e.getFunctionValueFactory().function(getParseTreeType, (t, u) -> { ISourceLocation resolvedLocation = Locations.toClientLocation((ISourceLocation) t[0]); try { - var tree = rascalTextDocumentService.getFile(resolvedLocation).getCurrentTreeAsync(false).get(); + // although we cannot type-check modules with errors, we prefer to get the errors here instead of retrying the parse and still failing after this try-block + var tree = rascalTextDocumentService.getFile(resolvedLocation).getCurrentTreeAsync(true).get(); if (tree != null) { return tree.get(); } diff --git a/rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/IDECheckerWrapper.rsc b/rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/IDECheckerWrapper.rsc index d8ffd1074..dae685569 100644 --- a/rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/IDECheckerWrapper.rsc +++ b/rascal-lsp/src/main/rascal/lsp/lang/rascal/lsp/IDECheckerWrapper.rsc @@ -36,7 +36,7 @@ import Location; import analysis::graphs::Graph; import util::FileSystem; import util::Monitor; -import util::Reflective; +import util::ParseErrorRecovery; import lang::rascal::\syntax::Rascal; import lang::rascalcore::check::Checker; @@ -53,7 +53,30 @@ import lang::rascalcore::check::ModuleLocations; } map[loc, set[Message]] checkFile(loc l, set[loc] workspaceFolders, start[Module](loc file) getParseTree, PathConfig(loc file) getPathConfig) = job("Rascal check", map[loc, set[Message]](void(str, int) step) { - checkForImports = [getParseTree(l)]; + + tuple[start[Module], set[Message]] getParseTreeOrErrors(loc l, str name, loc errorLocation) { + try { + t = getParseTree(l); + errors = hasParseErrors(t) + ? {error("Cannot typecheck this module, since dependency `` has parse error(s).", errorLocation, + causes=[error("Parse error around this position.", e.src) | e <- findBestParseErrors(t)])} + : {}; + return ; + } catch ParseError(loc err): { + return <(start[Module]) `module ModuleHadParseError`, {error("Cannot typecheck this module, since dependency `` has parse error(s).", errorLocation, causes=[error("Parse error(s).", err)])}>; + } + } + + // Note: check further down parses again, possibly leading to a different tree if the contents changed in the meantime. + // We cannot fix that here, unless we pass `getParseTree` to `check`. + = getParseTreeOrErrors(l, "unknown", l); + if ({} != parseErrors) { + // No need to return the errors, since the language server will take care of parse errors in open modules + return (); + } + + openFileHeader = openFile.top.header.name; + checkForImports = [openFile]; checkedForImports = {}; initialProject = inferProjectRoot(l); @@ -67,14 +90,23 @@ map[loc, set[Message]] checkFile(loc l, set[loc] workspaceFolders, start[Module] currentProject = inferProjectRoot(currentSrc); if (currentProject in workspaceFolders && currentProject.file notin {"rascal", "rascal-lsp"}) { for (i <- tree.top.header.imports, i has \module) { + modName = ""; try { - ml = locateRascalModule("", getPathConfig(currentProject), getPathConfig, workspaceFolders); - if (ml.extension == "rsc", mlpt := getParseTree(ml), mlpt.src.top notin checkedForImports) { - checkForImports += mlpt; - jobTodo("Building dependency graph"); - dependencies += ; + ml = locateRascalModule(modName, getPathConfig(currentProject), getPathConfig, workspaceFolders); + if ( := getParseTreeOrErrors(ml, modName, openFileHeader.src)) { + if ({} !:= importErrors) { + parseErrors += importErrors; + checkedForImports += currentSrc; // do not check this module again + continue; // since there is an error in this module, we do not recurse into its imports + } + if (mlpt.src.top notin checkedForImports) { + checkForImports += mlpt; + jobTodo("Building dependency graph"); + dependencies += ; + } } - } catch _: { + } catch e: { + println("Exception while building dependency graph at : "); ;// Continue } } @@ -85,6 +117,11 @@ map[loc, set[Message]] checkFile(loc l, set[loc] workspaceFolders, start[Module] return true; }, totalWork=1); + if ({} != parseErrors) { + // Since we only reported errors on `l`, there is not need to analyze to which files the errors belong here. + return (l: parseErrors); + } + cyclicDependencies = {p | <- (dependencies - ident(carrier(dependencies)))+}; if (cyclicDependencies != {}) { return (l : {error("Cyclic dependencies detected between projects {}. This is not supported. Fix your project setup.", l)});