Skip to main content

module analysis::typepal::refactor::Rename

rascal-0.41.2
org.rascalmpl.typepal-0.15.5

Usage

import analysis::typepal::refactor::Rename;

Dependencies

import analysis::diff::edits::TextEdits;
import analysis::typepal::FailMessage;
import analysis::typepal::Messenger;
import analysis::typepal::TModel;
import util::Monitor;
import Exception;
import IO;
import List;
import Map;
import Message;
import Node;
import ParseTree;
import Relation;
import Set;

alias RenameResult

tuple[list[FileSystemChange], set[Message]]

alias Focus

list[Tree]

data Renamer

Tracks state of renaming and provides helper functions.

data Renamer  
= renamer(
void(FailMessage) msg
, void(FileSystemChange) documentEdit
, void(TextEdit) textEdit
, RenameConfig() getConfig
)
;

Tracks the state of the renaming, as an argument to every function of the rename framework.

  • msg registers a Fail Message. Registration of an FailMessage-error triggers premature termination of the renaming at the soonest possibility (typically before the next rename phase).
  • documentEdit registers a FileSystemChange, which represents a change required for the renaming.
  • textEdit registers a Text Edit, which represents a change required for the renaming. It is a convenience function that converts to a FileSystemChange internally, grouping Text Edits to the same file where possible.
  • getConfig retrieves the RenameConfig.

data RenameConfig

Language-specific configuration of identifier renaming.

data RenameConfig  
= rconfig(
Tree(loc) parseLoc
, TModel(Tree) tmodelForTree
, TModel(loc) tmodelForLoc = TModel(loc l) { return tmodelForTree(parseLoc(l)); }
, bool debug = false
, str jobLabel = "Renaming"
)
;

Configures the rename for a specific language, by giving the following mandatory arguments:

  • Tree parseLoc(loc l), which parses a file and returns a Tree. Typically this would be parse(#StartSymbol, l).
  • TModel tmodelForTree(Tree t), which type-checks a Tree and returns a TModel. Typically, this would be Collect And Solve. The configuration also takes some optional arguments:
  • TModel tmodelForLoc(loc l), which type-checks a file given its loc instead of its parse tree. This can be useful when the file does not have to be parsed to produce a TModel, for example when it had already been type-checked just before starting the renaming. Defaults to tmodelForTree(parseLoc(l)).
  • bool debug, which indicates whether debug output should be printed during the renaming. Defaults to false. *str jobLabel, which can be used to change the base label of the rename progress bar. Defaults to "Renaming".

function sortDocEdits

Sorts ((analysis::diff::edits::TextEdits::FileSystemChange))s.

list[FileSystemChange] sortDocEdits(list[FileSystemChange] edits)

Applying edits through ExecuteTextEdits should happen in a specific order. Specifically, files should be created before they can be modified, and after renaming them, modifications/deletions should refer to the new name. This functions sorts edits in the following order.

  1. created
  2. changed
  3. renamed
  4. removed

function rename

Renames the identifier under the cursor to newName and returns the required ((analysis::diff::edits::TextEdits::FileSystemChange))s and ((Message::Message))s.

RenameResult rename(
Focus cursor
, str newName
, RenameConfig config)

Renames the identifier under the cursor (represented as a Focus) to newName, given a specific RenameConfig. This renaming uses TModels produced by the type-checker.

The rename framework provides a default implementation, which can be selectively extended for languages that require different rename behaviour than the default. These functions constitute the implementation of renaming:

Pitfalls

  • Since the RenameConfig that this function passes to the various hooks contains state and caches, it should never escape the scope of a single invoation of rename. Instead, it should always be accessed via Renamer's getConfig.
  • The renaming highly depends on a type-check function TModel(Tree). If such a function does not exist, this framework cannot function.

function _rename

RenameResult _rename(
Focus cursor
, str newName
, RenameConfig config)

function defNameLocations

Compute locations of names of defs in tr.

map[Define, loc] defNameLocations(Tree tr, set[Define] defs, Renamer _r)

function getCursorDefinitions

Computes ((Define))(s) for the name under cursor.

default set[Define] getCursorDefinitions(Focus cursor, Tree(loc) _getTree, TModel(Tree) getModel, Renamer r)

function findOccurrenceFiles

Computes in which files occurrences of cursorDefs and newName might occur (over-approximation). This is not supposed to call the type-checker on any file for performance reasons.

default tuple[set[loc] defFiles, set[loc] useFiles, set[loc] newNameFiles] findOccurrenceFiles(set[Define] cursorDefs, Focus cursor, str newName, Tree(loc) _getTree, Renamer r)

Pitfalls

For any file in defFiles + useFiles, the framework calls RenameConfig::tmodelForLoc. If type-cehcking is expensive and this function over-approximates by a large margin, the performance of the renaming might degrade.

function findAdditionalDefinitions

Computes additional definitions (e.g. overloads of cursorDefs) in a single file.

default set[Define] findAdditionalDefinitions(set[Define] _cursorDefs, Tree _tr, TModel _tm, Renamer _r)

function validateNewNameOccurrences

Validates for a single file with occurrences of newName that, when renaming all occurrences of cursorDefs to newName, no problems will be introduced.

default void validateNewNameOccurrences(set[Define] cursorDefs, str newName, Tree tr, Renamer r)

Examples

This could be used to detect many kinds of problems, e.g. static errors and semantic changes due to shadowing or overloading.

function renameDefinition

Renames a single ((Define)) _d with its name at nameLoc, defined in ((TModel)) _tm, to newName, by producing corresponding ((FileSystemChange))s.

default void renameDefinition(Define _d, loc nameLoc, str newName, TModel _tm, Renamer r)

function renameUses

{Renames all uses of defs in a single file/((TModel)) tm, by producing corresponding ((FileSystemChange))s.}

default void renameUses(set[Define] defs, str newName, TModel tm, Renamer r)

function nameLocation

Finds the location of the identifier within definition ((ParseTree::Tree)) t corresponding to ((Define)) d, where t.src == d.defined.

default loc nameLocation(Tree t, Define d)