summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Ridge2013-07-24 02:09:31 (EDT)
committerSergey Prigogin2013-07-28 15:35:20 (EDT)
commit5fb92e3cf8cff344cd447234f6c2d6dc5239a642 (patch)
treed428976590615f5069377fd40adc9838a97a7a6f
parentb947a9816cad4e5266e5b423238d1ce28ec8d642 (diff)
downloadorg.eclipse.cdt-5fb92e3cf8cff344cd447234f6c2d6dc5239a642.zip
org.eclipse.cdt-5fb92e3cf8cff344cd447234f6c2d6dc5239a642.tar.gz
org.eclipse.cdt-5fb92e3cf8cff344cd447234f6c2d6dc5239a642.tar.bz2
Bug 351612 - Pure virtual implementation not recognized if multiplyrefs/changes/06/14906/4
inherited Change-Id: I1e9141fbb5cc72bb7b59b77d6faf958726094e5f Signed-off-by: Nathan Ridge <zeratul976@hotmail.com> Reviewed-on: https://git.eclipse.org/r/14906 Reviewed-by: Sergey Prigogin <eclipse.sprigogin@gmail.com> IP-Clean: Sergey Prigogin <eclipse.sprigogin@gmail.com> Tested-by: Sergey Prigogin <eclipse.sprigogin@gmail.com>
-rw-r--r--codan/org.eclipse.cdt.codan.core.test/src/org/eclipse/cdt/codan/core/internal/checkers/AbstractClassInstantiationCheckerTest.java54
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/SemanticQueries.java3
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/ClassTypeHelper.java260
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 207fa0f..df33e42 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 523fd65..36cb039 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 21d1520..ea80b71 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;
}
}
}