diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexUpdateTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexUpdateTests.java index 52d618fb496..cd0b65a4318 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexUpdateTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/index/tests/IndexUpdateTests.java @@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.ITypedef; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.core.dom.ast.IVariable; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; @@ -898,4 +899,58 @@ public class IndexUpdateTests extends IndexTestBase { checkValue("C::mem", null); checkValue("e0", 0L); } + + //class A {}; + //class B {friend class A;}; + + //class B {}; + public void testFriendClass() throws Exception { + setupFile(2, true); + assertFriendRemoval("B", "A"); + } + + // class X {public: char* foo(int);}; + // class Y {friend char* X::foo(int);}; + + // class Y {}; + public void testFriendMethod() throws Exception { + setupFile(2, true); + assertFriendRemoval("Y", "X::foo"); + } + + // class X {friend void friend_set(X*, int);}; + // void friend_set(X* p, int i) {} + + // class X {}; + public void testFriendFunction() throws Exception { + setupFile(2, true); + assertFriendRemoval("X", "friend_set"); + } + + private void assertFriendRemoval(String clientClassBinding, String supplierBinding) throws Exception { + fIndex.acquireReadLock(); + try { + IBinding client = findBinding(clientClassBinding); + IBinding supplier = findBinding(supplierBinding); + assertNotNull("Unable to find binding with name \""+clientClassBinding+"\"", client); + assertTrue("Unable to find binding with name \""+clientClassBinding+"\"", client instanceof ICPPClassType); + assertNotNull("Unable to find binding with name \""+supplierBinding+"\"", supplier); + assertTrue(((ICPPClassType)client).getFriends().length == 1); + assertTrue(((ICPPClassType)client).getFriends()[0].equals(supplier)); + } finally { + fIndex.releaseReadLock(); + } + + updateFile(); + + fIndex.acquireReadLock(); + try { + IBinding client = findBinding(clientClassBinding); + assertNotNull("Unable to find binding with name \""+clientClassBinding+"\"", client); + assertTrue("Unable to find binding with name \""+clientClassBinding+"\"", client instanceof ICPPClassType); + assertTrue(((ICPPClassType)client).getFriends().length == 0); + } finally { + fIndex.releaseReadLock(); + } + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/ClassTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/ClassTests.java index 2c2f83d64df..4e308640ae5 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/ClassTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/ClassTests.java @@ -124,7 +124,7 @@ public class ClassTests extends PDOMTestBase { } /* Test friend relationships between classes */ - public void _testFriend() throws Exception { + public void testFriend() throws Exception { IBinding[] bindings = pdom.findBindings(Pattern.compile("ClassA"), true, IndexFilter.ALL_DECLARED, NPM); assertEquals(1, bindings.length); ICPPClassType classA = (ICPPClassType) bindings[0]; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexCPPBindingConstants.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexCPPBindingConstants.java index 104fee0a86a..f88f4d3e8e5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexCPPBindingConstants.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IIndexCPPBindingConstants.java @@ -59,4 +59,5 @@ public interface IIndexCPPBindingConstants { int CPP_UNKNOWN_CLASS_TYPE= IIndexBindingConstants.LAST_CONSTANT + 42; int CPP_UNKNOWN_CLASS_INSTANCE= IIndexBindingConstants.LAST_CONSTANT + 43; int CPP_TEMPLATE_NON_TYPE_PARAMETER= IIndexBindingConstants.LAST_CONSTANT + 44; + int CPP_FRIEND_DECLARATION = IIndexBindingConstants.LAST_CONSTANT + 45; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java index cd36c7bbf6e..98b255c218b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java @@ -164,11 +164,12 @@ public class PDOM extends PlatformObject implements IPDOM { * 72.1 - store flag for pure virtual methods. * 73.0 - add values for variables and enumerations, bug 250788 * 74.0 - changes for proper template argument support, bug 242668 + * 75.0 - support for friends, bug 250167 */ private static int version(int major, int minor) { return major << 16 + minor; } - public static final int MAJOR_VERSION = 74; + public static final int MAJOR_VERSION = 75; public static final int MINOR_VERSION = 0; // minor versions must be compatible public static final int CURRENT_VERSION= version(MAJOR_VERSION, MINOR_VERSION); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java index a18188b6e4c..c0178577afe 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/PDOMName.java @@ -52,9 +52,10 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { public static final int IS_REFERENCE = IS_DECLARATION | IS_DEFINITION; public static final int DECL_DEF_REF_MASK = IS_DECLARATION | IS_DEFINITION | IS_REFERENCE; public static final int IS_INHERITANCE_SPEC = 0x04; - public static final int COULD_BE_POLYMORPHIC_METHOD_CALL = 0x08; - public static final int READ_ACCESS = 0x10; - public static final int WRITE_ACCESS = 0x20; + public static final int IS_FRIEND_SPEC = 0x08; + public static final int COULD_BE_POLYMORPHIC_METHOD_CALL = 0x10; + public static final int READ_ACCESS = 0x20; + public static final int WRITE_ACCESS = 0x40; public PDOMName(PDOM pdom, IASTName name, PDOMFile file, PDOMBinding binding, PDOMName caller) @@ -209,6 +210,15 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { return pdom.getDB().getByte(record + FLAGS) & mask; } + public void setIsFriendSpecifier(boolean val) throws CoreException { + int flags= pdom.getDB().getByte(record + FLAGS) & 0xff; + if (val) + flags |= IS_FRIEND_SPEC; + else + flags &= ~IS_FRIEND_SPEC; + pdom.getDB().putByte(record + FLAGS, (byte) flags); + } + public void setIsBaseSpecifier(boolean val) throws CoreException { int flags= pdom.getDB().getByte(record + FLAGS) & 0xff; if (val) @@ -218,6 +228,10 @@ public final class PDOMName implements IIndexFragmentName, IASTFileLocation { pdom.getDB().putByte(record + FLAGS, (byte) flags); } + public boolean isFriendSpecifier() throws CoreException { + return getFlags(IS_FRIEND_SPEC) == IS_FRIEND_SPEC; + } + public boolean isBaseSpecifier() throws CoreException { return getFlags(IS_INHERITANCE_SPEC) == IS_INHERITANCE_SPEC; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPClassType.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPClassType.java index 4a873d3fec9..1dbc0314f60 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPClassType.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPClassType.java @@ -56,11 +56,13 @@ class PDOMCPPClassType extends PDOMCPPBinding implements IPDOMCPPClassType, IPDO private static final int MEMBERLIST = PDOMCPPBinding.RECORD_SIZE + 4; - private static final int KEY = PDOMCPPBinding.RECORD_SIZE + 8; // byte - private static final int ANONYMOUS= PDOMCPPBinding.RECORD_SIZE + 9; // byte + private static final int FIRSTFRIEND = PDOMCPPBinding.RECORD_SIZE + 8; + + private static final int KEY = PDOMCPPBinding.RECORD_SIZE + 12; // byte + private static final int ANONYMOUS= PDOMCPPBinding.RECORD_SIZE + 13; // byte @SuppressWarnings("hiding") - protected static final int RECORD_SIZE = PDOMCPPBinding.RECORD_SIZE + 10; + protected static final int RECORD_SIZE = PDOMCPPBinding.RECORD_SIZE + 14; private ICPPClassScope fScope; @@ -182,6 +184,45 @@ class PDOMCPPClassType extends PDOMCPPBinding implements IPDOMCPPClassType, IPDO base.delete(); } } + + public void addFriend(PDOMCPPFriend friend) throws CoreException { + PDOMCPPFriend firstFriend = getFirstFriend(); + friend.setNextFriend(firstFriend); + setFirstFriend(friend); + } + + private PDOMCPPFriend getFirstFriend() throws CoreException { + int rec = pdom.getDB().getInt(record + FIRSTFRIEND); + return rec != 0 ? new PDOMCPPFriend(pdom, rec) : null; + } + + private void setFirstFriend(PDOMCPPFriend friend) throws CoreException { + int rec = friend != null ? friend.getRecord() : 0; + pdom.getDB().putInt(record + FIRSTFRIEND, rec); + } + + public void removeFriend(PDOMName pdomName) throws CoreException { + PDOMCPPFriend friend = getFirstFriend(); + PDOMCPPFriend predecessor= null; + int nameRec= pdomName.getRecord(); + while (friend != null) { + PDOMName name = friend.getSpecifierName(); + if (name != null && name.getRecord() == nameRec) { + break; + } + predecessor= friend; + friend= friend.getNextFriend(); + } + if (friend != null) { + if (predecessor != null) { + predecessor.setNextFriend(friend.getNextFriend()); + } + else { + setFirstFriend(friend.getNextFriend()); + } + friend.delete(); + } + } public IScope getCompositeScope() throws DOMException { if (fScope == null) { @@ -309,8 +350,15 @@ class PDOMCPPClassType extends PDOMCPPBinding implements IPDOMCPPClassType, IPDO } public IBinding[] getFriends() throws DOMException { - // not yet supported. - return IBinding.EMPTY_BINDING_ARRAY; + try { + final List list = new ArrayList(); + for (PDOMCPPFriend friend = getFirstFriend(); + friend != null; friend = friend.getNextFriend()) list.add(0,friend.getFriendSpecifier()); + return list.toArray(new IBinding[list.size()]); + } catch (CoreException e) { + CCorePlugin.log(e); + return new IBinding[0]; + } } public ICPPMethod[] getMethods() throws DOMException { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPFriend.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPFriend.java new file mode 100644 index 00000000000..a7edc368a59 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPFriend.java @@ -0,0 +1,75 @@ +package org.eclipse.cdt.internal.core.pdom.dom.cpp; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.internal.core.index.IIndexCPPBindingConstants; +import org.eclipse.cdt.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.db.Database; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; +import org.eclipse.core.runtime.CoreException; + +class PDOMCPPFriend extends PDOMNode { + + private static final int FRIEND_SPECIFIER = PDOMNode.RECORD_SIZE + 0; + private static final int NEXT_FRIEND = PDOMNode.RECORD_SIZE + 4; + + @SuppressWarnings("hiding") + protected static final int RECORD_SIZE = PDOMNode.RECORD_SIZE + 8; + + public PDOMCPPFriend(PDOM pdom, int record) { + super(pdom, record); + } + + public PDOMCPPFriend(PDOM pdom, PDOMName friendSpec) throws CoreException { + super(pdom, null); + Database db = pdom.getDB(); + + int friendrec = friendSpec != null ? friendSpec.getRecord() : 0; + db.putInt(record + FRIEND_SPECIFIER, friendrec); + } + + @Override + protected int getRecordSize() { + return RECORD_SIZE; + } + + @Override + public int getNodeType() { + return IIndexCPPBindingConstants.CPP_FRIEND_DECLARATION; + } + + public PDOMName getSpecifierName() throws CoreException { + int rec = pdom.getDB().getInt(record + FRIEND_SPECIFIER); + if (rec != 0) return new PDOMName(pdom, rec); + return null; + } + + public IBinding getFriendSpecifier() { + PDOMName friendSpecName; + try { + friendSpecName = getSpecifierName(); + if (friendSpecName != null) { + return friendSpecName.getBinding(); + } + } catch (CoreException e) { + CCorePlugin.log(e); + } + return null; + } + + public void setNextFriend(PDOMCPPFriend nextFriend) throws CoreException { + int rec = nextFriend != null ? nextFriend.getRecord() : 0; + pdom.getDB().putInt(record + NEXT_FRIEND, rec); + } + + public PDOMCPPFriend getNextFriend() throws CoreException { + int rec = pdom.getDB().getInt(record + NEXT_FRIEND); + return rec != 0 ? new PDOMCPPFriend(pdom, rec) : null; + } + + public void delete() throws CoreException { + pdom.getDB().free(record); + } + +} diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java index ed96f6c5e0b..c5ba2171d84 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/dom/cpp/PDOMCPPLinkage.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IEnumeration; @@ -31,6 +32,9 @@ import org.eclipse.cdt.core.dom.ast.IProblemBinding; import org.eclipse.cdt.core.dom.ast.IScope; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.ITypedef; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective; @@ -908,6 +912,32 @@ class PDOMCPPLinkage extends PDOMLinkage implements IIndexCPPBindingConstants { CCorePlugin.log(e); } } + else if (parentNode instanceof ICPPASTElaboratedTypeSpecifier) { + ICPPASTElaboratedTypeSpecifier elaboratedSpecifier = (ICPPASTElaboratedTypeSpecifier)parentNode; + if (elaboratedSpecifier.isFriend()) { + pdomName.setIsFriendSpecifier(true); + PDOMName enclClassName= (PDOMName) pdomName.getEnclosingDefinition(); + PDOMBinding enclClassBinding= enclClassName.getBinding(); + if (enclClassBinding instanceof PDOMCPPClassType) { + ((PDOMCPPClassType)enclClassBinding).addFriend(new PDOMCPPFriend(pdom, pdomName)); + } + } + } + else if (parentNode instanceof ICPPASTFunctionDeclarator) { + if (parentNode.getParent() instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration grandparentNode = (IASTSimpleDeclaration) parentNode.getParent(); + if (grandparentNode.getDeclSpecifier() instanceof ICPPASTDeclSpecifier) { + if (((ICPPASTDeclSpecifier)grandparentNode.getDeclSpecifier()).isFriend()) { + pdomName.setIsFriendSpecifier(true); + PDOMName enclClassName= (PDOMName) pdomName.getEnclosingDefinition(); + PDOMBinding enclClassBinding= enclClassName.getBinding(); + if (enclClassBinding instanceof PDOMCPPClassType) { + ((PDOMCPPClassType)enclClassBinding).addFriend(new PDOMCPPFriend(pdom, pdomName)); + } + } + } + } + } } /* (non-Javadoc) @@ -946,6 +976,14 @@ class PDOMCPPLinkage extends PDOMLinkage implements IIndexCPPBindingConstants { } } } + if (pdomName.isFriendSpecifier()) { + PDOMName enclClassName= (PDOMName) pdomName.getEnclosingDefinition(); + PDOMBinding enclClassBinding= enclClassName.getBinding(); + if (enclClassBinding instanceof PDOMCPPClassType) { + PDOMCPPClassType ownerClass = (PDOMCPPClassType)enclClassBinding; + ownerClass.removeFriend(pdomName); + } + } } @Override