Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 530366

Summary: Changing class name in source leads to missing inner class compilation artefact
Product: [Eclipse Project] JDT Reporter: Simeon Andreev <simeon.danailov.andreev>
Component: CoreAssignee: Simeon Andreev <simeon.danailov.andreev>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: loskutov, stephan.herrmann
Version: 3.8.2   
Target Milestone: 4.8 M6   
Hardware: PC   
OS: Linux   
See Also: https://git.eclipse.org/r/116552
https://git.eclipse.org/c/jdt/eclipse.git/commit/?id=05b6ddd634f88cfd820ceaa84cbc7bf143602be6
Whiteboard:
Bug Depends on: 530634    
Bug Blocks:    
Attachments:
Description Flags
Example project to reproduce the problem with. none

Description Simeon Andreev CLA 2018-01-26 06:58:05 EST
Created attachment 272414 [details]
Example project to reproduce the problem with.

Steps to reproduce:

1. Import project from attached archive.
2. Open MyClass2.java.
3. In MyClass2.java, change class name to MyClass1.
4. Observe expected error, that MyClass1 in MyClass2.java should be defined in its own class.
5. In MyClass2.java, change class name back to MyClass2.
6. Observe that bin/somepackage/MyClass1$InnerClass.class is gone.

Or, from scratch:

1. Create Java project BugExample.
2. Create a package somepackage.
3. Create a class somepackage.MyClass1.
4. Define package private, non-static inner class MyClass1$InnerClass.
5. Copy MyClass1.java to MyClass2.java.
6. Open MyClass2.java.
7. In MyClass2.java, change class name to MyClass1.
8. Observe expected error, that MyClass1 in MyClass2.java should be defined in its own class.
9. In MyClass2.java, change class name back to MyClass2.
10. Observe that bin/somepackage/MyClass1$InnerClass.class is gone.

A clean build or changing MyClass1.java results in restoring the missing artefact for MyClass1$InnerClass.class.


Result here is that any program which needed MyClass1$InnerClass will run into a run-time error (due to the missing .class), although there is no actual compile error and the workspace is supposedly compiled.


Reproduced with:

Eclipse SDK
Version: Photon (4.8)
Build id: I20171217-2000

Originally found on Eclipse 3.8.2.
Comment 1 Simeon Andreev CLA 2018-01-29 09:50:30 EST
Apparently the build thinks the renamed class is a secondary type, so it removes its .class files:

Thread [Worker-14] (Suspended (breakpoint at line 724 in Resource))	
	File(Resource).delete(int, IProgressMonitor) line: 724	
	IncrementalImageBuilder.removeClassFile(IPath, IContainer) line: 800	
	IncrementalImageBuilder.removeSecondaryTypes() line: 813	
	IncrementalImageBuilder.incrementalBuildLoop() line: 184	
	IncrementalImageBuilder.build(SimpleLookupTable) line: 140	
	JavaBuilder.buildDeltas(SimpleLookupTable) line: 276	
	JavaBuilder.build(int, Map, IProgressMonitor) line: 197	
	BuildManager$2.run() line: 740	
	SafeRunner.run(ISafeRunnable) line: 42	
	BuildManager.basicBuild(int, IncrementalProjectBuilder, Map<String,String>, MultiStatus, IProgressMonitor) line: 210	
	BuildManager.basicBuild(IBuildConfiguration, int, IBuildContext, ICommand[], MultiStatus, IProgressMonitor) line: 253	
	BuildManager$1.run() line: 306	
	SafeRunner.run(ISafeRunnable) line: 42	
	BuildManager.basicBuild(IBuildConfiguration, int, IBuildContext, MultiStatus, IProgressMonitor) line: 309	
	BuildManager.basicBuildLoop(IBuildConfiguration[], IBuildConfiguration[], int, MultiStatus, IProgressMonitor) line: 361	
	BuildManager.build(IBuildConfiguration[], IBuildConfiguration[], int, IProgressMonitor) line: 382	
	AutoBuildJob.doBuild(IProgressMonitor) line: 142	
	AutoBuildJob.run(IProgressMonitor) line: 232	
	Worker.run() line: 56	

Removed files in org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder.removeSecondaryTypes() are:

[a/B, a/B$Inner]
Comment 2 Simeon Andreev CLA 2018-01-29 10:12:49 EST
> 5. In MyClass2.java, change class name back to MyClass2.

I.e. on this step, the builder will remove "secondary types", in this case MyClass1$InnerClass from MyClass2.java.

It doesn't rebuild MyClass1.java after this though.
Comment 3 Simeon Andreev CLA 2018-02-01 07:19:41 EST
The following code looks like it should filter out the generation of the inner class:

