diff --git a/rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc b/rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc index 0fb4134c..b1e68546 100644 --- a/rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc +++ b/rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc @@ -49,6 +49,7 @@ import String; import lang::rascal::\syntax::Rascal; import lang::rascalcore::check::Checker; +import lang::rascalcore::check::BasicRascalConfig; import lang::rascal::lsp::refactor::rename::Modules; @@ -487,6 +488,12 @@ private bool rascalContainsName(loc l, str name) { return false; } +private TModel getTModel(str modName, ModuleStatus ms) { + = getTModelForModule(modName, ms); + if (!found) throw unexpectedFailure("Cannot read TModel for module \'\'\n"); + return convertTModel2PhysicalLocs(tm); +} + private set[TModel] rascalTModels(set[loc] fs, PathConfig pcfg) { if (fs == {}) return {}; @@ -495,13 +502,16 @@ private set[TModel] rascalTModels(set[loc] fs, PathConfig pcfg) { list[str] topModuleNames = [getModuleName(mloc, pcfg) | mloc <- fs]; ms = rascalTModelForNames(topModuleNames, ccfg, dummy_compile1); - set[TModel] tmodels = {}; - for (str modName <- ms.moduleLocs) { - = getTModelForModule(modName, ms); - if (!found) throw unexpectedFailure("Cannot read TModel for module \'\'\n"); - tmodels += convertTModel2PhysicalLocs(tm); + map[str, TModel] tmodels = (); + modsToDo = toSet(topModuleNames); + while ({str modName, *rest} := modsToDo) { + modsToDo = rest; + tm = getTModel(modName, ms); + tmodels[modName] = tm; + depNames = domain(tm.store[key_bom]); + modsToDo += depNames - domain(tmodels); } - return tmodels; + return range(tmodels); } ProjectFiles preloadFiles(set[loc] workspaceFolders, loc cursorLoc) { diff --git a/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/Performance.rsc b/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/Performance.rsc index 0bffeb72..a9a394de 100644 --- a/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/Performance.rsc +++ b/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/Performance.rsc @@ -29,7 +29,12 @@ module lang::rascal::tests::rename::Performance import lang::rascal::tests::rename::TestUtils; import lang::rascal::lsp::refactor::Exception; +import lang::rascalcore::check::Checker; +import lang::rascalcore::check::RascalConfig; + +import IO; import List; +import util::Reflective; int LARGE_TEST_SIZE = 200; test bool largeTest() = testRenameOccurrences(({0} | it + {foos + 3, foos + 4, foos + 5} | i <- [0..LARGE_TEST_SIZE], foos := 5 * i), ( @@ -42,3 +47,21 @@ test bool largeTest() = testRenameOccurrences(({0} | it + {foos + 3, foos + 4, f @expected{unsupportedRename} test bool failOnError() = testRename("int foo = x + y;"); + +test bool incrementalTypeCheck() { + procLoc = |memory://tests/incremental|; + pcfg = getTestPathConfig(procLoc); + procSrc = pcfg.srcs[0]; + + modName = "A"; + moduleLoc = procSrc + ".rsc"; + writeFile(moduleLoc, "module + 'int foo() = 1; + 'void main() { x = foo(); } + "); + + ms = rascalTModelForNames([modName], rascalCompilerConfig(pcfg), dummy_compile1); + res = testRenameOccurrences({byLoc(modName, moduleLoc, {0, 1})}); + remove(procLoc); + return res; +} diff --git a/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/TestUtils.rsc b/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/TestUtils.rsc index a188df01..b4a05173 100644 --- a/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/TestUtils.rsc +++ b/rascal-lsp/src/main/rascal/lang/rascal/tests/rename/TestUtils.rsc @@ -56,7 +56,7 @@ import util::Reflective; //// Fixtures and utility functions data TestModule = byText(str name, str body, set[int] nameOccs, str newName = name, set[int] skipCursors = {}) - | byLoc(loc file, set[int] nameOccs, str newName = name, set[int] skipCursors = {}); + | byLoc(str name, loc file, set[int] nameOccs, str newName = name, set[int] skipCursors = {}); private list[DocumentEdit] sortEdits(list[DocumentEdit] edits) = [sortChanges(e) | e <- edits]; @@ -68,10 +68,20 @@ private default DocumentEdit sortChanges(DocumentEdit e) = e; private void verifyTypeCorrectRenaming(loc root, Edits edits, PathConfig pcfg) { list[loc] editLocs = [l | /replace(l, _) := edits<0>]; assert size(editLocs) == size(toSet(editLocs)) : "Duplicate locations in suggested edits - VS Code cannot handle this"; + + // Back-up sources + loc backupLoc = |memory://tests/backup|; + remove(backupLoc, recursive = true); + copy(root, backupLoc, recursive = true); + executeDocumentEdits(sortEdits(edits<0>)); remove(pcfg.resources); RascalCompilerConfig ccfg = rascalCompilerConfig(pcfg)[verbose = false][logPathConfig = false]; throwAnyErrors(checkAll(root, ccfg)); + + // Restore back-up + remove(root, recursive = true); + move(backupLoc, root, overwrite = true); } bool expectEq(&T expected, &T actual, str epilogue = "") { @@ -92,29 +102,31 @@ bool expectEq(&T expected, &T actual, str epilogue = "") { bool testRenameOccurrences(set[TestModule] modules, str oldName = "foo", str newName = "bar") { bool success = true; - for (mm <- modules, cursorOcc <- (mm.nameOccs - mm.skipCursors)) { - str testName = "Test__"; - loc testDir = |memory://tests/rename/|; - if(any(m <- modules, m is byLoc)) { - testDir = cover([m.file | m <- modules, m is byLoc]); + bool moduleExistsOnDisk = any(mmm <- modules, mmm is byLoc); + for (mm <- modules, cursorOcc <- (mm.nameOccs - mm.skipCursors)) { + loc testDir = |unknown:///|; + if (moduleExistsOnDisk){ + testDir = cover([m.file.parent | m <- modules, m is byLoc]).parent; } else { // If none of the modules refers to an existing file, clear the test directory before writing files. + str testName = "Test__"; + testDir = |memory://tests/rename/|; remove(testDir); } pcfg = getTestPathConfig(testDir); - modulesByLocation = {mByLoc | m <- modules, mByLoc := (m is byLoc ? m : byLoc(storeTestModule(testDir, m.name, m.body), m.nameOccs, newName = m.newName, skipCursors = m.skipCursors))}; + modulesByLocation = {mByLoc | m <- modules, mByLoc := (m is byLoc ? m : byLoc(m.name, storeTestModule(testDir, m.name, m.body), m.nameOccs, newName = m.newName, skipCursors = m.skipCursors))}; - for (byLoc(loc ml, _) <- modulesByLocation) { + for (m <- modulesByLocation) { try { - parseModuleWithSpaces(ml); + parseModuleWithSpaces(m.file); } catch _: { throw "Parse error in test module "; } } - cursorT = findCursor([m.file | m <- modulesByLocation, getModuleName(m.file, pcfg) == mm.name][0], oldName, cursorOcc); + cursorT = findCursor([m.file | m <- modulesByLocation, m.name == mm.name][0], oldName, cursorOcc); println("Renaming \'\' from "); edits = rascalRenameSymbol(cursorT, toSet(pcfg.srcs), newName, PathConfig(loc _) { return pcfg; }); @@ -149,11 +161,13 @@ bool testRenameOccurrences(set[TestModule] modules, str oldName = "foo", str new success = false; } - for (success, src <- pcfg.srcs) { - verifyTypeCorrectRenaming(src, edits, pcfg); + if (success) { + verifyTypeCorrectRenaming(testDir, edits, pcfg); } - remove(testDir); + if (!moduleExistsOnDisk) { + remove(testDir); + } } return success; @@ -186,7 +200,7 @@ bool testRename(str stmtsStr, int cursorAtOldNameOccurrence = 0, str oldName = " return false; } -private PathConfig getTestPathConfig(loc testDir) { +public PathConfig getTestPathConfig(loc testDir) { return pathConfig( bin=testDir + "bin", libs=[|lib://rascal|],