Bug 236306 - [content assist] for method invocation in variable initializer should not guess variable
Summary: [content assist] for method invocation in variable initializer should not gue...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows Vista
: P3 normal (vote)
Target Milestone: 3.6 M7   Edit
Assignee: Ayushman Jain CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 254756 (view as bug list)
Depends on:
Blocks:
 
Reported: 2008-06-09 15:58 EDT by Hajo Czub CLA
Modified: 2010-04-26 05:42 EDT (History)
6 users (show)

See Also:
frederic_fusier: review+


Attachments
proposed fix v1.0 + regression tests (27.21 KB, patch)
2010-03-22 11:22 EDT, Ayushman Jain CLA
no flags Details | Diff
proposed fix v2.0 + regression tests (28.47 KB, patch)
2010-03-26 12:44 EDT, Ayushman Jain CLA
no flags Details | Diff
proposed fix v2.0 + regression tests (28.46 KB, patch)
2010-03-26 13:01 EDT, Ayushman Jain CLA
frederic_fusier: iplog+
frederic_fusier: review+
Details | Diff
Modified patch as Frédéric suggested (28.42 KB, patch)
2010-04-16 09:26 EDT, Olivier Thomann CLA
Olivier_Thomann: iplog+
Olivier_Thomann: review+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Hajo Czub CLA 2008-06-09 15:58:48 EDT
Build ID: I20080523-0100

Steps To Reproduce:
1. type "String newText = String.format"
2. press 'Ctrl+Space' to get the default proposals
3. select the first proposal
-> result is:
   "String newText = String.format(newText, null)"
but
   'newText' should not be a proposal. It is not initialized yet!

More information:
Comment 1 David Audel CLA 2008-06-10 09:37:17 EDT
I can reproduce it if the preference 'Java>Editor>Content Assist>Fill method arguments and show guessed arguments>Insert best guessed arguments' is enabled.

In 3.3 'newText' is not proposed.
In 3.3 JDT/Text use ICodeAssist#codeComplete() before the local variable declaration to guess arguments. So 'newText' is not proposed.
In 3.4 JDT/Text use the new API CompletionContext#getVisibleElement() and 'newText' is visible from the completion location. So 'newText' is proposed

To avoid that JDT/Text propose 'newText' as a possible argument, i see two possibilities:
1) CompletionContext#getVisibleElement() would not return an enclosing local variable as a visible element.
2) JDT/Text would filter the visible element 'newText'. To do that JDT/Text need to know that the completion occurs in a local variable initialization. That's currently not possible but CompletionContext#getEnclosingElement() could return an enclosing ILocalVariable instead of the enclosing IMethod.

I think that 2) is the best way to solve the problem because 'newText' is really visible from the completion location.

Daniel - What do you think about this bug ?
Comment 2 Dani Megert CLA 2008-06-10 09:46:52 EDT
>Daniel - What do you think about this bug ?
Agree to go with 2) but this a 3.5 item.
Comment 3 Jerome Lanneluc CLA 2009-04-28 11:27:43 EDT
Not for 3.5
Comment 4 David Audel CLA 2009-06-29 11:22:35 EDT
*** Bug 254756 has been marked as a duplicate of this bug. ***
Comment 5 Ayushman Jain CLA 2010-03-22 11:22:22 EDT
Created attachment 162679 [details]
proposed fix v1.0 + regression tests

InternalExtendedCompletionContext.getEnclosingElement() relies upon CompilationUnit.getElementAt(int), which uses JavaElement.getSourceElementAt(int). This method only works on compilation units and types and narrows down to an enclosing element not beyond a method. The methods are know there because SourceTypeElementInfo$children contains the methods and fields defined in a type. On the other hand, to know what all local variables are present in the method, we will have to collect all local variables just like we already do for InternalExtendedCompletionContext.getVisibleElements(String) via InternalExtendedCompletionContext.searchVisibleVariablesAndMethods(..), and then check each local variable's declaration for the completion node. This duplication of code is useless.

We can better do the filtering directly in InternalExtendedCompletionContext.searchVisibleVariablesAndMethods(..). As far as the visibility issue is concerned, since its a private method and not used anywhere, its safe to say that we assume those local variables as not visible that are being initialised and where a proposal has been requested for during the initialisation.
Comment 6 Frederic Fusier CLA 2010-03-24 11:46:32 EDT
I talked about your patch with David and he confirmed my initial feeling that the patch is not good enough, mainly for two reasons.

