Skip to content

Commit

Permalink
Merge pull request #487 from usethesource/fix/rename-refactoring/adt-…
Browse files Browse the repository at this point in the history
…fields-duplicate-names

Refine resolution of overloaded constructor & field declarations
  • Loading branch information
toinehartman authored Oct 29, 2024
2 parents cd94730 + fe7845a commit 86be386
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,13 @@ import String;
import lang::rascal::\syntax::Rascal;

import lang::rascalcore::check::Checker;
import lang::rascalcore::check::Import;
import lang::rascalcore::check::RascalConfig;

import analysis::typepal::TypePal;
import analysis::typepal::Collector;

extend lang::rascal::lsp::refactor::Exception;
import lang::rascal::lsp::refactor::Util;
import lang::rascal::lsp::refactor::WorkspaceInfo;

import analysis::diff::edits::TextEdits;

import vis::Text;

import util::FileSystem;
import util::Maybe;
import util::Monitor;
Expand Down Expand Up @@ -286,7 +279,7 @@ Maybe[AType] rascalConsFieldType(str fieldName, Define _:<_, _, _, constructorId
private CursorKind rascalGetDataFieldCursorKind(WorkspaceInfo ws, loc container, loc cursorLoc, str cursorName) {
for (Define dt <- rascalGetADTDefinitions(ws, container)
, adtType := dt.defInfo.atype) {
, AType adtType := dt.defInfo.atype) {
if (just(fieldType) := rascalAdtCommonKeywordFieldType(ws, cursorName, dt)) {
// Case 4 or 5 (or 0): common keyword field
return dataCommonKeywordField(dt.defined, fieldType);
Expand Down
110 changes: 77 additions & 33 deletions rascal-lsp/src/main/rascal/lang/rascal/lsp/refactor/WorkspaceInfo.rsc
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ set[Define] rascalReachableDefs(WorkspaceInfo ws, set[loc] defs) {
rel[loc from, loc to] modulePaths = rascalGetTransitiveReflexiveModulePaths(ws);
rel[loc from, loc to] scopes = rascalGetTransitiveReflexiveScopes(ws);
rel[loc from, Define define] reachableDefs =
(ws.defines<defined, defined, scope>)[defs] // <definition, scope> pairs
((ws.defines<defined, defined, scope>)[defs] // <definition, scope> pairs
+ (ws.defines<scope, defined, scope>)[defs])
o (
(scopes // All scopes surrounding defs
o modulePaths // Transitive-reflexive paths from scope to reachable modules
Expand All @@ -210,53 +211,96 @@ set[Define] rascalReachableDefs(WorkspaceInfo ws, set[loc] defs) {

set[loc] rascalGetOverloadedDefs(WorkspaceInfo ws, set[loc] defs, MayOverloadFun mayOverloadF) {
if (defs == {}) return {};
set[loc] overloadedDefs = defs;

set[Define] originalDefs = definitionsRel(ws)[defs];
set[IdRole] roles = originalDefs.idRole;
set[Define] overloadedDefs = definitionsRel(ws)[defs];
set[IdRole] roles = overloadedDefs.idRole;

// Pre-conditions
assert size(roles) == 1:
"Initial defs are of different roles!";
assert mayOverloadF(overloadedDefs, ws.definitions):
assert mayOverloadF(defs, ws.definitions):
"Initial defs are invalid overloads!";

IdRole role = getFirstFrom(roles);
map[loc file, loc scope] moduleScopePerFile = getModuleScopePerFile(ws);
rel[loc def, loc scope] defUseScopes = {<d, moduleScopePerFile[u.top]> | <loc u, loc d> <- ws.useDef};
rel[loc from, loc to] modulePaths = rascalGetTransitiveReflexiveModulePaths(ws);
rel[loc fromScope, loc toScope] modulePaths = rascalGetTransitiveReflexiveModulePaths(ws);
rel[loc def, loc scope] defScopes = ws.defines<defined, scope>+;
rel[loc scope, loc defined] scopeDefs =
(ws.defines<scope, defined>)+ // Follow definition scopes ...
o ((ws.defines<idRole, defined, defined>)[role]) // Until we arrive at a definition with the same role ...
;

rel[loc from, loc to] fromDefPaths =
(defScopes + defUseScopes) // 1. Look up scopes of defs and scopes of their uses
o modulePaths // 2. Follow import/extend relations to reachable scopes
o scopeDefs // 3. Find definitions in the reached scope, and definitions within those definitions (transitively)
;

rel[loc from, loc to] defPaths = {};
if (constructorId() := role) {
// We are just looking for constructors for the same ADT/nonterminal type
rel[loc, loc] selectedConstructors = (ws.defines<defInfo, defined, defined>)[originalDefs.defInfo];
defPaths = (defPaths o selectedConstructors)
+ (invert(defPaths) o selectedConstructors);
} else {
defPaths = fromDefPaths + invert(fromDefPaths);
}

rel[loc def, loc moduleScope] defPathStep =
(defScopes + defUseScopes) // 1. Look up scopes of defs and scopes of their uses
o (modulePaths + invert(modulePaths)) // 2. Follow import/extend relations to reachable scopes
;

rel[loc fromDef, loc toDef] defPaths = {};
set[loc] reachableDefs = rascalReachableDefs(ws, overloadedDefs.defined).defined;

solve(overloadedDefs) {
rel[loc from, loc to] reachableDefs = ident(overloadedDefs) o defPaths;
if (constructorId() := role) {
set[AType] adtTypes = {adtType | defType(acons(AType adtType, _, _)) <- overloadedDefs.defInfo};
set[loc] initialADTs = {
adtDef
| Define _: <_, _, _, dataId(), loc adtDef, defType(AType adtType)> <- rascalReachableDefs(ws, overloadedDefs.defined)
, adtType in adtTypes
};
set[loc] selectedADTs = rascalGetOverloadedDefs(ws, initialADTs, mayOverloadF);

// Any constructor definition of the right type where any `selectedADTs` element is in the reachable defs
rel[loc scope, loc def] selectedConstructors = {<s, d>
| <s, d, defType(acons(AType adtType, _, _))> <- (ws.defines<idRole, scope, defined, defInfo>)[role]
, adtType in adtTypes
, any(<_, _, _, dataId(), loc r, _> <- rascalReachableDefs(ws, {d}), r in selectedADTs)
};

// We transitively resolve module scopes via modules that have a relevant constructor/ADTs use or definition
rel[loc scope, loc def] selectedDefs = selectedConstructors + (ws.defines<defined, scope, defined>)[selectedADTs];
rel[loc fromScope, loc toScope] constructorStep = (selectedDefs + invert(defUseScopes)) o defPathStep;

defPathStep = defPathStep /* <def, scope> */
+ (defPathStep /* <def, scope> */ o constructorStep+ /* <scope, scope> */) /* <def, scope> */;

defPaths = defPathStep /* <def, scope> */ o selectedConstructors /* <scope, def> */;
} else if (dataId() := role) {
set[AType] adtTypes = {adtType | defType(AType adtType) <- overloadedDefs.defInfo};
set[loc] constructorDefs = {d
| <defType(acons(AType adtType, _, _)), d> <- (rascalReachableDefs(ws, overloadedDefs.defined)<idRole, defInfo, defined>)[constructorId()]
, adtType in adtTypes
, any(dd <- overloadedDefs.defined, isStrictlyContainedIn(d, dd))
};

set[Define] defsReachableFromOverloads = rascalReachableDefs(ws, overloadedDefs.defined + defUseScopes[overloadedDefs.defined]);
set[Define] defsReachableFromOverloadConstructors = rascalReachableDefs(ws, constructorDefs + defUseScopes[constructorDefs]);

rel[loc scope, loc def] selectedADTs = {
<s, d>
| <s, d, defType(AType adtType)> <- ((defsReachableFromOverloads + defsReachableFromOverloadConstructors)<idRole, scope, defined, defInfo>)[role]
, adtType in adtTypes
};

rel[loc fromScope, loc toScope] adtStep = (selectedADTs + invert(defUseScopes)) o defPathStep;
defPathStep = defPathStep
+ (defPathStep o adtStep+);
defPaths = defPathStep o selectedADTs;
} else if (fieldId() := role) {
// We are looking for fields for the same ADT type (but not necessarily same constructor type)
set[DefInfo] selectedADTTypes = (ws.defines<defined, defInfo>)[overloadedDefs.scope];
rel[loc, loc] selectedADTs = (ws.defines<defInfo, scope, defined>)[selectedADTTypes];
rel[loc, loc] selectedFields = selectedADTs o ws.defines<scope, defined>;
defPaths = defPathStep o selectedFields;
} else {
// Find definitions in the reached scope, and definitions within those definitions (transitively)
defPaths = defPathStep o invert(defScopes);
}

overloadedDefs += {d
| loc d <- reachableDefs<1>
, mayOverloadF(overloadedDefs + d, ws.definitions)
set[loc] overloadCandidates = defPaths[overloadedDefs.defined];
overloadedDefs += {ws.definitions[d]
| loc d <- overloadCandidates
, mayOverloadF(overloadedDefs.defined + d, ws.definitions)
};
reachableDefs = rascalReachableDefs(ws, overloadedDefs.defined).defined;
}

return overloadedDefs;
return overloadedDefs.defined;
}

private rel[loc, loc] NO_RENAMES(str _) = {};
Expand Down Expand Up @@ -452,9 +496,9 @@ DefsUsesRenames rascalGetDefsUses(WorkspaceInfo ws, cursor(typeParam(), cursorLo
});
}
bool definesTypeParam(Define _: <_, _, _, functionId(), _, defType(dT)>, AType paramType) =
bool definesTypeParam(Define _: <_, _, _, functionId(), _, defType(AType dT)>, AType paramType) =
afunc(_, /paramType, _) := dT;
bool definesTypeParam(Define _: <_, _, _, nonterminalId(), _, defType(dT)>, AType paramType) =
bool definesTypeParam(Define _: <_, _, _, nonterminalId(), _, defType(AType dT)>, AType paramType) =
aadt(_, /paramType, _) := dT;
default bool definesTypeParam(Define _, AType _) = false;
Expand Down
Loading

0 comments on commit 86be386

Please sign in to comment.