diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java index 4e68e5b7360..f07a4c9fe16 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java @@ -40,7 +40,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; 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.ICPPMethod; -import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; +import org.eclipse.cdt.core.dom.ast.cpp.SemanticQueries; /** * Reports a problem if object of a class cannot be created because @@ -196,7 +196,7 @@ public class AbstractClassInstantiationChecker extends AbstractIndexAstChecker { ICPPClassType classType = (ICPPClassType) unwindedType; ICPPMethod[] pureVirtualMethods = pureVirtualMethodsCache.get(classType); if (pureVirtualMethods == null) { - pureVirtualMethods = ClassTypeHelper.getPureVirtualMethods(classType, problemNode); + pureVirtualMethods = SemanticQueries.getPureVirtualMethods(classType, problemNode); pureVirtualMethodsCache.put(classType, pureVirtualMethods); } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java index 7f95fd9038e..20c9fe2090f 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java @@ -127,6 +127,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPPointerToMemberType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable; +import org.eclipse.cdt.core.dom.ast.cpp.SemanticQueries; import org.eclipse.cdt.core.parser.ParserLanguage; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator; @@ -9579,7 +9580,7 @@ public class AST2CPPTests extends AST2TestBase { public void testRecursiveClassInheritance_Bug357256() throws Exception { BindingAssertionHelper bh= getAssertionHelper(); ICPPClassType c= bh.assertNonProblem("A", 1); - assertEquals(0, ClassTypeHelper.getPureVirtualMethods(c, null).length); + assertEquals(0, SemanticQueries.getPureVirtualMethods(c, null).length); } // template struct CT1 {}; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/SemanticQueries.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/SemanticQueries.java index 36cb039bae2..18e0af64b33 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/SemanticQueries.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/SemanticQueries.java @@ -13,7 +13,17 @@ package org.eclipse.cdt.core.dom.ast.cpp; 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.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; @@ -71,4 +81,187 @@ public class SemanticQueries { return function.getParameters().length >= numArguments && function.getRequiredArgumentCount() <= numArguments; } + + /** + * Returns all pure virtual methods of a class. Inherited pure virtual methods + * that have not been implemented are also returned. + * + * NOTE: The method produces complete results for template instantiations + * but doesn't take into account base classes and methods dependent on unspecified + * template parameters. + * + * @param classType the class whose pure virtual methods should be returned + * @param point the point of template instantiation, if applicable + * @return an array containing all pure virtual methods of the class + * @since 5.6 + */ + public static ICPPMethod[] getPureVirtualMethods(ICPPClassType classType, IASTNode point) { + return new PureVirtualMethodCollector().collect(classType, point); + } + + /** Helper class for {@link #getPureVirtualMethods(ICPPClassType, IASTNode)} */ + private static class PureVirtualMethodCollector { + /** + * This class represents a mapping of virtual methods in a class hierarchy + * to their final overriders (see [class.virtual] p2). Since a class hierarchy + * can contain multiple subobjects of the same type (if multiple, non-virtual + * inheritance is used), and the pure virtual methods of each subobject must + * be implemented independently, we give each subobject of a given type a + * number, and for each method we keep track of the final overrider for each + * subobject number. + */ + private static class FinalOverriderMap { + private Map> fMap = new HashMap>(); + + /** + * Record 'overrider' as being the final ovverider of 'method' in subobject + * 'subobjectNumber'. + */ + public void add(ICPPMethod method, int subobjectNumber, ICPPMethod overrider) { + Map overriders = fMap.get(method); + if (overriders == null) { + overriders = new HashMap(); + fMap.put(method, overriders); + } + overriders.put(subobjectNumber, overrider); + } + + /** + * For each subobject for which 'method' has been overridden, update + * its final overrider to 'overrider'. + */ + public void replaceForAllSubobjects(ICPPMethod method, ICPPMethod overrider) { + Map overriders = fMap.get(method); + if (overriders == null) + return; + for (Integer i : overriders.keySet()) + overriders.put(i, overrider); + } + + /** + * Merge the final overriders from another FinalOverriderMap into this one. + */ + public void addOverriders(FinalOverriderMap other) { + for (ICPPMethod method : other.fMap.keySet()) { + Map overriders = fMap.get(method); + if (overriders == null) { + overriders = new HashMap(); + fMap.put(method, overriders); + } + Map otherOverriders = other.fMap.get(method); + for (Integer i : otherOverriders.keySet()) { + ICPPMethod overrider = otherOverriders.get(i); + overriders.put(i, overrider); + } + } + } + + /** + * Go through the final overrider map and find functions which are + * pure virtual in the hierarchy's root. These are functions which + * are declared pure virtual, and whose final overrider is themself, + * meaning they have not been overridden. + */ + public ICPPMethod[] collectPureVirtualMethods() { + List pureVirtualMethods = new ArrayList(); + for (ICPPMethod method : fMap.keySet()) { + if (method.isPureVirtual()) { + Map finalOverriders = fMap.get(method); + for (Integer subobjectNumber : finalOverriders.keySet()) { + ICPPMethod finalOverrider = finalOverriders.get(subobjectNumber); + if (finalOverrider == method) { + pureVirtualMethods.add(method); + } + } + } + } + return pureVirtualMethods.toArray(new ICPPMethod[pureVirtualMethods.size()]); + } + } + + // The last subobject number used for each type in the hierarchy. This is used to + // assign subobject numbers to subobjects. Virtual subobjects get a subobject + // number of zero, while non-virtual subobjects are number starting from one. + private Map subobjectNumbers = new HashMap(); + + // Cache of final overrider maps for virtual base subobjects. Since such subobjects + // only occur once in the hierarchy, we can cache the final overrider maps we + // compute for them. + private Map virtualBaseCache = new HashMap(); + + public ICPPMethod[] collect(ICPPClassType root, IASTNode point) { + FinalOverriderMap finalOverriderMap = collectFinalOverriders(root, false, new HashSet(), point); + return finalOverriderMap.collectPureVirtualMethods(); + } + + /** + * Compute the final overrider map for a subtree in a class hierarchy. + * + * @param classType the root of the subtree in question + * @param isVirtualBase whether 'classType' is inherited virtually + * @param inheritanceChain the chain of classes from the entire hierarchy's root to 'classType'. + * This is used to guard against circular inheritance. + * @param point the point of template instantiation, if applicable + * @return the computed final overrider map + */ + private FinalOverriderMap collectFinalOverriders(ICPPClassType classType, boolean isVirtualBase, + Set inheritanceChain, IASTNode point) { + FinalOverriderMap result = new FinalOverriderMap(); + + inheritanceChain.add(classType); + + // Determine the subobject number for the current class. + int subobjectNumber = 0; + if (!isVirtualBase) { + Integer lastNumber = subobjectNumbers.get(classType); + subobjectNumber = (lastNumber == null ? 0 : lastNumber) + 1; + subobjectNumbers.put(classType, subobjectNumber); + } + + // Go through our base classes. + for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) { + IBinding baseClass = base.getBaseClass(); + if (!(baseClass instanceof ICPPClassType)) + continue; + ICPPClassType baseType = (ICPPClassType) baseClass; + + // Guard against circular inheritance. + if (inheritanceChain.contains(baseType)) + continue; + + // Collect final overrider information from the base class. + // If it's a virtual base class and we've already processed it + // in this class hierarchy, don't process it again. + FinalOverriderMap baseOverriderMap; + if (base.isVirtual()) { + baseOverriderMap = virtualBaseCache.get(baseType); + if (baseOverriderMap == null) { + baseOverriderMap = collectFinalOverriders(baseType, true, inheritanceChain, point); + } + } else { + baseOverriderMap = collectFinalOverriders(baseType, false, inheritanceChain, point); + } + + // Merge final overrider information from base class into this class. + result.addOverriders(baseOverriderMap); + } + + // Go through our own methods. + for (ICPPMethod method : ClassTypeHelper.getOwnMethods(classType, point)) { + // For purposes of this computation, every virtual method is + // deemed for override itself. + result.add(method, subobjectNumber, method); + + // Find all methods overridden by this method, and set their final overrider + // to be this method. + ICPPMethod[] overriddenMethods = ClassTypeHelper.findOverridden(method, point); + for (ICPPMethod overriddenMethod : overriddenMethods) + result.replaceForAllSubobjects(overriddenMethod, method); + } + + inheritanceChain.remove(classType); + + return result; + } + } } 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 ea80b7181a3..5d02c4031be 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 @@ -1007,187 +1007,4 @@ public class ClassTypeHelper { name = ""; //$NON-NLS-1$ return new IllegalArgumentException(name + " is not a member of " + classType.getName()); //$NON-NLS-1$ } - - /** - * Gets all pure virtual methods of a class. Inherited pure virtual methods - * that have not been implemented are also returned. - * - * NOTE: The method produces complete results for template instantiations - * but doesn't take into account base classes and methods dependent on unspecified - * template parameters. - * - * @param classType the class whose pure virtual methods should be returned - * @param point the point of template instantiation, if applicable - * @return an array containing all pure virtual methods of the class - * @since 5.6 - */ - public static ICPPMethod[] getPureVirtualMethods(ICPPClassType classType, IASTNode point) { - return new PureVirtualMethodCollector().collect(classType, point); - } - - // Helper class for getPureVirtualMethods() - private static class PureVirtualMethodCollector { - /** - * This class represents a mapping of virtual methods in a class hierarchy - * to their final overriders (see [class.virtual] p2). Since a class hierarchy - * can contain multiple subobjects of the same type (if multiple, non-virtual - * inheritance is used), and the pure virtual methods of each subobject must - * be implemented independently, we give each subobject of a given type a - * number, and for each method we keep track of the final overrider for each - * subobject number. - */ - private static class FinalOverriderMap { - private Map> fMap = new HashMap>(); - - /** - * Record 'overrider' as being the final ovverider of 'method' in subobject - * 'subobjectNumber'. - */ - public void add(ICPPMethod method, int subobjectNumber, ICPPMethod overrider) { - Map overriders = fMap.get(method); - if (overriders == null) { - overriders = new HashMap(); - fMap.put(method, overriders); - } - overriders.put(subobjectNumber, overrider); - } - - /** - * For each subobject for which 'method' has been overridden, update - * its final overrider to 'overrider'. - */ - public void replaceForAllSubobjects(ICPPMethod method, ICPPMethod overrider) { - Map overriders = fMap.get(method); - if (overriders == null) - return; - for (Integer i : overriders.keySet()) - overriders.put(i, overrider); - } - - /** - * Merge the final overriders from another FinalOverriderMap into this one. - */ - public void addOverriders(FinalOverriderMap other) { - for (ICPPMethod method : other.fMap.keySet()) { - Map overriders = fMap.get(method); - if (overriders == null) { - overriders = new HashMap(); - fMap.put(method, overriders); - } - Map otherOverriders = other.fMap.get(method); - for (Integer i : otherOverriders.keySet()) { - ICPPMethod overrider = otherOverriders.get(i); - overriders.put(i, overrider); - } - } - } - - /** - * Go through the final overrider map and find functions which are - * pure virtual in the hierarchy's root. These are functions which - * are declared pure virtual, and whose final overrider is themself, - * meaning they have not been overridden. - */ - public ICPPMethod[] collectPureVirtualMethods() { - List pureVirtualMethods = new ArrayList(); - for (ICPPMethod method : fMap.keySet()) { - if (method.isPureVirtual()) { - Map finalOverriders = fMap.get(method); - for (Integer subobjectNumber : finalOverriders.keySet()) { - ICPPMethod finalOverrider = finalOverriders.get(subobjectNumber); - if (finalOverrider == method) { - pureVirtualMethods.add(method); - } - } - } - } - return pureVirtualMethods.toArray(new ICPPMethod[pureVirtualMethods.size()]); - } - } - - // The last subobject number used for each type in the hierarchy. This is used to - // assign subobject numbers to subobjects. Virtual subobjects get a subobject - // number of zero, while non-virtual subobjects are number starting from one. - private Map subobjectNumbers = new HashMap(); - - // Cache of final overrider maps for virtual base subobjects. Since such subobjects - // only occur once in the hierarchy, we can cache the final overrider maps we - // compute for them. - private Map virtualBaseCache = new HashMap(); - - public ICPPMethod[] collect(ICPPClassType root, IASTNode point) { - FinalOverriderMap finalOverriderMap = collectFinalOverriders(root, false, new HashSet(), point); - return finalOverriderMap.collectPureVirtualMethods(); - } - - /** - * Compute the final overrider map for a subtree in a class hierarchy. - * - * @param classType the root of the subtree in question - * @param isVirtualBase whether 'classType' is inherited virtually - * @param inheritanceChain the chain of classes from the entire hierarchy's root to 'classType'. - * This is used to guard against circular inheritance. - * @param point the point of template instantiation, if applicable - * @return the computed final overrider map - */ - private FinalOverriderMap collectFinalOverriders(ICPPClassType classType, boolean isVirtualBase, - Set inheritanceChain, IASTNode point) { - FinalOverriderMap result = new FinalOverriderMap(); - - inheritanceChain.add(classType); - - // Determine the subobject number for the current class. - int subobjectNumber = 0; - if (!isVirtualBase) { - Integer lastNumber = subobjectNumbers.get(classType); - subobjectNumber = (lastNumber == null ? 0 : lastNumber) + 1; - subobjectNumbers.put(classType, subobjectNumber); - } - - // Go through our base classes. - for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) { - IBinding baseClass = base.getBaseClass(); - if (!(baseClass instanceof ICPPClassType)) - continue; - ICPPClassType baseType = (ICPPClassType) baseClass; - - // Guard against circular inheritance. - if (inheritanceChain.contains(baseType)) - continue; - - // Collect final overrider information from the base class. - // If it's a virtual base class and we've already processed it - // in this class hierarchy, don't process it again. - FinalOverriderMap baseOverriderMap; - if (base.isVirtual()) { - baseOverriderMap = virtualBaseCache.get(baseType); - if (baseOverriderMap == null) { - baseOverriderMap = collectFinalOverriders(baseType, true, inheritanceChain, point); - } - } else { - baseOverriderMap = collectFinalOverriders(baseType, false, inheritanceChain, point); - } - - // Merge final overrider information from base class into this class. - result.addOverriders(baseOverriderMap); - } - - // Go through our own methods. - for (ICPPMethod method : ClassTypeHelper.getOwnMethods(classType, point)) { - // For purposes of this computation, every virtual method is - // deemed for override itself. - result.add(method, subobjectNumber, method); - - // Find all methods overridden by this method, and set their final overrider - // to be this method. - ICPPMethod[] overriddenMethods = ClassTypeHelper.findOverridden(method, point); - for (ICPPMethod overriddenMethod : overriddenMethods) - result.replaceForAllSubobjects(overriddenMethod, method); - } - - inheritanceChain.remove(classType); - - return result; - } - } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TypeTraits.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TypeTraits.java index 71454ab5d6e..d02dfdcd849 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TypeTraits.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/TypeTraits.java @@ -23,6 +23,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType; +import org.eclipse.cdt.core.dom.ast.cpp.SemanticQueries; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper.MethodKind; @@ -298,7 +299,7 @@ public class TypeTraits { } public static boolean isAbstract(ICPPClassType classType, IASTNode point) { - return ClassTypeHelper.getPureVirtualMethods(classType, point).length != 0; + return SemanticQueries.getPureVirtualMethods(classType, point).length != 0; } /**