First, only check the type of certain expressions (AllocationExpression, MessageSend, ConditionalExpression) and also only for certain type on completion nodes expose your fix to a hole in case the AST nodes structure will be different from what you currently expected. After having made some tests, we found a simple test case for which the proposed fix does not work:
	String x2 = (x3 = String.format
When completing after the format, x2 is inserted when selecting the first proposal...

Second, doing the verification to remove this kind of local variables might be costly, especially if it's done for all local variables of all blocks in the stack...

Hence, the patch needs to be deeply reworked. To address those two issues, our advice is first to do the verification only if the position of the completion is inside the declaration positions (i.e. between the declarationSourceStart and the declarationSourceEnd) if they are well set. Second, in case the declaration positions are not set (i.e. declarationSourceEnd <= 0), typically when the recovery wasn't able to close it, then visit the entire AST nodes tree from the initialization to search whether a completion node belongs to it or not. Then, we'll be sure to accept any kind of AST nodes structure.

Note that the ASTVisitor might not be necessary if you can prove that the declarationSourceEnd will ever be set (David does not precisely remember whether this assumption is true or not)...
Comment 7 Ayushman Jain CLA 2010-03-26 12:44:09 EDT
Created attachment 163101 [details]
proposed fix v2.0 + regression tests

Thanks Frederic.
I agree that the strategy you mentioned above sounds more reasonable. The new patch captures this. The ASTVisitor was indeed required (called here via CompletionNodeDetector) since declarationSourceEnd was not set in all cases.
Comment 8 Ayushman Jain CLA 2010-03-26 13:01:54 EDT
Created attachment 163103 [details]
proposed fix v2.0 + regression tests

same patch with small formatting change
Comment 9 Frederic Fusier CLA 2010-03-29 06:25:57 EDT
(In reply to comment #8)
> Created an attachment (id=163103) [details]
> proposed fix v2.0 + regression tests
> 
> same patch with small formatting change

The patch looks good to me.

Just an aesthetic point, personally I would prefer the conditions written as
follow:
if (local.declaration.initialization != null) {
    if (local.declaration.initialization.sourceEnd > 0) {
        if (this.assistNode.sourceEnd <= local.decl...initial...sourceEnd &&
            this.assistNode.sourceStart >= local.decl...initial...sourceStart){
            continue next;
        }
    } else {
        CompletionNodeDetector detector = new CompletionNodeDetector(
            this.assistNode,
            local.declaration.initialization);
        if (detector.containsCompletionNode()) {
            continue next;
        }
    }
}

IMO, it would better show that in case the source end is not set, then the AST
Visitor was used to see whether the completion node is inside the
initialization or not...

However, I set the review as +, because it's only a suggestion not a mandatory
change. Anyone may have different feeling reading this code...
Comment 10 Ayushman Jain CLA 2010-04-16 03:18:26 EDT
Olivier, can you please release this patch? Thanks!
Comment 11 Olivier Thomann CLA 2010-04-16 09:26:19 EDT
Created attachment 165092 [details]
Modified patch as Frédéric suggested
Comment 12 Olivier Thomann CLA 2010-04-16 09:27:57 EDT
Released for 3.6M7.
Added regression test in:
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0167
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0168
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0169
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0170
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0171
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0172
org.eclipse.jdt.core.tests.model.CompletionContextTests#test0173
Comment 13 Srikanth Sankaran CLA 2010-04-26 05:42:27 EDT
I could verify that the test case attached to this bug
works ok: However I found the following similar problems
are still there:

(1) public class X {
	public static void main(String args[]) {
		int xyk = 10;
		int xyz = xy|;
	}
}

Completing at the | symbol proposes xyz, which would immediately
lead to an error (uninitialized)

(2) public class X {
	int xyk = 0;
	int xyz = xy|;
	int xyp = 1;
}

Completing at the | symbol proposes xyz and xyp, which would immediately
lead to an error (field referenced b4 definition)

(3) String newText = String.format(newText, null) proposal
still shows up if newText declaration were to happen as
a field and not as local.

I'll leave the current bug as RESOLVED, but have raised a follow
up defect:  bug# 310427.

Verified for 3.6M7 using build I20100424-