Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename: Fix incremental type check issues #565

Merged
merged 6 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -487,6 +488,12 @@ private bool rascalContainsName(loc l, str name) {
return false;
}

private TModel getTModel(str modName, ModuleStatus ms) {
<found, tm, ms> = getTModelForModule(modName, ms);
if (!found) throw unexpectedFailure("Cannot read TModel for module \'<modName>\'\n<toString(ms.messages)>");
return convertTModel2PhysicalLocs(tm);
}

private set[TModel] rascalTModels(set[loc] fs, PathConfig pcfg) {
if (fs == {}) return {};

Expand All @@ -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) {
<found, tm, ms> = getTModelForModule(modName, ms);
if (!found) throw unexpectedFailure("Cannot read TModel for module \'<modName>\'\n<toString(ms.messages)>");
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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), (
Expand All @@ -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 + "<modName>.rsc";
writeFile(moduleLoc, "module <modName>
'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;
}
42 changes: 28 additions & 14 deletions rascal-lsp/src/main/rascal/lang/rascal/tests/rename/TestUtils.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand All @@ -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 = "") {
Expand All @@ -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_<mm.name>_<cursorOcc>";
loc testDir = |memory://tests/rename/<testName>|;

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_<mm.name>_<cursorOcc>";
testDir = |memory://tests/rename/<testName>|;
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 <ml>";
}
}

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 \'<oldName>\' from <cursorT.src>");
edits = rascalRenameSymbol(cursorT, toSet(pcfg.srcs), newName, PathConfig(loc _) { return pcfg; });
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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|],
Expand Down
Loading