enigma icon indicating copy to clipboard operation
enigma copied to clipboard

Navigating to method calls within lambdas doesn't work correctly

Open PiTheGuy opened this issue 1 year ago • 5 comments

When double-clicking a method call that's within a lambda in the show calls docker, it will open up the associated class, but won't move the caret or highlight the actual method call itself.

PiTheGuy avatar Aug 22 '24 19:08 PiTheGuy

Since lambda methods are synthetic and never referenced directly, the decompiler never visits them, and tokens are never created for them during indexing. When it then tries to look up the token so it knows where to navigate, it doesn't find any and thus doesn't navigate anywhere within the class.

PiTheGuy avatar Mar 09 '25 02:03 PiTheGuy

This also affects method calls within <clinit> methods.

supersaiyansubtlety avatar Sep 27 '25 02:09 supersaiyansubtlety

Consider this code snippet taken from SpawnPlacements:

    private static <T extends Mob> void register(EntityType<T> entityType, SpawnPlacementType spawnPlacementType, Types heightmapType, SpawnPlacements.SpawnPredicate<T> predicate) {
        SpawnPlacements.Data $$4 = DATA_BY_TYPE.put(entityType, new SpawnPlacements.Data(heightmapType, spawnPlacementType, predicate));
        if ($$4 != null) {
            throw new IllegalStateException("Duplicate registration for type " + BuiltInRegistries.ENTITY_TYPE.getKey(entityType));
        }
    }
	
	public static <T extends Entity> boolean checkSpawnRules(EntityType<T> entityType, ServerLevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) {
        SpawnPlacements.Data $$5 = DATA_BY_TYPE.get(entityType);
        return $$5 == null || $$5.predicate.test(entityType, level, spawnReason, pos, random);
    }
	
	static {
        register(EntityType.AXOLOTL, SpawnPlacementTypes.IN_WATER, Types.MOTION_BLOCKING_NO_LEAVES, Axolotl::checkAxolotlSpawnRules);
	}

When the class is parsed to map tokens to references, the code sees the call to $$5.predicate.test, which is a bridge method to Axolotl.checkAxolotlSpawnRules. It then creates a reference to Axolotl.checkAxolotlSpawnRules with context SpawnPlacements.checkSpawnRules since that's what ultimately calls it. But when Enigma goes to look up the tokens, it's looking for a reference with context SpawnPlacements.<clinit> because that's where the method is actually referenced.

PiTheGuy avatar Sep 27 '25 19:09 PiTheGuy

with a simple test case I made in engima, even a direct call like the call to register in <clinit> couldn't be navigated to

supersaiyansubtlety avatar Sep 27 '25 19:09 supersaiyansubtlety

From EnigmaTextTokenCollector:

	@Override
	public void visitMethod(TextRange range, boolean declaration, String className, String name, MethodDescriptor descriptor) {
		super.visitMethod(range, declaration, className, name, descriptor);
		Token token = this.getToken(range);
		MethodEntry entry = getMethodEntry(className, name, descriptor);

		if (declaration) {
			this.addDeclaration(token, entry);
			this.currentMethod = entry;
		} else {
			this.addReference(token, entry, this.currentMethod);
		}
	}

Lambdas and are synthetic and never declared so this.currentMethod is never set to them. When methods inside them are encountered in the source code, currentMethod was never set so it will remain its old value, meaning the context on the EntryReference is incorrect.

PiTheGuy avatar Sep 27 '25 19:09 PiTheGuy