1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-24 09:25:31 +02:00

Bug 351612 - Pure virtual implementation not recognized if multiply

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>
This commit is contained in:
Nathan Ridge 2013-07-24 02:09:31 -04:00 committed by Sergey Prigogin
parent b947a9816c
commit 5fb92e3cf8
3 changed files with 240 additions and 91 deletions

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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;
@ -864,32 +862,6 @@ public class ClassTypeHelper {
return null;
}
/**
* 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));
}
}
}
// 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();
}
}
}
if (method.isPureVirtual()) {
if (list == null) {
list= new ArrayList<ICPPMethod>();
result.put(key, list);
}
list.add(method);
} else if (list != null && list.isEmpty()) {
result.remove(key);
}
}
return result;
/**
* 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);
}
/**
* 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);
}
}
}
/**
* 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);
}
}
}
}
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<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;
}
}
}