Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map.of with strings causes confusing type inference with FormatChecker #6641

Closed
frjonsen opened this issue Jun 1, 2024 · 0 comments · Fixed by #6672
Closed

Map.of with strings causes confusing type inference with FormatChecker #6641

frjonsen opened this issue Jun 1, 2024 · 0 comments · Fixed by #6672
Assignees

Comments

@frjonsen
Copy link

frjonsen commented Jun 1, 2024

I am using the gradle plugin, which now when updated to 0.6.39 now by default uses Checker Framework version 3.43.0, and we got some new errors with this. The only enabled checker is "org.checkerframework.checker.formatter.FormatterChecker". This is with Java 21, if that makes any kind of difference.

In particular, we have issues with the format checker, when using Map.of. When first declaring a new HashMap<String, String>(), the issue goes away.

In short, the following code produces an error:

static void foo(Map<String, String> map) {}

public static void main(String[] args) {
    var input = Map.of("key1", "value1");
    foo(input);
}

The error we get is:

error: [argument] incompatible argument for parameter map of Main.foo.
        foo(input);
            ^
  found   : Map<@Format({}) String, @Format({}) String>
  required: Map<String, String>

However, the following code builds and runs fine:

static void foo(Map<String, String> map) {}

public static void main(String[] args) {
    var input = new HashMap<String, String>();
    input.put("key1", "value1");
    foo(input);
}

When manually specifying the format, it also works, but of course won't be a solution when calling library code.

static void foo(Map<@Format({}) String, @Format({}) String> map) {}

public static void main(String[] args) {
    var input = Map.of("key1", "value1");
    foo(input);
}

Manually specifying the type of the map also works:

static void foo(Map<String, String> map) {}

public static void main(String[] args) {
    Map<String, String> input = Map.of("key1", "value1");
    foo(input);
}

None of these solutions truly resolve the issue when having less trivial cases, such as when the receiving function is a generic and the values of the map is mixed, for example:

static <T> void foo(Map<String, T> map) {}

public static void main(String[] args) {
    var input = Map.of(
        "key1", "value1",
        "key2", 1
    );
    foo(input);
}

produces the error

error: [argument] incompatible argument for parameter map of Main.foo.
        foo(input);
            ^
  found   : Map<@Format({}) String, Serializable & Comparable<? extends Serializable & Comparable<? extends Object> & Constable & ConstantDesc> & Constable & ConstantDesc>
  required: Map<String, Serializable & Comparable<? extends Serializable & Comparable<? extends Object> & Constable & ConstantDesc> & Constable & ConstantDesc>

which while technically possible to specify, it would be incredibly cumbersome.

Reverting to 3.42.0 also makes it go away, so I assume it's related to the type inference changes.

Is this intended behavior? We use Map.of extensively in our code base, and have a few cases where we call library code that takes either Map<String, T> or Map<String, ?>. We're currently leaning towards disabling the format checker instead, which would be unfortunate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants