Bug 425833 - IllegalArgumentException I is not a member of Cls1
Summary: IllegalArgumentException I is not a member of Cls1
Status: RESOLVED FIXED
Alias: None
Product: CDT
Classification: Tools
Component: cdt-indexer (show other bugs)
Version: Next   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 8.3.0   Edit
Assignee: Sergey Prigogin CLA
QA Contact: Markus Schorn CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-01-15 17:00 EST by Andrew Eidsness CLA
Modified: 2014-01-16 14:19 EST (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Andrew Eidsness CLA 2014-01-15 17:00:43 EST
I've reduced a problem from a large codebase to the following two files:

cls1.h:
    struct Cls1 { typedef int I; };

src.cpp:
    #include "cls1.h"
    void f1() { Cls1 c; c.>|< } // ref A

    struct Cls2 { typedef int I; };
    void f2() { Cls2 c; c.>|< } // ref B

In a simple C++ project ('New - C/C++ - C++ Project - Makefile project - Empty Project'), Content Assist will work properly at the places marked with >|< in both A and B.

However, if the project references any other project ('Project References' in the project properties dialog) then ref A will no longer work.

I can reproduce this with an empty General project ('New - General - Project').  If that project is in the reference list, Content Assist is broken.  If I take it out of the list, Content Assist works again.

Also, if I change the typedef to a simple field, then all cases work properly.

Here is the top of the stack trace:

!ENTRY org.eclipse.cdt.ui 4 4 2014-01-15 16:56:51.561
!MESSAGE Error
!STACK 0
java.lang.IllegalArgumentException: I is not a member of Cls1
	at org.eclipse.cdt.internal.core.pdom.dom.cpp.PDOMCPPClassType.getVisibility(PDOMCPPClassType.java:434)
	at org.eclipse.cdt.internal.core.index.composite.cpp.CompositeCPPClassType.getVisibility(CompositeCPPClassType.java:220)
	at org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.AccessContext.isAccessible(AccessContext.java:107)
	at org.eclipse.cdt.internal.ui.text.contentassist.DOMCompletionProposalComputer.computeCompletionProposals(DOMCompletionProposalComputer.java:144)
	at org.eclipse.cdt.internal.ui.text.contentassist.ParsingBasedProposalComputer.computeCompletionProposals(ParsingBasedProposalComputer.java:56)
	at org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalComputerDescriptor.computeCompletionProposals(CompletionProposalComputerDescriptor.java:294)
	at org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalCategory.computeCompletionProposals(CompletionProposalCategory.java:261)
Comment 1 Andrew Eidsness CLA 2014-01-15 20:52:14 EST
The difference seems to be the instanceof checks in AccessContext.isAccessible( IBinding ).

public boolean isAccessible(IBinding binding) {
    int bindingVisibility;
    if (binding instanceof ICPPMember) {
        bindingVisibility = ((ICPPMember) binding).getVisibility();
    } else {
        while (binding instanceof ICPPSpecialization) {
            binding = ((ICPPSpecialization) binding).getSpecializedBinding();
        }
        if (binding instanceof ICPPClassTemplatePartialSpecialization) {
        // A class template partial specialization inherits the visibility of its primary class template. 
            binding = ((ICPPClassTemplatePartialSpecialization) binding).getPrimaryClassTemplate();
        }
        if (binding instanceof ICPPAliasTemplateInstance) {
            binding = ((ICPPAliasTemplateInstance) binding).getTemplateDefinition();
        }
        IBinding owner = binding.getOwner();
        if (owner instanceof ICPPClassType) {
            bindingVisibility = ((ICPPClassType) owner).getVisibility(binding);
        } else {
            bindingVisibility = v_public;
        }
    }
    return isAccessible(binding, bindingVisibility);
}

When the class definition contains "int I;", then the IBinding is a CompositeCPPField, which is an instance of ICPPMember, so the first condition is used.

When the class definition contains "typedef int I;", then the IBinding is a CompositeCPPTypedef then it goes all the way down to using the owner and the call to ICPPClassType.getVisibility throws the exception.

The annotations on this method point to Bug 418479, which has a very similar description.  That bug is focused on templates, but this example does not use any.

Comparing that behaviour between the the working and not working cases, the difference is that in the non-working case owner is an instance of CompositeCPPClassType.  In the working case it is an instance of CPPClassType.

In the working case the owner is treated as a ICPPInternalClassTypeMixinHost, but that interface is not visible to the definition of CompositeCPPClassType.

----

I'm not sure if the right fix is to change CompositeCPPClassType so that it can be treated as an ICPPInternalClassTypeMixinHost or to resolve the CompositeCPPTypedef to the PDOMCPPTypedef to avoid the IllegalArgumentException.
Comment 2 Andrew Eidsness CLA 2014-01-15 20:59:34 EST
(In reply to Andrew Eidsness from comment #1)

> Comparing that behaviour between the the working and not working cases, the
> difference is that in the non-working case owner is an instance of
> CompositeCPPClassType.  In the working case it is an instance of
> CPPClassType.

Just to clarify this part.  I meant the behaviour between working Cls2 and non-working Cls1 (when both contain typedefs).  Previous explanations in this bug were comparing two definitions for Cls1:

    class Cls1 { public: typedef int I; };
and class Cls1 { public:         int I; };
Comment 3 Sergey Prigogin CLA 2014-01-15 21:06:48 EST
(In reply to Andrew Eidsness from comment #2)
A more meaningful comparison is between the case with dependent projects and the case with a single project.
Comment 4 Nathan Ridge CLA 2014-01-15 21:27:08 EST
> I'm not sure if the right fix is to change CompositeCPPClassType so that it
> can be treated as an ICPPInternalClassTypeMixinHost 

I don't think we can do this. The method "ICPPASTCompositeTypeSpecifier getCompositeTypeSpecifier()" in ICPPInternalClassTypeMixinHost cannot be implemented for an index binding because there is no AST to work with, and CPPClassType.getVisibility() relies on this method.

> or to resolve the
> CompositeCPPTypedef to the PDOMCPPTypedef to avoid the
> IllegalArgumentException.

That sounds reasonable to me. Composite bindings have a getRawBinding() method (defined in CompositeIndexBinding) that returns the underlying PDOM binding. We can probably call this in PDOMCPPClassType.getVisibility().
Comment 5 Nathan Ridge CLA 2014-01-15 21:30:07 EST
(In reply to Nathan Ridge from comment #4)
> > or to resolve the
> > CompositeCPPTypedef to the PDOMCPPTypedef to avoid the
> > IllegalArgumentException.
> 
> That sounds reasonable to me. Composite bindings have a getRawBinding()
> method (defined in CompositeIndexBinding) that returns the underlying PDOM
> binding. We can probably call this in PDOMCPPClassType.getVisibility().

Actually, it would probably be even better for CompositeCPPClassType.getVisibility() to unwrap the member binding prior to passing it to PDOMCPPClassType.getVisibility().
Comment 6 Sergey Prigogin CLA 2014-01-15 21:44:14 EST
(In reply to Nathan Ridge from comment #4)
> That sounds reasonable to me. Composite bindings have a getRawBinding()
> method (defined in CompositeIndexBinding) that returns the underlying PDOM
> binding. We can probably call this in PDOMCPPClassType.getVisibility().

Better to change PDOMCPPMemberBlock.getVisibility(PDOMCPPMemberBlock, IBinding) by replacing

    if (!(member instanceof PDOMNode))
        return -1;
		
    PDOMNode memberNode = (PDOMNode) member;
    if (memberNode.getPDOM() != block.getPDOM())
        return -1;

by

    PDOMNode memberNode = (PDOMNode) block.getPDOM().adaptBinding(member);
    if (memberNode == null)
        return -1;
Comment 7 Nathan Ridge CLA 2014-01-15 22:03:36 EST
(In reply to Sergey Prigogin from comment #6)
> (In reply to Nathan Ridge from comment #4)
> > That sounds reasonable to me. Composite bindings have a getRawBinding()
> > method (defined in CompositeIndexBinding) that returns the underlying PDOM
> > binding. We can probably call this in PDOMCPPClassType.getVisibility().
> 
> Better to change PDOMCPPMemberBlock.getVisibility(PDOMCPPMemberBlock,
> IBinding) by replacing
> 
>     if (!(member instanceof PDOMNode))
>         return -1;
> 		
>     PDOMNode memberNode = (PDOMNode) member;
>     if (memberNode.getPDOM() != block.getPDOM())
>         return -1;
> 
> by
> 
>     PDOMNode memberNode = (PDOMNode) block.getPDOM().adaptBinding(member);
>     if (memberNode == null)
>         return -1;

Oh neat! I didn't know adaptBinding() could do a Composite -> PDOM adaptation, I thought it was for AST -> PDOM only.
Comment 8 CDT Genie CLA 2014-01-15 23:23:01 EST
*** cdt git genie on behalf of Sergey Prigogin ***

    Bug 425833 - IllegalArgumentException I is not a member of Cls1
    Change-Id: I61d52d2585142c15be21638e614d5b8daa13dfae

[*] http://git.eclipse.org/c/cdt/org.eclipse.cdt.git/commit/?id=8d14ab8793d3a50857bcb159242146f654a77af3
Comment 9 Sergey Prigogin CLA 2014-01-15 23:28:22 EST
(In reply to CDT Genie from comment #8)
Andrew, please check if the issue is fixed now.
Comment 10 Andrew Eidsness CLA 2014-01-16 07:36:58 EST
Perfect, thanks.

Its fixed in both my toy sample and in the original, large project.