Thread [Worker-12] (Suspended)	
	State.isDuplicateLocator(String, String) line: 142	
	IncrementalImageBuilder(AbstractImageBuilder).acceptResult(CompilationResult) line: 167	
	Compiler.processCompiledUnits(int, boolean) line: 615	
	Compiler.compile(ICompilationUnit[], boolean) line: 472	
	Compiler.compile(ICompilationUnit[]) line: 423	
	IncrementalImageBuilder(AbstractImageBuilder).compile(SourceFile[], SourceFile[], boolean) line: 381	
	IncrementalImageBuilder.compile(SourceFile[], SourceFile[], boolean) line: 368	
	IncrementalImageBuilder(AbstractImageBuilder).compile(SourceFile[]) line: 313	
	IncrementalImageBuilder.incrementalBuildLoop() line: 183	
	IncrementalImageBuilder.build(SimpleLookupTable) line: 140	
	JavaBuilder.buildDeltas(SimpleLookupTable) line: 276	
	JavaBuilder.build(int, Map, IProgressMonitor) line: 197	
	BuildManager$2.run() line: 740	
	SafeRunner.run(ISafeRunnable) line: 42	
	BuildManager.basicBuild(int, IncrementalProjectBuilder, Map<String,String>, MultiStatus, IProgressMonitor) line: 210	
	BuildManager.basicBuild(IBuildConfiguration, int, IBuildContext, ICommand[], MultiStatus, IProgressMonitor) line: 253	
	BuildManager$1.run() line: 306	
	SafeRunner.run(ISafeRunnable) line: 42	
	BuildManager.basicBuild(IBuildConfiguration, int, IBuildContext, MultiStatus, IProgressMonitor) line: 309	
	BuildManager.basicBuildLoop(IBuildConfiguration[], IBuildConfiguration[], int, MultiStatus, IProgressMonitor) line: 361	
	BuildManager.build(IBuildConfiguration[], IBuildConfiguration[], int, IProgressMonitor) line: 382	
	AutoBuildJob.doBuild(IProgressMonitor) line: 142	
	AutoBuildJob.run(IProgressMonitor) line: 232	
	Worker.run() line: 56	

However it does not, since no duplicate is detected:

public boolean isDuplicateLocator(String qualifiedTypeName, String typeLocator) {
	String existing = (String) this.typeLocators.get(qualifiedTypeName);
	return existing != null && !existing.equals(typeLocator);
}

When checking with the inner class SomeClass$InnerClass in SomeOtherclass.java, typeLocators contains:

somepackage/SomeClass -> src/somepackage/SomeClass.java
somepackage/SomeOtherClass -> src/somepackage/SomeOtherClass.java

The following piece of code doesn't make much sense as far as I can tell:

// Look for a possible collision, if one exists, report an error but do not write the class file
if (isNestedType) {
	String qualifiedTypeName = new String(classFile.outerMostEnclosingClassFile().fileName());
	if (this.newState.isDuplicateLocator(qualifiedTypeName, typeLocator))
		continue;
} else {
	...
	this.newState.recordLocatorForType(qualifiedTypeName, typeLocator);
	if (result.checkSecondaryTypes && !qualifiedTypeName.equals(compilationUnit.initialTypeName))
		acceptSecondaryType(classFile);
}

There are no other entry points to State.typeLocators, aside from reading the build state from the disk.

So this code block will check for duplicates for nested types, but will not record nested types with which to check... This is as old as 2002, commit: https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=1d631cdc6d8aa97d297a027f3883f9c7312c678d
Comment 4 Simeon Andreev CLA 2018-02-01 07:20:45 EST
So to sum up, there are two problems that I can see:

1. SomeClass$InnerClass.class from SomeOtherClass.java overwrites SomeClass$InnerClass.class from SomeClass.java, when the name of the type in SomeOtherClass.java is changed from SomeOtherClass to SomeClass. (comment #3)
2. SomeClass$InnerClass.class is removed, when renaming type SomeClass to SomeOtherClass in SomeOtherClass.java. (comments #1 and #2)

Andrey please judge if its worth to try to fix those 2 points. The problem reported in our product, and the more general problem reported in this bug, can both be avoided by using refactor rename and copy-paste in Eclipse.
Comment 5 Eclipse Genie CLA 2018-02-01 11:55:54 EST
New Gerrit change created: https://git.eclipse.org/r/116552
Comment 6 Simeon Andreev CLA 2018-02-01 11:56:42 EST
I believe I found an OK solution, submitted review https://git.eclipse.org/r/#/c/116552/.
Comment 7 Stephan Herrmann CLA 2018-02-01 15:24:20 EST
(In reply to Simeon Andreev from comment #6)
> I believe I found an OK solution, submitted review
> https://git.eclipse.org/r/#/c/116552/.

FYI, your jenkins job seems to be affected / blocked by bug 530634.
Comment 8 Simeon Andreev CLA 2018-02-02 03:44:06 EST
(In reply to Stephan Herrmann from comment #7)
> (In reply to Simeon Andreev from comment #6)
> > I believe I found an OK solution, submitted review
> > https://git.eclipse.org/r/#/c/116552/.
> 
> FYI, your jenkins job seems to be affected / blocked by bug 530634.

Thanks for the info!
Comment 10 Andrey Loskutov CLA 2018-02-12 05:18:33 EST
Thanks Simeon!
Comment 11 Manoj N Palat CLA 2018-03-08 01:21:12 EST
Verified for Eclipse Photon (4.8) M6 with Build id: I20180306-0800