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

Bug 399914

Summary: Compiler reports unused private field which is read inside an anonymous inner class within its constructor
Product: [Eclipse Project] JDT Reporter: Andreas Haufler <aha>
Component: CoreAssignee: JDT-Core-Inbox <jdt-core-inbox>
Status: CLOSED WONTFIX QA Contact: Sasikanth Bharadwaj <sasikanth.bharadwaj>
Severity: normal    
Priority: P3 CC: jarthana, shankhba, srikanth_sankaran, stephan.herrmann
Version: 3.8.1   
Target Milestone: ---   
Hardware: Macintosh   
OS: Mac OS X   
Whiteboard: stalebug

Description Andreas Haufler CLA 2013-02-04 12:54:19 EST
The following code gets a warning in line 2 (The value of the field Test.T is not used - it is read):

public class Test {
	private static final Test T = new Test(new Runnable() {

		@Override
		public void run() {
			System.out.println(T);
		}
	});

	private static Runnable r;

	public Test(Runnable x) {
		r = x;
	}

	public static void main(String[] args) {
		new Thread(r).start();
	}
}

javac -Xlint Test.java reports no errors or warnings at all.

This example can be further reduced to:

public class Test {
	private static final Test T = new Test(new Runnable() {

		@Override
		public void run() {
			System.out.println(T);
		}
	});

	public Test(Runnable r) {
		r.run();
	}

	public static void main(String[] args) {
	}
}

(In this case, T is always read before its initialization, but nevertheless it is not unused).
Comment 1 Stephan Herrmann CLA 2013-02-04 18:21:13 EST
we fixed s.t. similar a while ago, I'll dig out that bug when I find the time.
Comment 2 Srikanth Sankaran CLA 2013-03-12 20:19:19 EDT
Shankha, please take a look. See why we think T is unused while it actually
is. (Look for the references to IProblem.UnusedPrivateField and understand the
surrounding code as a first step)
Comment 3 shankha banerjee CLA 2013-04-08 09:19:42 EDT
Update:

Following piece of code seems to be responsible for the failure to recognize that T is used. 

public final boolean isDefinedInField(FieldBinding field) {
		Scope scope = this;
		do {
			if (scope instanceof MethodScope) {
				MethodScope methodScope = (MethodScope) scope;
				if (methodScope.initializedField == field) return true;
			}
			scope = scope.parent;
		} while (scope != null);
		return false;
	}

The method is called through isFieldUseDeprecated. 
This function marks if there is a use of the variable:
				
407: field.original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed;

Call stack:
Daemon Thread [Compiler Processing Task] (Suspended)	
	MethodScope(Scope).isDefinedInField(FieldBinding) line: 3084	
	SingleNameReference(ASTNode).isFieldUseDeprecated(FieldBinding, Scope, int) line: 401	
	SingleNameReference.checkFieldAccess(BlockScope) line: 223	
	SingleNameReference.resolveType(BlockScope) line: 968	
	MessageSend.resolveType(BlockScope) line: 605	
	MessageSend(Expression).resolve(BlockScope) line: 970	
	MethodDeclaration(AbstractMethodDeclaration).resolveStatements() line: 510	
	MethodDeclaration.resolveStatements() line: 265	
	MethodDeclaration(AbstractMethodDeclaration).resolve(ClassScope) line: 469	
	TypeDeclaration.resolve() line: 1184	
	TypeDeclaration.resolve(BlockScope) line: 1269	
	QualifiedAllocationExpression.resolveType(BlockScope) line: 503	
	AllocationExpression.resolveType(BlockScope) line: 358	
	FieldDeclaration.resolve(MethodScope) line: 237	
	TypeDeclaration.resolve() line: 1120	
	TypeDeclaration.resolve(CompilationUnitScope) line: 1294	
	CompilationUnitDeclaration.resolve() line: 561	
	Compiler.process(CompilationUnitDeclaration, int) line: 770	
	ProcessTaskManager.run() line: 137	
	Thread.run() line: 722
Comment 4 Stephan Herrmann CLA 2013-04-08 20:29:29 EDT
(In reply to comment #3)
> Update:
> 
> Following piece of code seems to be responsible for the failure to recognize
> that T is used. 
> 
> public final boolean isDefinedInField(FieldBinding field) {
> 		Scope scope = this;
> 		do {
> 			if (scope instanceof MethodScope) {
> 				MethodScope methodScope = (MethodScope) scope;
> 				if (methodScope.initializedField == field) return true;
> 			}
> 			scope = scope.parent;
> 		} while (scope != null);
> 		return false;
> 	}
> 
> The method is called through isFieldUseDeprecated. 
> This function marks if there is a use of the variable:
> 				
> 407: field.original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed;

Seeing the comment
//  ignore cases where field is used from inside itself
gives a strong hint that the behaviour is by design.

BTW, it's always interesting to see, when a questionable piece of code
was added, this particular check was added via commit
343b3de832b5278eaddde8a3101cc17190191f85, dated 03/03/03 :)
Unfortunately, Philipe gave no comment on it, other than the inline
code comments...

The compiler assumes, that accessing T inside its own declaration doesn't
yield any execution paths into relevantly reading T. Sounds reasonable?

The example shows the point we're missing in this analysis: the side-effect
of Test's constructor makes the inner instance accessible via an alias.

We'd be hero, if we could detect this alias (via r), but we can't and will
not be able.

My gut feeling is: if we change anything here, we'll no longer be able
to detect the following truly unused field:

  Runnable r = new Runnable() { public void run() { System.out.println(r); };

So what's better? Never reporting? Reporting one too many warnings?

Here's a dogma:
"Any warranty is void if constructors perform non-local side-effects!"
"Neither humans nor compilers are very good in understanding such code"

While this leads into the realm of discussing code styles, I'm leaning
towards leaving everything as it is and simply asking the user to insert
@SuppressWarnings("unused"), if he is keen on keeping this questionable
design :)

Or does anybody have an idea how we could reliably distinguish the examples
from comment 0 and my simplified version here?
Comment 5 Eclipse Genie CLA 2020-04-08 10:01:43 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.