Skip to content

java/non-final-call-in-constructor: a call made inside a lambda in the constructor is not reported #22970

Open

Description

@MarkLee131

Description of the issue

The query reports toString() in the constructor of CtorSeed.Senior: Junior overrides it, the override reads name, and name is only assigned after super() returns, so new Junior() throws. Move the call into a lambda that the constructor runs right away (CtorVariant) and nothing is reported, though the call still happens during construction with the same effect.

Probably unqualifiedCallToNonAbstractMethod, which requires the call's enclosing callable to be the constructor (result.getEnclosingCallable() = c). Inside the lambda the enclosing callable is the lambda's own method.

Query: java/non-final-call-in-constructor (codeql/java-queries 1.1.10)

CodeQL CLI: 2.27.2

Code samples

seed/CtorSeed.java:

public class CtorSeed {

    public static class Senior {
        public Senior() {
            toString();                        // overridden in Junior, reads a field not yet set
        }
        public String toString() {
            return "Senior";
        }
    }

    public static class Junior extends Senior {
        private String name;
        public Junior() {
            super();
            name = "Junior";
        }
        public String toString() {
            return name.toUpperCase();         // NullPointerException when called from Senior()
        }
    }
}

variant/CtorVariant.java:

public class CtorVariant {

    public static class Senior {
        public Senior() {
            Runnable r = () -> {
                toString();                    // the same call, inside a lambda run at once; not reported
            };
            r.run();
        }
        public String toString() {
            return "Senior";
        }
    }

    public static class Junior extends Senior {
        private String name;
        public Junior() {
            super();
            name = "Junior";
        }
        public String toString() {
            return name.toUpperCase();
        }
    }
}

How to reproduce

codeql database create db --language=java --build-mode=none \
    --source-root=<one directory> --overwrite
codeql database analyze db \
    codeql/java-queries:codeql-suites/java-security-and-quality.qls \
    --format=csv --output=out.csv
# one database per directory

Expected result

"Non-final method invocation in constructor" on the toString() call inside the lambda in CtorVariant.java (line 6), as on CtorSeed.java (line 5).

Output of the commands above:

### CodeQL 2.27.2  query: java/non-final-call-in-constructor
### suite: java-security-and-quality.qls, --build-mode=none, per-file isolated databases
### extraction JDK: openjdk version "21.0.11" 2026-04-21 LTS

--- SEED -- expected: query fires (seed/)
"Non-final method invocation in constructor","If a constructor calls a method that is overridden in a subclass

--- VARIANT -- expected: query fires, actual: not reported (the bug) (variant/)
    (query reported nothing)

Activity

  1. sam-robson commented on Nov 3, 2026

    @sam-robson
    Contributor

    Hi @MarkLee131, thanks for the detailed report. We'll take a look into this and follow up.

  2. sam-robson commented on Nov 4, 2026

    @sam-robson
    Contributor

    The java/non-final-call-in-constructor query is a syntactic check for direct calls made inside the constructor body itself, rather than tracking whether lambdas or callbacks created in a constructor are invoked synchronously before construction finishes (since callbacks defined in a constructor are typically invoked later, after initialization).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions