Intercepting broken Reference Error


I would like to react when a reference is broken. I can see the error Message "brokenReference", but I would like to catch that "event" on some kind of lvl and react on it. For that I need of course the Node who holds the broken reference. The Node the reference points to is not of interrest for me.


Any ideas?






One possibility would be to create your own editor checker and register it to run periodically in the background. Look at the AutoResolver class in MPS. This will only work while the user is editing a node in an editor.

You could also attach listeners to the model that contains the nodes with the references, and all models that contain the reference targets and listen for reference changes and node deletions (since node deletion is the event that causes references to the node to break), but I imagine that managing all the listeners could become complicated.

What do you want to do in reaction to a reference becoming broken?


Thanks for the hints.

We have a node attribute that enables to point from a node in one AST (the one that contains the program) to a node in another AST. This node in turn maintains a reference to this node attribute. Thus, we have a bidirectional relationship that must be maintained during changes.

Let's assume the node attribute is somewhere in the subtree of an InstanceMethodDeclaration. When a side transform to a StaticMethodDeclaration is performed, the node attribute is copied. As a result the reference to the node attribute is broken and needs to be updated.

What we already did: We registered a SNodeChangeListener catching nodeRemoved events. We then searched for all node attributes and deleted them. To keep the necessary information, we hooked into the actions aspect. However, this didn't work in any situation (e.g. intentions). Moreover, copyAllAttributes in SNodeFactoryOperations was problematic in this scenario as well.

As the reference doesn't change when the attributed node is copied, this seems not to be an option.

We currently, try to leverage the SNodeAccessListener, but it's still not clear how to find out when the node attribute is copied.

The editor checker is also no option because, we need to maintain the structure also in case of side transforms, substitute actions and so on...





Ah, I forgot to mention there are actually two use cases:

1. The original node attribute gets replaced.

Currently, the new attributed node has the same smart references as the original node. However, the node that is referenced by the attributed node (outside the current AST in another one), needs to update its smart reference to the attributed node as well. The easiest way would be to do this when the new attributed node is added, but we don't know how. In this scenario, the reference to the node attribute is broken, but needs to be maintained.

2. The original node attribute gets removed.

As soon as the node attribute gets removed, the node which it refers to should be removed as well. In this scenario, the reference breaks as well, but the node referring to the node attribute should be removed as well.

The first scenario can be solved by a non-typechecking rule + quick fix:

checking rule NodeAttributeCheck
do {
if (myNodeAttribute.externalNode.nodeAttributeReference.isNull) {
error "nodeAttributeReference is null" -> myNodeAttribute;


if (node.isInstanceOf(MyNodeAttribute)) {
((node<MyNodeAttribute>) node).externalNode.nodeAttributeReference = ((node<MyNodeAttribute>) node);

This works, but seems not to be the optimal solution. Moreover, warnings of broken references are still shown and it may be complicated to distinguish whether a node has been deleted or substituted (i.e., which non-typechecking rule comes first: the one that detects that the MyNodeAttribute instance points to the external node and repairs the link or the one that detects that the nodeAttributeReference is broken and thus the node can be deleted.

We had no luck with SNodeAccessListener, because we couldn't manage to write the model and travers through the references without freezing MPS. We suspect that this issue arises as we read a reference from reading a reference...

Any help is greatly appreciated.




Here is what we did (it would be nice to know if there is a better solution). We registered a new SNodeChangeListener.

On node removal, we set references to null and remember the node for possible deletion:

if (node.@MyNodeAttribute.isNotEmpty) { 
node.@myNodeAttribute.externalNode.nodeAttributeReference = null;
foreach myNodeAttribute in node.descendants<concept = MyNodeAttribute> {
myNodeAttribute.externalNode.nodeAttributeReference = null;

On adding nodes, we just rewire the reference:

if (node.@MyNodeAttribute.isNotEmpty) { 
node.@myNodeAttribute.externalNode.nodeAttributeReference = node.@myNodeAttribute;
foreach myNodeAttribute in node.descendants<concept = MyNodeAttribute> {
myNodeAttribute.externalNode.nodeAttributeReference = myNodeAttribute;

Next, we just delete nodes that are left over in the buffer and have not been removed before. These are the candidates that have been really deleted and not just rewired.

This solution is of course pretty search intensive. It would have been much nicer to rewire as soon as the attribute is copied. Any ideas?


I don't have any more ideas unfortunately. Perhaps you could formulate this as a feature request and enter it into YouTrack.


Please sign in to leave a comment.