Skip to content

Method.overrides(_) returns false for lambda synthetic methods when the target functional interface is unresolved (buildMode: none) #22085

Description

@jesuistombe

Summary

When a CodeQL Java database is built with buildMode: none and a lambda expression targets a method from a third-party library not on the classpath, the lambda's synthetic public method is not modelled as overriding the functional interface's abstract method. m.overrides(_) returns false for it, making it an unexpected match for any query that uses not m.overrides(_) to exclude interface implementations.


Environment

  • CodeQL CLI: 2.25.6
  • Language: Java
  • Build mode: none

Minimal reproduction

Directory layout:

lambda-override-bug/
├── qlpack.yml
├── LambdaNotOverride.ql
├── src/
│   └── Example.java          ← source root for CodeQL
└── stub/
    └── com/example/
        └── ThirdParty.java   ← only needed for the control case

Note: The stub must be outside src/ so that buildMode:none does not extract it. If it were inside src/, CodeQL would resolve com.example.ThirdParty from source and the bug would not reproduce.

qlpack.yml

name: lambda-bug-repro
version: 0.0.1
dependencies:
  codeql/java-all: "*"

src/Example.java

class Example {
    // Lambda targeting an unresolved third-party type.
    void useThirdParty(com.example.ThirdParty obj) {
        obj.onSuccess(result -> System.out.println(result));
    }

    // Lambda targeting a JDK type (always resolved — control).
    void useJdk() {
        java.util.stream.Stream.of("x").filter(s -> !s.isEmpty());
    }
}

stub/com/example/ThirdParty.java

package com.example;
import java.util.function.Consumer;

public class ThirdParty {
    public void onSuccess(Consumer<String> consumer) {}
}

LambdaNotOverride.ql

import java

from Method m
where
  m.fromSource() and
  m.isPublic() and
  m.getDeclaringType() instanceof AnonymousClass and
  not m.overrides(_)
select m, "Lambda synthetic method not modelled as override: " + m.getDeclaringType()

Steps to reproduce

cd lambda-override-bug
codeql pack install

# Bug: build without the stub (com.example.ThirdParty is unresolved)
codeql database create db-none --language=java --build-mode=none --source-root=src --overwrite
codeql query run --database db-none -- LambdaNotOverride.ql
# → 1 result: the lambda in useThirdParty() is returned

# Control: compile with the stub so ThirdParty resolves correctly
javac stub/com/example/ThirdParty.java -d stub-classes
mkdir -p classes
codeql database create db-compiled \
  --language=java \
  --command="javac -cp $(pwd)/stub-classes $(pwd)/src/Example.java -d $(pwd)/classes" \
  --source-root=src --overwrite
codeql query run --database db-compiled -- LambdaNotOverride.ql
# → 0 results

Actual vs expected behaviour

Scenario useThirdParty lambda overrides(_) useJdk lambda overrides(_)
buildMode:none, ThirdParty unresolved false true
Compiled, ThirdParty resolved via stub true true

When the functional interface type cannot be resolved, CodeQL creates an anonymous class extending <TypeAccess of ErrorType>. With no known parent type, no override link is established and the synthetic public method leaks through guards like not m.overrides(_).


Impact

Any query that uses not m.overrides(_) to restrict results to non-overriding methods will produce false positives on lambda expressions in buildMode:none databases containing calls into third-party libraries.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions