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

Improve rename refactoring name check #533

Merged
merged 3 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ data IllegalRenameReason
| definitionsOutsideWorkspace(set[loc] defs)
;

data RuntimeException
data RenameException
= illegalRename(str message, set[IllegalRenameReason] reason)
| unsupportedRename(str message, rel[loc location, str message] issues = {})
| unexpectedFailure(str message)
Expand Down
43 changes: 17 additions & 26 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Rename.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module lang::rascal::lsp::refactor::Rename

import Exception;
import IO;
import Grammar;
import List;
import Location;
import Map;
Expand Down Expand Up @@ -76,35 +77,23 @@ void throwAnyErrors(program(_, msgs)) {
throwAnyErrors(msgs);
}

set[IllegalRenameReason] rascalCheckLegalName(str name, set[IdRole] defRoles) {
set[IllegalRenameReason] tryParseAs(type[&T <: Tree] begin, str idDescription) {
try {
parse(begin, rascalEscapeName(name));
return {};
} catch ParseError(_): {
return {invalidName(name, idDescription)};
}
}
private set[IllegalRenameReason] rascalCheckLegalName(str name, set[IdRole] roles) {
escName = rascalEscapeName(name);
tuple[type[&T <: Tree] as, str desc] asType = <#Name, "identifier">;
if ({moduleId(), *_} := roles) asType = <#QualifiedName, "module name">;
if ({constructorId(), *_} := roles) asType = <#NonterminalLabel, "constructor name">;
if ({fieldId(), *_} := roles) asType = <#NonterminalLabel, "constructor field name">;
if (size(syntaxRoles & roles) > 0) asType = <#Nonterminal, "non-terminal name">;

bool isSyntaxRole = any(role <- defRoles, role in syntaxRoles);
bool isField = any(role <- defRoles, role is fieldId);
bool isConstructor = any(role <- defRoles, role is constructorId);
if (!tryParseAs(asType.as, escName)) return {invalidName(escName, asType.desc)};
return {};
}

set[IllegalRenameReason] reasons = {};
if (isSyntaxRole) {
reasons += tryParseAs(#Nonterminal, "non-terminal name");
}
if (isField) {
reasons += tryParseAs(#NonterminalLabel, "constructor field name");
private void rascalCheckLegalName(str name, Symbol sym) {
g = grammar(#start[Module]);
if (!tryParseAs(type(sym, g.rules), name)) {
throw illegalRename("\'<name>\' is not a valid name at this position", {invalidName(name, "<sym>")});
}
if (isConstructor) {
reasons += tryParseAs(#NonterminalLabel, "constructor name");
}
if (!(isSyntaxRole || isField || isConstructor)) {
reasons += tryParseAs(#Name, "identifier");
}

return reasons;
}

private set[IllegalRenameReason] rascalCheckDefinitionsOutsideWorkspace(WorkspaceInfo ws, set[loc] defs) =
Expand Down Expand Up @@ -531,6 +520,8 @@ Edits rascalRenameSymbol(Tree cursorT, set[loc] workspaceFolders, str newName, P
loc cursorLoc = cursorT.src;
str cursorName = "<cursorT>";

rascalCheckLegalName(newName, typeOf(cursorT));

step("collecting workspace information", 1);
WorkspaceInfo ws = workspaceInfo(
// Get path config
Expand Down
13 changes: 13 additions & 0 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/Util.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import IO;
import List;
import Location;
import Message;
import ParseTree;
import String;

import util::Maybe;
Expand Down Expand Up @@ -79,6 +80,18 @@ start[Module] parseModuleWithSpacesCached(loc l) {
return parseModuleWithSpacesCached(l, lastModified(l));
}

@synopsis{
Try to parse string `name` as reified type `begin` and return whether this succeeded.
}
bool tryParseAs(type[&T <: Tree] begin, str name, bool allowAmbiguity = false) {
try {
parse(begin, name, allowAmbiguity = allowAmbiguity);
return true;
} catch ParseError(_): {
return false;
}
}

Maybe[&B] flatMap(nothing(), Maybe[&B](&A) _) = nothing();
Maybe[&B] flatMap(just(&A a), Maybe[&B](&A) f) = f(a);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ test bool newNameHasNumericPrefix() = testRename("int foo = 8;", newName = "8abc

@expected{illegalRename}
test bool newNameIsEscapedInvalid() = testRename("int foo = 8;", newName = "\\8int");

@expected{illegalRename}
test bool qualifiedNameWhereNameExpected() = testRename("int foo = 8;", newName = "Foo::foo");
Loading