diff options
3 files changed, 233 insertions, 84 deletions
diff --git a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/AbstractClassInstantiationCheckerTest.java b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/AbstractClassInstantiationCheckerTest.java index 207fa0f0220..df33e42fd17 100644 --- a/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/AbstractClassInstantiationCheckerTest.java +++ b/codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/AbstractClassInstantiationCheckerTest.java @@ -258,4 +258,58 @@ public class AbstractClassInstantiationCheckerTest extends CheckerTestCase { loadCodeAndRun(getAboveComment()); checkNoErrors(); } + + // struct MyInterface { + // virtual void doIt() = 0; + // }; + // + // struct Empty: virtual public MyInterface {}; + // + // struct Implementer: virtual public MyInterface { + // virtual void doIt(); + // }; + // + // struct Multiple: public Empty, public Implementer {}; + // + // static Multiple sharedMultiple; + public void testDiamondInheritanceWithOneImplementor_bug351612a() { + loadCodeAndRun(getAboveComment()); + checkNoErrors(); + } + + // struct MyInterface { + // virtual void doIt() = 0; + // }; + // + // struct Empty: virtual public MyInterface {}; + // + // struct Implementer: public MyInterface { + // virtual void doIt(); + // }; + // + // struct Multiple: public Empty, public Implementer {}; + // + // static Multiple sharedMultiple; + public void testDiamondInheritanceWithOneImplementor_bug351612b() { + loadCodeAndRun(getAboveComment()); + checkErrorLine(13); + } + + // struct MyInterface { + // virtual void doIt() = 0; + // }; + // + // struct Empty: public MyInterface {}; + // + // struct Implementer: virtual public MyInterface { + // virtual void doIt(); + // }; + // + // struct Multiple: public Empty, public Implementer {}; + // + // static Multiple sharedMultiple; + public void testDiamondInheritanceWithOneImplementor_bug351612c() { + loadCodeAndRun(getAboveComment()); + checkErrorLine(13); + } } 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 523fd65593c..36cb039bae2 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 @@ -18,6 +18,9 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; /** + * This class exposes semantic queries about C++ code to clients such + * as code analysis. + * * @since 5.5 */ public class SemanticQueries { 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 21d15202c58..ea80b7181a3 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 @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -85,8 +84,6 @@ import org.eclipse.core.runtime.CoreException; * @see CPPClassTemplate */ public class ClassTypeHelper { - private static final String DESTRUCTOR_OVERRIDE_KEY = "~"; //$NON-NLS-1$ - public static IBinding[] getFriends(ICPPInternalClassTypeMixinHost host) { if (host.getDefinition() == null) { host.checkForDefinition(); @@ -350,7 +347,7 @@ public class ClassTypeHelper { * Returns methods either declared by the given class or generated by the compiler. Does not * include methods declared in base classes. */ - private static ObjectSet<ICPPMethod> getOwnMethods(ICPPClassType classType, IASTNode point) { + public static ObjectSet<ICPPMethod> getOwnMethods(ICPPClassType classType, IASTNode point) { ObjectSet<ICPPMethod> set= new ObjectSet<ICPPMethod>(4); set.addAll(ClassTypeHelper.getDeclaredMethods(classType, point)); set.addAll(getImplicitMethods(classType, point)); @@ -621,8 +618,9 @@ public class ClassTypeHelper { ICPPMethod candidate= null; boolean hasOverridden= false; for (ICPPMethod method : methods) { - if (CharArrayUtils.equals(methodName, method.getNameCharArray()) && - functionTypesAllowOverride(methodType, method.getType())) { + if (methodName[0] == '~' && method.isDestructor() + || (CharArrayUtils.equals(methodName, method.getNameCharArray()) + && functionTypesAllowOverride(methodType, method.getType()))) { candidate= method; hasOverridden= method.isVirtual(); break; @@ -865,32 +863,6 @@ public class ClassTypeHelper { } /** - * Checks whether class is abstract, i.e. has pure virtual functions that were - * not implemented in base after declaration. - * - * NOTE: The method produces complete results for template instantiations - * but doesn't take into account base classes and methods dependent on unspecified - * template parameters. - */ - public static ICPPMethod[] getPureVirtualMethods(ICPPClassType classType, IASTNode point) { - Map<String, List<ICPPMethod>> result= collectPureVirtualMethods(classType, - new HashMap<ICPPClassType, Map<String, List<ICPPMethod>>>(), point); - - int resultArraySize = 0; - for (List<ICPPMethod> methods : result.values()) { - resultArraySize += methods.size(); - } - ICPPMethod[] resultArray = new ICPPMethod[resultArraySize]; - int resultArrayIdx = 0; - for (List<ICPPMethod> methods : result.values()) { - for (ICPPMethod method : methods) { - resultArray[resultArrayIdx++] = method; - } - } - return resultArray; - } - - /** * Returns the visibility for a given <code>member</code> in the <code>host</code>. * Throws an IllegalArgumentException if <code>member</code> is not a member of <code>host</code> * @@ -1035,67 +1007,187 @@ public class ClassTypeHelper { name = "<anonymous>"; //$NON-NLS-1$ return new IllegalArgumentException(name + " is not a member of " + classType.getName()); //$NON-NLS-1$ } - - private static Map<String, List<ICPPMethod>> collectPureVirtualMethods(ICPPClassType classType, - Map<ICPPClassType, Map<String, List<ICPPMethod>>> cache, IASTNode point) { - Map<String, List<ICPPMethod>> result = cache.get(classType); - if (result != null) - return result; - - result= new HashMap<String, List<ICPPMethod>>(); - cache.put(classType, result); - - // Look at the pure virtual methods of the base classes - Set<IBinding> handledBaseClasses= new HashSet<IBinding>(); - for (ICPPBase base : ClassTypeHelper.getBases(classType, point)) { - final IBinding baseClass = base.getBaseClass(); - if (baseClass instanceof ICPPClassType && handledBaseClasses.add(baseClass)) { - Map<String, List<ICPPMethod>> pureVirtuals = collectPureVirtualMethods((ICPPClassType) baseClass, cache, point); - // Merge derived pure virtual methods - for (String key : pureVirtuals.keySet()) { - List<ICPPMethod> list = result.get(key); - if (list == null) { - list= new ArrayList<ICPPMethod>(); - result.put(key, list); - } - list.addAll(pureVirtuals.get(key)); + + /** + * 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<ICPPMethod, Map<Integer, ICPPMethod>> fMap = new HashMap<ICPPMethod, Map<Integer, ICPPMethod>>(); + + /** + * Record 'overrider' as being the final ovverider of 'method' in subobject + * 'subobjectNumber'. + */ + public void add(ICPPMethod method, int subobjectNumber, ICPPMethod overrider) { + Map<Integer, ICPPMethod> overriders = fMap.get(method); + if (overriders == null) { + overriders = new HashMap<Integer, ICPPMethod>(); + fMap.put(method, overriders); } + overriders.put(subobjectNumber, overrider); } - } - - // Remove overridden pure-virtual methods and add in new pure virtuals. - final ObjectSet<ICPPMethod> methods = getOwnMethods(classType, point); - for (ICPPMethod method : methods) { - String key= getMethodNameForOverrideKey(method); - List<ICPPMethod> list = result.get(key); - if (list != null) { - final ICPPFunctionType methodType = method.getType(); - for (Iterator<ICPPMethod> it= list.iterator(); it.hasNext(); ) { - ICPPMethod pureVirtual = it.next(); - if (functionTypesAllowOverride(methodType, pureVirtual.getType())) { - it.remove(); + + /** + * For each subobject for which 'method' has been overridden, update + * its final overrider to 'overrider'. + */ + public void replaceForAllSubobjects(ICPPMethod method, ICPPMethod overrider) { + Map<Integer, ICPPMethod> 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<Integer, ICPPMethod> overriders = fMap.get(method); + if (overriders == null) { + overriders = new HashMap<Integer, ICPPMethod>(); + fMap.put(method, overriders); + } + Map<Integer, ICPPMethod> otherOverriders = other.fMap.get(method); + for (Integer i : otherOverriders.keySet()) { + ICPPMethod overrider = otherOverriders.get(i); + overriders.put(i, overrider); } } } - if (method.isPureVirtual()) { - if (list == null) { - list= new ArrayList<ICPPMethod>(); - result.put(key, list); + + /** + * 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<ICPPMethod> pureVirtualMethods = new ArrayList<ICPPMethod>(); + for (ICPPMethod method : fMap.keySet()) { + if (method.isPureVirtual()) { + Map<Integer, ICPPMethod> finalOverriders = fMap.get(method); + for (Integer subobjectNumber : finalOverriders.keySet()) { + ICPPMethod finalOverrider = finalOverriders.get(subobjectNumber); + if (finalOverrider == method) { + pureVirtualMethods.add(method); + } + } + } } - list.add(method); - } else if (list != null && list.isEmpty()) { - result.remove(key); + return pureVirtualMethods.toArray(new ICPPMethod[pureVirtualMethods.size()]); } } - return result; - } + + // 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<ICPPClassType, Integer> subobjectNumbers = new HashMap<ICPPClassType, Integer>(); + + // 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<ICPPClassType, FinalOverriderMap> virtualBaseCache = new HashMap<ICPPClassType, FinalOverriderMap>(); + + public ICPPMethod[] collect(ICPPClassType root, IASTNode point) { + FinalOverriderMap finalOverriderMap = collectFinalOverriders(root, false, new HashSet<ICPPClassType>(), point); + return finalOverriderMap.collectPureVirtualMethods(); + } - private static String getMethodNameForOverrideKey(ICPPMethod method) { - if (method.isDestructor()) { - // Destructor's names may differ but they will override each other. - return DESTRUCTOR_OVERRIDE_KEY; - } else { - return method.getName(); + /** + * 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<ICPPClassType> 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; } } } |