diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/ClassTypeHelperTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/ClassTypeHelperTests.java new file mode 100644 index 00000000000..495b2c8f26d --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/ClassTypeHelperTests.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2010 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.parser.tests.ast2; + +import java.io.IOException; + +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; +import org.eclipse.cdt.internal.core.parser.ParserException; + +/** + * Tests for ClassTypeHelper class. + */ +public class ClassTypeHelperTests extends AST2BaseTest { + + public ClassTypeHelperTests() { + } + + public ClassTypeHelperTests(String name) { + super(name); + } + + public static TestSuite suite() { + return suite(ClassTypeHelperTests.class); + } + + protected BindingAssertionHelper getAssertionHelper() throws ParserException, IOException { + String code= getAboveComment(); + return new BindingAssertionHelper(code, true); + } + + // class A { + // public: + // A(); + // int x; + // A* y; + // const A& z; + // }; + public void testHasTrivialCopyCtor_1() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("A {", 1, ICPPClassType.class); + assertTrue(ClassTypeHelper.hasTrivialCopyCtor(classType)); + } + + // struct A { + // A(const A& a); + // }; + // + // class B { + // public: + // A a; + // }; + public void testHasTrivialCopyCtor_2() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("B {", 1, ICPPClassType.class); + assertFalse(ClassTypeHelper.hasTrivialCopyCtor(classType)); + } + + // class A { + // public: + // A(); + // A(const A& a); + // int x; + // A* y; + // const A& z; + // }; + public void testHasTrivialDesctructor_1() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("A {", 1, ICPPClassType.class); + assertTrue(ClassTypeHelper.hasTrivialDestructor(classType)); + } + + // struct A { + // ~A(); + // }; + // + // class B { + // public: + // A a; + // }; + public void testHasTrivialDesctructor_2() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("B {", 1, ICPPClassType.class); + assertFalse(ClassTypeHelper.hasTrivialDestructor(classType)); + } + + // class A { + // public: + // A(); + // A(const A& a); + // void m(); + // int x; + // A* y; + // const A& z; + // }; + public void testIsPolymorphic_1() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("A {", 1, ICPPClassType.class); + assertFalse(ClassTypeHelper.isPolymorphic(classType)); + } + + // struct A { + // virtual void m(); + // }; + // + // class B : public A { + // }; + public void testIsPolymorphic_2() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + ICPPClassType classType = helper.assertNonProblem("B", 1, ICPPClassType.class); + assertTrue(ClassTypeHelper.isPolymorphic(classType)); + } +} diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java index f29296daa4d..1aa2dc3e859 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/DOMParserTestSuite.java @@ -30,6 +30,7 @@ public class DOMParserTestSuite extends TestCase { suite.addTest(ASTCPPSpecDefectTests.suite()); suite.addTest(AST2CPPImplicitNameTests.suite()); suite.addTest(AST2TemplateTests.suite()); + suite.addTest(ClassTypeHelperTests.suite()); suite.addTestSuite(QuickParser2Tests.class); suite.addTest(CompleteParser2Tests.suite()); suite.addTest(DOMLocationTests.suite()); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java index d27172e0038..4dd50a7d918 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java @@ -15,6 +15,10 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ARRAY; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE; +import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -180,7 +184,7 @@ public class ClassTypeHelper { ICPPClassType backup= getBackupDefinition(host); if (backup != null) return backup.getBases(); - + return ICPPBase.EMPTY_BASE_ARRAY; } } @@ -203,7 +207,7 @@ public class ClassTypeHelper { ICPPClassType backup= getBackupDefinition(host); if (backup != null) return backup.getDeclaredFields(); - + return ICPPField.EMPTY_CPPFIELD_ARRAY; } } @@ -235,7 +239,7 @@ public class ClassTypeHelper { } return (ICPPField[]) ArrayUtil.trim(ICPPField.class, result); } - + /** * Returns all direct and indirect base classes. * @param classType a class @@ -248,7 +252,7 @@ public class ClassTypeHelper { result.remove(classType); return result.toArray(new ICPPClassType[result.size()]); } - + private static void getAllBases(ICPPClassType classType, HashSet result) { ICPPBase[] bases= classType.getBases(); for (ICPPBase base : bases) { @@ -291,10 +295,10 @@ public class ClassTypeHelper { } return (ICPPMethod[]) ArrayUtil.trim(ICPPMethod.class, methods); } - + public static ICPPMethod[] getMethods(ICPPClassType ct) { ObjectSet set = getOwnMethods(ct); - + ICPPClassType[] bases= getAllBases(ct); for (ICPPClassType base : bases) { set.addAll(base.getDeclaredMethods()); @@ -319,7 +323,7 @@ public class ClassTypeHelper { } return set; } - + public static ICPPMethod[] getDeclaredMethods(ICPPInternalClassTypeMixinHost host) { if (host.getDefinition() == null) { host.checkForDefinition(); @@ -393,7 +397,7 @@ public class ClassTypeHelper { ICPPClassType backup= getBackupDefinition(host); if (backup != null) return backup.getNestedClasses(); - + return ICPPClassType.EMPTY_CLASS_ARRAY; } } @@ -445,18 +449,17 @@ public class ClassTypeHelper { } return field; } - - + /** - * Returns whether {@code method} is virtual. This is the case if it is declared to be virtual or - * overrides another virtual method. + * Returns whether {@code method} is virtual. This is the case if it is declared to be virtual + * or overrides another virtual method. */ public static boolean isVirtual(ICPPMethod m) { if (m instanceof ICPPConstructor) return false; if (m.isVirtual()) return true; - + final char[] mname= m.getNameCharArray(); final ICPPClassType mcl= m.getClassOwner(); if (mcl != null) { @@ -511,18 +514,18 @@ public class ClassTypeHelper { return false; if (!functionTypesAllowOverride(source.getType(), target.getType())) return false; - + final ICPPClassType sourceClass= source.getClassOwner(); final ICPPClassType targetClass= target.getClassOwner(); if (sourceClass == null || targetClass == null) return false; - + ICPPClassType[] bases= getAllBases(sourceClass); for (ICPPClassType base : bases) { if (base.isSameType(targetClass)) return true; } - + return false; } @@ -532,12 +535,12 @@ public class ClassTypeHelper { public static ICPPMethod[] findOverridden(ICPPMethod method) { if (method instanceof ICPPConstructor) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; - + final char[] mname= method.getNameCharArray(); final ICPPClassType mcl= method.getClassOwner(); if (mcl == null) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; - + final ArrayList result= new ArrayList(); final HashMap virtualInClass= new HashMap(); final ICPPFunctionType mft= method.getType(); @@ -550,7 +553,7 @@ public class ClassTypeHelper { findOverridden((ICPPClassType) b, mname, mft, virtualInClass, result); } } - + // list is filled from most derived up to here, reverse it Collections.reverse(result); return result.toArray(new ICPPMethod[result.size()]); @@ -567,7 +570,7 @@ public class ClassTypeHelper { Boolean visitedBefore= virtualInClass.get(cl); if (visitedBefore != null) return visitedBefore; - + ICPPMethod[] methods= cl.getDeclaredMethods(); ICPPMethod candidate= null; boolean hasOverridden= false; @@ -578,7 +581,7 @@ public class ClassTypeHelper { break; } } - + // prevent recursion virtualInClass.put(cl, hasOverridden); ICPPBase[] bases= cl.getBases(); @@ -610,7 +613,7 @@ public class ClassTypeHelper { final ICPPClassType mcl= method.getClassOwner(); if (mcl == null) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; - + ICPPClassType[] subclasses= getSubClasses(index, mcl); return findOverriders(subclasses, method); } @@ -645,7 +648,7 @@ public class ClassTypeHelper { private static void getSubClasses(IIndex index, ICPPBinding classOrTypedef, List result, HashSet handled) throws CoreException { if (!(classOrTypedef instanceof IType)) return; - + final String key = ASTTypeUtil.getType((IType) classOrTypedef, true); if (!handled.add(key)) { return; @@ -674,7 +677,7 @@ public class ClassTypeHelper { private static final int KIND_ASSIGNMENT_OP= 2; private static final int KIND_DTOR= 3; private static final int KIND_OTHER= 4; - + /** * For implicit methods the exception specification is inherited, search it */ @@ -688,7 +691,7 @@ public class ClassTypeHelper { int kind= getImplicitMethodKind(owner, implicitMethod); if (kind == KIND_OTHER) return null; - + List inheritedTypeids = new ArrayList(); ICPPClassType[] bases= getAllBases(owner); for (ICPPClassType base : bases) { @@ -737,13 +740,13 @@ public class ClassTypeHelper { } return KIND_OTHER; } - return KIND_OTHER; + return KIND_OTHER; } private static boolean isRefToConstClass(ICPPClassType ct, IType t) { while (t instanceof ITypedef) t= ((ITypedef) t).getType(); - + if (t instanceof ICPPReferenceType) { t= ((ICPPReferenceType) t).getType(); while (t instanceof ITypedef) @@ -809,6 +812,126 @@ public class ClassTypeHelper { return true; } + /** + * Returns true if and only if the given class has a trivial copy constructor. + * A copy constructor is trivial if: + *
    + *
  • it is implicitly defined by the compiler, and
  • + *
  • isPolymorphic(classTarget) == false, and
  • + *
  • the class has no virtual base classes, and
  • + *
  • every direct base class has trivial copy constructor, and
  • + *
  • for every nonstatic data member that has class type or array of class type, that type + * has trivial copy constructor.
  • + *
+ * Similar to std::tr1::has_trivial_copy. + * + * @param classTarget the class to check + * @return true if the class has a trivial copy constructor + */ + public static boolean hasTrivialCopyCtor(ICPPClassType classTarget) { + if (getImplicitCopyCtor(classTarget) == null) + return false; + if (isPolymorphic(classTarget)) + return false; + for (ICPPBase base : classTarget.getBases()) { + if (base.isVirtual()) + return false; + } + for (ICPPClassType baseClass : getAllBases(classTarget)) { + if (!classTarget.isSameType(baseClass) && !hasTrivialCopyCtor(baseClass)) + return false; + } + for (ICPPField field : classTarget.getDeclaredFields()) { + if (!field.isStatic()) { + IType type = field.getType(); + type = SemanticUtil.getNestedType(type, TDEF | CVTYPE | ARRAY); + if (type instanceof ICPPClassType && !classTarget.isSameType(type) && + !hasTrivialCopyCtor((ICPPClassType) type)) { + return false; + } + } + } + return true; + } + + /** + * Returns the compiler-generated copy constructor for the given class, or null + * if the class doesn't have a compiler-generated copy constructor. + * + * @param classTarget the class to get the copy ctor for. + * @return the compiler-generated copy constructor, or null if the class doesn't + * have a compiler-generated copy constructor. + */ + private static ICPPConstructor getImplicitCopyCtor(ICPPClassType classTarget) { + for (ICPPConstructor ctor : classTarget.getConstructors()) { + if (ctor.isImplicit() && getImplicitMethodKind(classTarget, ctor) == KIND_COPY_CTOR) + return ctor; + } + return null; + } + + /** + * Returns true if and only if the given class has a trivial destructor. + * A destructor is trivial if: + *
    + *
  • it is implicitly defined by the compiler, and
  • + *
  • every direct base class has trivial destructor, and
  • + *
  • for every nonstatic data member that has class type or array of class type, that type + * has trivial destructor.
  • + *
+ * Similar to std::tr1::has_trivial_destructor. + * + * @param classTarget the class to check + * @return true if the class has a trivial destructor + */ + public static boolean hasTrivialDestructor(ICPPClassType classTarget) { + for (ICPPMethod method : classTarget.getDeclaredMethods()) { + if (method.isDestructor()) + return false; + } + for (ICPPClassType baseClass : getAllBases(classTarget)) { + if (!classTarget.isSameType(baseClass) && !hasTrivialDestructor(baseClass)) + return false; + } + for (ICPPField field : classTarget.getDeclaredFields()) { + if (!field.isStatic()) { + IType type = field.getType(); + type = SemanticUtil.getNestedType(type, TDEF | CVTYPE | ARRAY); + if (type instanceof ICPPClassType && !classTarget.isSameType(type) && + !hasTrivialDestructor((ICPPClassType) type)) { + return false; + } + } + } + return true; + } + + /** + * Returns true if and only if the given class declares or inherits a virtual + * function. Similar to std::tr1::is_polymorphic. + * + * @param classTarget the class to check + * @return true if the class declares or inherits a virtual function. + */ + public static boolean isPolymorphic(ICPPClassType classTarget) { + if (hasDeclaredVirtualMethod(classTarget)) + return true; + for (ICPPClassType baseClass : getAllBases(classTarget)) { + if (hasDeclaredVirtualMethod(baseClass)) + return true; + } + return false; + } + + private static boolean hasDeclaredVirtualMethod(ICPPClassType classTarget) { + for (ICPPMethod method : classTarget.getDeclaredMethods()) { + if (method.isVirtual()) { + return true; + } + } + return false; + } + /** * Checks whether class is abstract, i.e. has pure virtual functions that were * not implemented in base after declaration. @@ -820,7 +943,7 @@ public class ClassTypeHelper { public static ICPPMethod[] getPureVirtualMethods(ICPPClassType classType) { Map> result= collectPureVirtualMethods(classType, new HashMap>>()); - + int resultArraySize = 0; for (List methods : result.values()) { resultArraySize += methods.size(); @@ -837,14 +960,13 @@ public class ClassTypeHelper { private static Map> collectPureVirtualMethods(ICPPClassType classType, Map>> cache) { - Map> result = cache.get(classType); if (result != null) return result; - + result= new HashMap>(); cache.put(classType, result); - + // Look at the pure virtual methods of the base classes Set handledBaseClasses= new HashSet(); for (ICPPBase base : classType.getBases()) {