From b0d8f8d3f16ac89eacc8ed07d4dcbe0e08bc9cac Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Thu, 23 Jul 2015 21:47:19 -0400 Subject: [PATCH] Bug 393717 - Have constructor definitions reference implicitly called base constructors via implicit names Change-Id: Ib42a44488c5f5851a227295f075f028ff1aa7ded Signed-off-by: Nathan Ridge --- .../core/parser/tests/ast2/AST2CPPTests.java | 53 +++++++++++ .../core/parser/tests/ast2/AST2TestBase.java | 13 +++ .../parser/cpp/CPPASTFunctionDefinition.java | 88 ++++++++++++++++++- .../core/dom/parser/cpp/ClassTypeHelper.java | 44 ++++++++-- 4 files changed, 191 insertions(+), 7 deletions(-) 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 87bd065f7c2..69a35126ab4 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 @@ -11573,4 +11573,57 @@ public class AST2CPPTests extends AST2TestBase { // this test will need to be updated. helper.assertVariableValue("generic_lambdas_supported", 0); } + + // struct A { + // A() {} + // }; + // + // struct B { + // B() {} + // }; + // + // struct C : A { + // C() {} + // }; + // + // struct D : virtual A, virtual B { + // D() {} + // }; + // + // struct E { + // E() {} + // }; + // + // struct F : D, virtual E { + // F() {} + // }; + public void testImplicitlyCalledBaseConstructor_393717() throws Exception { + BindingAssertionHelper helper = getAssertionHelper(); + + ICPPConstructor aCtor = helper.assertNonProblem("A()", "A"); + ICPPConstructor bCtor = helper.assertNonProblem("B()", "B"); + ICPPConstructor dCtor = helper.assertNonProblem("D()", "D"); + ICPPConstructor eCtor = helper.assertNonProblem("E()", "E"); + + ICPPASTFunctionDefinition ctorDef = helper.assertNode("C() {}"); + IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) ctorDef).getImplicitNames(); + assertEquals(1, implicitNames.length); + assertEquals(aCtor, implicitNames[0].resolveBinding()); + + ctorDef = helper.assertNode("D() {}"); + implicitNames = ((IASTImplicitNameOwner) ctorDef).getImplicitNames(); + sortNames(implicitNames); + assertEquals(2, implicitNames.length); + assertEquals(aCtor, implicitNames[0].resolveBinding()); + assertEquals(bCtor, implicitNames[1].resolveBinding()); + + ctorDef = helper.assertNode("F() {}"); + implicitNames = ((IASTImplicitNameOwner) ctorDef).getImplicitNames(); + sortNames(implicitNames); + assertEquals(4, implicitNames.length); + assertEquals(aCtor, implicitNames[0].resolveBinding()); + assertEquals(bCtor, implicitNames[1].resolveBinding()); + assertEquals(dCtor, implicitNames[2].resolveBinding()); + assertEquals(eCtor, implicitNames[3].resolveBinding()); + } } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java index d81d29936ea..f9adac7c4c8 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TestBase.java @@ -20,6 +20,8 @@ import static org.eclipse.cdt.core.parser.ParserLanguage.CPP; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -913,4 +915,15 @@ public class AST2TestBase extends BaseTestCase { assertInstance(stmt, IASTExpressionStatement.class); return (T) ((IASTExpressionStatement) stmt).getExpression(); } + + /** + * Sort the given array of AST names lexicographically. + */ + protected static void sortNames(T[] names) { + Arrays.sort(names, new Comparator() { + @Override + public int compare(IASTName a, IASTName b) { + return a.toString().compareTo(b.toString()); + }}); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTFunctionDefinition.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTFunctionDefinition.java index acafc5315b1..42e713a2ff3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTFunctionDefinition.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTFunctionDefinition.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2014 IBM Corporation and others. + * Copyright (c) 2004, 2015 IBM Corporation 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 @@ -12,18 +12,30 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; +import java.util.HashSet; +import java.util.Set; + import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTImplicitName; +import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner; +import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IScope; +import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.dom.parser.ASTAttributeOwner; +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; @@ -32,11 +44,12 @@ import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; * it may contain member initializers. */ public class CPPASTFunctionDefinition extends ASTAttributeOwner - implements ICPPASTFunctionDefinition, IASTAmbiguityParent { + implements ICPPASTFunctionDefinition, IASTAmbiguityParent, IASTImplicitNameOwner { private IASTDeclSpecifier declSpecifier; private IASTFunctionDeclarator declarator; private IASTStatement bodyStatement; private ICPPASTConstructorChainInitializer[] memInits; + private IASTImplicitName[] implicitNames; // for constructors: base constructors called implicitly private int memInitPos= -1; private boolean fDeleted; private boolean fDefaulted; @@ -198,6 +211,14 @@ public class CPPASTFunctionDefinition extends ASTAttributeOwner if (!memInit.accept(action)) return false; } + + if (action.shouldVisitImplicitNames) { + for (IASTImplicitName implicitName : getImplicitNames()) { + if (!implicitName.accept(action)) { + return false; + } + } + } if (bodyStatement != null && !bodyStatement.accept(action)) return false; @@ -226,4 +247,67 @@ public class CPPASTFunctionDefinition extends ASTAttributeOwner bodyStatement = (IASTStatement) other; } } + + @Override + public IASTImplicitName[] getImplicitNames() { + if (implicitNames == null) { + implicitNames = IASTImplicitName.EMPTY_NAME_ARRAY; + IASTName functionName = ASTQueries.findInnermostDeclarator(declarator).getName(); + IBinding function = functionName.resolveBinding(); + if (function instanceof ICPPConstructor) { + ICPPClassType classOwner = ((ICPPConstructor) function).getClassOwner(); + + // Determine the bases of 'classOwner' that need to be initialized by this constructor. + Set basesThatNeedInitialization = new HashSet<>(); + for (ICPPBase base : ClassTypeHelper.getBases(classOwner, this)) { + IType baseType = base.getBaseClassType(); + if (baseType instanceof ICPPClassType) { + basesThatNeedInitialization.add((ICPPClassType) baseType); + } + } + for (ICPPClassType virtualBase : ClassTypeHelper.getVirtualBases(classOwner, this)) { + basesThatNeedInitialization.add(virtualBase); + } + + // Go through the bases determined above, and see which ones aren't initialized + // explicitly in the mem-initializer list. + for (ICPPClassType base : basesThatNeedInitialization) { + if (!isInitializedExplicitly(base)) { + // Try to find a default constructor to create an implicit name for. + for (ICPPConstructor constructor : ClassTypeHelper.getConstructors(base, this)) { + if (constructor.getRequiredArgumentCount() == 0) { // default constructor + CPPASTImplicitName ctorName = new CPPASTImplicitName( + constructor.getNameCharArray(), this); + ctorName.setBinding(constructor); + ctorName.setOffsetAndLength((ASTNode) functionName); + implicitNames = ArrayUtil.append(implicitNames, ctorName); + break; + } + } + } + } + } + implicitNames = ArrayUtil.trim(implicitNames); + } + return implicitNames; + } + + // Returns whether the base type 'base' is explicitly initialized by one of the mem-initializers + // of this constructor. + private boolean isInitializedExplicitly(ICPPClassType base) { + for (ICPPASTConstructorChainInitializer memInitializer : getMemberInitializers()) { + IBinding binding = memInitializer.getMemberInitializerId().resolveBinding(); + if (binding instanceof IType) { + if (((IType) binding).isSameType(base)) { + return true; + } + } + if (binding instanceof ICPPConstructor) { + if (((ICPPConstructor) binding).getClassOwner().isSameType(base)) { + return true; + } + } + } + return false; + } } 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 4d4faada9fb..293ceac6458 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2014 IBM Corporation and others. + * Copyright (c) 2004, 2015 IBM Corporation 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 @@ -24,6 +24,7 @@ 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.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; @@ -284,15 +285,15 @@ public class ClassTypeHelper { * @return An array of base classes in arbitrary order. */ public static ICPPClassType[] getAllBases(ICPPClassType classType, IASTNode point) { - HashSet result= new HashSet<>(); + Set result= new HashSet<>(); result.add(classType); getAllBases(classType, result, point); result.remove(classType); return result.toArray(new ICPPClassType[result.size()]); } - private static void getAllBases(ICPPClassType classType, HashSet result, IASTNode point) { - ICPPBase[] bases= ClassTypeHelper.getBases(classType, point); + private static void getAllBases(ICPPClassType classType, Set result, IASTNode point) { + ICPPBase[] bases= getBases(classType, point); for (ICPPBase base : bases) { IBinding b= base.getBaseClass(); if (b instanceof ICPPClassType) { @@ -303,7 +304,40 @@ public class ClassTypeHelper { } } } - + + /** + * Returns all (direct or indirect) virtual base classes of 'classType'. + * @param point the point of instantiation for name lookups + */ + public static ICPPClassType[] getVirtualBases(ICPPClassType classType, IASTNode point) { + Set result = new HashSet<>(); + result.add(classType); + getVirtualBases(classType, result, point); + result.remove(classType); + return result.toArray(new ICPPClassType[result.size()]); + } + + // Helper function for getVirtualBases(classType, point). + private static void getVirtualBases(ICPPClassType classType, Set result, IASTNode point) { + ICPPBase[] bases = getBases(classType, point); + for (ICPPBase base : bases) { + IBinding b = base.getBaseClass(); + if (b instanceof ICPPClassType) { + final ICPPClassType baseClass = (ICPPClassType) b; + if (base.isVirtual()) { + if (result.add(baseClass)) { + getVirtualBases(baseClass, result, point); + } + } else { + // A non-virtual base could have virtual bases in its hierarchy. + if (!result.contains(baseClass)) { + getVirtualBases(baseClass, result, point); + } + } + } + } + } + /** * Checks inheritance relationship between two classes. * @return {@code true} if {@code subclass} is a subclass of {@code superclass}.