JDK-8304533 : Add new lint flag "output-file-clash"
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 21
  • Submitted: 2023-03-20
  • Updated: 2023-03-27
  • Resolved: 2023-03-27
Related Reports
CSR :  
Description
Summary
-------

Add a new compiler lint flag `output-file-clash` that will enable detection of output file clashes. An output file clash is when the compiler writes two different output files, but due to the behavior of the filesystem these files end up being written to the same underlying filesystem file.

Problem
-------

Sometimes the compiler thinks it's writing out two separate files, but due to the way the underlying filesystem maps `Path`'s to actual files, it's really writing the same file twice.

This is usually due to case-insensitive filesystems, but can also be due to how a filesystem "normalizes" file names. For example, on MacOS, compiling this class will generate such a clash:

```
public class Test {
    interface Cafe\u0301 {
    }
    interface Caf\u00e9 {
    }
}
```

The reason is that `\u0301` is the Unicode character "Combining Acute Accent" which means "stick an accent over the previous character". So MacOS normalizes the letter `e` followed by a `\u0301` into a Unicode `\u00e9`, that is, `é`. However, the Java language treats these the two names `Cafe\u0301` and `Caf\u00e9` as distinct.

Of course, on any case-insensitive filesystem a class like this would cause an output file clash for the file `Test$Inner.class'`:
```
public class Test {
    class Inner {
    }
    class INNER {
    }
}
```

Solution
--------

There's no way to avoid this problem without changing the way the compiler maps output files to filesystem filenames, which would be too disruptive.

So the solution here is to simply provide a way for people experiencing this problem to convert what is currently a runtime error into a compile-time warning (or error with `-Werror`). This is by adding a new lint flag `output-file-clash` that enables output file clash detection. The term "output file" covers class files, source files, and native header files.

If the flag is not used, then the compiler should behave exactly as it currently does. Therefore, there should be no backward compatibility risk.

An alternative, a more aggressive approach would be to always enable this behavior and/or make it an error instead of a warning.

Specification
-------------

New addition to "javac --help-lint":
```
output-file-clash
    Warn when an output file is overwritten during compilation. This can occur, for example,
    on case-insensitive filesystems. Covers class files, native header files, and source files.
```
An example of the new warning:
```
$ javac -Xlint:output-file-clash Test.java
warning: [output-file-clash] output file written more than once: /Users/archie/test/Test$Café.class
1 warning
```

Comments
Release note created: https://bugs.openjdk.org/browse/JDK-8304995
27-03-2023

Moving to Approved contingent on a release note being written; thanks.
27-03-2023

If including this check as part of the lint facility causes more disruption than expected in practice, we can revisit moving the check to its own flag. Soliciting input from quality outreach may be in order once the feature is in a promoted build.
24-03-2023

OK you guys convinced me :) I'll update this CSR and the patch when I get a chance.
24-03-2023

[~acobbs] but I don't think this new code adds a lot of overhead if executed all the time, I wouldn't expect a huge penalty so that supports the `Xlint` case I think.
24-03-2023

Makes sense... but here's what still bothers me... There are lots of people who use "-Xlint:all" to maximize code integrity (I'm one of them). So whenever we add a new lint flag, we're effectively "mandating" it (in a somewhat soft sense) for all of those users. For example, the new "this-escape" flag is going to force a lot of these users to add new @SuppressWarnings annotation(s) to their code - or, change "-Xlint:all" to "-Xlint:all,-this-escape". If we include this functionality in "-Xlint:all" then we are effectively "mandating" its performance penalty on all of those users - including all of the ones using a filesystem which doesn't need it (e.g., most all Linux). That doesn't seem right/fair to me... instead, the users with the "offending" filesystems should be the ones paying the penalty. They should be the ones who have to go out of their way to adjust flags/settings, etc.
23-03-2023

My thinking was there are already idioms to manage warnings and errors around lint warnings ( ways to disable a particular warning, -Werror to cause a failure, etc.) and a few examples where javac issues warnings for not strictly source-related items, such as various settings of command-line options.
22-03-2023

> Was incorporating this check into the lint mechanism considered? No... frankly I didn't even think of doing that. I think of "lint" as normally applying to the source code rather than the compiler's O/S environment. But that may be an unnecessarily narrow view. So I'm open to the suggestion - and don't really have a strong opinion either way. I'm guessing you may have some reasons in favor of using lint? If so I'd be interested to hear them.
22-03-2023

Hmmm. Seems like a good facility to add to javac; not sure of the best way to expose and control it. Was incorporating this check into the lint mechanism considered? Moving to Provisional.
22-03-2023