1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 22:52:11 +02:00

Detection of implicit constructor calls in declarators and initializers. Bug 248854.

This commit is contained in:
Sergey Prigogin 2010-04-11 19:08:38 +00:00
parent 28ffaab1ff
commit d596dcb716
6 changed files with 226 additions and 26 deletions

View file

@ -7,6 +7,7 @@
*
* Contributors:
* Mike Kucera (IBM)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2;
@ -16,8 +17,10 @@ import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.parser.ParserLanguage;
/**
@ -277,7 +280,7 @@ public class AST2CPPImplicitNameTests extends AST2BaseTest {
// bool b = true;
// x(b); // 1
// x(); // 2
// x(1,2); // 3
// x(1, 2); // 3
// }
public void testFunctionCallOperator() throws Exception {
BindingAssertionHelper ba= new BindingAssertionHelper(getAboveComment(), true);
@ -301,7 +304,7 @@ public class AST2CPPImplicitNameTests extends AST2BaseTest {
assertTrue(n2.isAlternate());
assertSame(col.getName(3).resolveBinding(), n1.resolveBinding());
n1 = ba.assertImplicitName("(1,2); // 3", 1, ICPPMethod.class);
n1 = ba.assertImplicitName("(1, 2); // 3", 1, ICPPMethod.class);
n2 = ba.assertImplicitName("); // 3", 1, ICPPMethod.class);
assertSame(n1.resolveBinding(), n2.resolveBinding());
assertFalse(n1.isAlternate());
@ -416,18 +419,18 @@ public class AST2CPPImplicitNameTests extends AST2BaseTest {
// int test() {
// X* fp = new (nothrow) X;
// int* p = new (nothrow) int[5];
// int* p2 = new (5,6) int[5];
// int* p2 = new (5, 6) int[5];
// }
public void testNew() throws Exception {
BindingAssertionHelper ba= new BindingAssertionHelper(getAboveComment(), true);
IASTImplicitName n1 = ba.assertImplicitName("new (nothrow) X", 3, ICPPFunction.class);
IASTImplicitName n2 = ba.assertImplicitName("new (nothrow) int", 3, ICPPFunction.class);
IASTImplicitName n3 = ba.assertImplicitName("new (5,6) int", 3, ICPPFunction.class);
IASTImplicitName n3 = ba.assertImplicitName("new (5, 6) int", 3, ICPPFunction.class);
IASTTranslationUnit tu = ba.getTranslationUnit();
CPPNameCollector col = new CPPNameCollector();
tu.accept(col);
assertSame(col.getName(4).resolveBinding(), n1.resolveBinding());
assertSame(col.getName(9).resolveBinding(), n2.resolveBinding());
assertSame(col.getName(14).resolveBinding(), n3.resolveBinding());
@ -440,4 +443,56 @@ public class AST2CPPImplicitNameTests extends AST2BaseTest {
BindingAssertionHelper ba= new BindingAssertionHelper(getAboveComment(), true);
ba.assertNoImplicitName("throw;", 5);
}
// struct A {
// A() {}
// A(int) {}
// template <typename T>
// A(T, int) {}
// };
// typedef A B;
//
// void test() {
// B a;
// B b(1);
// B c = 1;
// B d("", 1);
// extern B e;
// }
//
// struct C {
// static B s = 1;
// static B t;
// B u;
// B v;
// C(int p) : u(), v(p) {}
// };
// B C::t = 1;
public void testConstructorCall() throws Exception {
BindingAssertionHelper ba= new BindingAssertionHelper(getAboveComment(), true);
IASTTranslationUnit tu = ba.getTranslationUnit();
ICPPConstructor ctor0 = ba.assertNonProblem("A()", 1, ICPPConstructor.class);
ICPPConstructor ctor1 = ba.assertNonProblem("A(int)", 1, ICPPConstructor.class);
ICPPConstructor ctor2 = ba.assertNonProblem("A(T, int)", 1, ICPPConstructor.class);
IASTImplicitName a = ba.assertImplicitName("a;", 1, ICPPConstructor.class);
assertSame(ctor0, a.resolveBinding());
IASTImplicitName b = ba.assertImplicitName("b(", 1, ICPPConstructor.class);
assertSame(ctor1, b.resolveBinding());
IASTImplicitName c = ba.assertImplicitName("c =", 1, ICPPConstructor.class);
assertSame(ctor1, c.resolveBinding());
IASTImplicitName d = ba.assertImplicitName("d(", 1, ICPPConstructor.class);
assertSame(ctor2, ((ICPPTemplateInstance) d.resolveBinding()).getTemplateDefinition());
ba.assertNoImplicitName("e;", 1);
IASTImplicitName s = ba.assertImplicitName("s =", 1, ICPPConstructor.class);
assertSame(ctor1, s.resolveBinding());
ba.assertNoImplicitName("t;", 1);
IASTImplicitName t = ba.assertImplicitName("t =", 1, ICPPConstructor.class);
assertSame(ctor1, t.resolveBinding());
ba.assertNoImplicitName("u;", 1);
IASTImplicitName u = ba.assertImplicitName("u()", 1, ICPPConstructor.class);
assertSame(ctor0, u.resolveBinding());
IASTImplicitName v = ba.assertImplicitName("v(p)", 1, ICPPConstructor.class);
assertSame(ctor1, v.resolveBinding());
}
}

View file

@ -174,10 +174,11 @@ public class IndexNamesTests extends BaseTestCase {
assertNull(main.getEnclosingDefinition());
IIndexName[] enclosed= main.getEnclosedNames();
assertLength(3, enclosed);
assertName("C", enclosed[0]);
assertName("func", enclosed[1]);
assertName("var", enclosed[2]);
assertLength(4, enclosed);
assertName("C", enclosed[0]); // Class reference
assertName("C", enclosed[1]); // Implicit ctor call
assertName("func", enclosed[2]);
assertName("var", enclosed[3]);
IIndexName enclosing= enclosed[0].getEnclosingDefinition();
assertNotNull(enclosing);
@ -189,9 +190,13 @@ public class IndexNamesTests extends BaseTestCase {
enclosing= enclosed[2].getEnclosingDefinition();
assertNotNull(enclosing);
assertName("main", enclosing);
enclosing= enclosed[3].getEnclosingDefinition();
assertNotNull(enclosing);
assertName("main", enclosing);
IIndexBinding funcB= fIndex.findBinding(enclosed[1]);
IIndexBinding funcB= fIndex.findBinding(enclosed[2]);
assertNotNull(funcB);
names= fIndex.findDefinitions(funcB);
assertLength(1, names);

View file

@ -8,6 +8,7 @@
* Contributors:
* John Camelon (IBM) - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
@ -16,6 +17,8 @@ import java.util.Arrays;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -23,6 +26,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
@ -40,8 +44,9 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
* {@code Base()} and {@code field()} are the constructor chain initializers.<br>
*/
public class CPPASTConstructorChainInitializer extends ASTNode implements
ICPPASTConstructorChainInitializer, IASTCompletionContext {
ICPPASTConstructorChainInitializer, IASTImplicitNameOwner, IASTCompletionContext {
private IASTName name;
private IASTImplicitName[] implicitNames;
private IASTInitializer initializer;
private boolean fIsPackExpansion;
@ -101,6 +106,13 @@ public class CPPASTConstructorChainInitializer extends ASTNode implements
if (name != null && !name.accept(action))
return false;
if (action.shouldVisitImplicitNames) {
for (IASTImplicitName implicitName : getImplicitNames()) {
if (!implicitName.accept(action))
return false;
}
}
if (initializer != null && !initializer.accept(action))
return false;
@ -154,7 +166,6 @@ public class CPPASTConstructorChainInitializer extends ASTNode implements
for (IASTNode parent = name.getParent(); parent != null; parent = parent.getParent()) {
if (parent instanceof ICPPASTCompositeTypeSpecifier) {
ICPPASTCompositeTypeSpecifier specifier = (ICPPASTCompositeTypeSpecifier) parent;
return specifier.getBaseSpecifiers();
}
}
@ -201,4 +212,27 @@ public class CPPASTConstructorChainInitializer extends ASTNode implements
setInitializer(ctorInit);
}
}
/**
* @see IASTImplicitNameOwner#getImplicitNames()
*/
public IASTImplicitName[] getImplicitNames() {
if (implicitNames == null) {
ICPPConstructor ctor = CPPSemantics.findImplicitlyCalledConstructor(this);
if (ctor == null) {
implicitNames = IASTImplicitName.EMPTY_NAME_ARRAY;
} else {
CPPASTImplicitName ctorName = new CPPASTImplicitName(ctor.getNameCharArray(), this);
ctorName.setBinding(ctor);
IASTName id = name;
if (id instanceof ICPPASTQualifiedName) {
id = ((ICPPASTQualifiedName) id).getLastName();
}
ctorName.setOffsetAndLength((ASTNode) id);
implicitNames = new IASTImplicitName[] { ctorName };
}
}
return implicitNames;
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2009 IBM Corporation and others.
* Copyright (c) 2004, 2010 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
@ -8,6 +8,7 @@
* Contributors:
* John Camelon (IBM) - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
@ -17,6 +18,8 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -26,22 +29,26 @@ import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLinkageSpecification;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
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.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
/**
* C++ specific declarator.
*/
public class CPPASTDeclarator extends ASTNode implements ICPPASTDeclarator {
public class CPPASTDeclarator extends ASTNode implements ICPPASTDeclarator, IASTImplicitNameOwner {
private IASTInitializer initializer;
private IASTName name;
private IASTImplicitName[] implicitNames;
private IASTDeclarator nested;
private IASTPointerOperator[] pointerOps = null;
private IASTPointerOperator[] pointerOps;
private boolean isPackExpansion;
public CPPASTDeclarator() {
}
@ -139,7 +146,7 @@ public class CPPASTDeclarator extends ASTNode implements ICPPASTDeclarator {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default : break;
default: break;
}
}
@ -155,13 +162,19 @@ public class CPPASTDeclarator extends ASTNode implements ICPPASTDeclarator {
if (nested == null && name != null) {
IASTDeclarator outermost= ASTQueries.findOutermostDeclarator(this);
if (outermost.getPropertyInParent() != IASTTypeId.ABSTRACT_DECLARATOR) {
if (!name.accept(action)) return false;
if (!name.accept(action))
return false;
if (action.shouldVisitImplicitNames) {
for (IASTImplicitName implicitName : getImplicitNames()) {
if (!implicitName.accept(action))
return false;
}
}
}
}
if (nested != null) {
if (!nested.accept(action)) return false;
}
if (nested != null && !nested.accept(action))
return false;
if (!postAccept(action))
return false;
@ -233,4 +246,27 @@ public class CPPASTDeclarator extends ASTNode implements ICPPASTDeclarator {
}
return false;
}
/**
* @see IASTImplicitNameOwner#getImplicitNames()
*/
public IASTImplicitName[] getImplicitNames() {
if (implicitNames == null) {
ICPPConstructor ctor = CPPSemantics.findImplicitlyCalledConstructor(this);
if (ctor == null) {
implicitNames = IASTImplicitName.EMPTY_NAME_ARRAY;
} else {
CPPASTImplicitName ctorName = new CPPASTImplicitName(ctor.getNameCharArray(), this);
ctorName.setBinding(ctor);
IASTName id = name;
if (id instanceof ICPPASTQualifiedName) {
id = ((ICPPASTQualifiedName) id).getLastName();
}
ctorName.setOffsetAndLength((ASTNode) id);
implicitNames = new IASTImplicitName[] { ctorName };
}
}
return implicitNames;
}
}

View file

@ -89,7 +89,7 @@ public class CPPASTDeleteExpression extends ASTNode implements ICPPASTDeleteExpr
List<IASTImplicitName> names = new ArrayList<IASTImplicitName>();
if (!isVectored) {
ICPPFunction destructor = CPPSemantics.findDestructor(this);
ICPPFunction destructor = CPPSemantics.findImplicitlyCalledDestructor(this);
if (destructor != null) {
CPPASTImplicitName destructorName = new CPPASTImplicitName(destructor.getNameCharArray(), this);
destructorName.setBinding(destructor);
@ -121,7 +121,7 @@ public class CPPASTDeleteExpression extends ASTNode implements ICPPASTDeleteExpr
@Override
public boolean accept(ASTVisitor action) {
if (action.shouldVisitExpressions) {
switch(action.visit(this)) {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default: break;
@ -129,7 +129,7 @@ public class CPPASTDeleteExpression extends ASTNode implements ICPPASTDeleteExpr
}
if (action.shouldVisitImplicitNames) {
for(IASTImplicitName name : getImplicitNames()) {
for (IASTImplicitName name : getImplicitNames()) {
if (!name.accept(action))
return false;
}

View file

@ -158,6 +158,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.IASTInternalScope;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFieldReference;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLiteralExpression;
@ -2762,7 +2763,76 @@ public class CPPSemantics {
return null;
}
public static ICPPFunction findDestructor(ICPPASTDeleteExpression expr) {
/**
* Returns constructor called by a declarator, or <code>null</code> if no constructor is called.
*/
public static ICPPConstructor findImplicitlyCalledConstructor(CPPASTDeclarator declarator) {
if (declarator.getInitializer() == null) {
IASTNode parent = declarator.getParent();
if (parent instanceof IASTSimpleDeclaration) {
IASTDeclSpecifier declSpec = ((IASTSimpleDeclaration) parent).getDeclSpecifier();
parent = parent.getParent();
if (parent instanceof IASTCompositeTypeSpecifier ||
declSpec.getStorageClass() == IASTDeclSpecifier.sc_extern) {
// No initialization is performed for class members and extern declarations
// without an initializer.
return null;
}
}
}
return findImplicitlyCalledConstructor(declarator.getName(), declarator.getInitializer());
}
/**
* Returns constructor called by a class member initializer in a constructor initializer chain.
* Returns <code>null</code> if no constructor is called.
*/
public static ICPPConstructor findImplicitlyCalledConstructor(ICPPASTConstructorChainInitializer initializer) {
return findImplicitlyCalledConstructor(initializer.getMemberInitializerId(), initializer.getInitializer());
}
/**
* Returns constructor called by a variable declarator or an initializer in a constructor initializer
* chain. Returns <code>null</code> if no constructor is called.
*/
private static ICPPConstructor findImplicitlyCalledConstructor(IASTName name, IASTInitializer initializer) {
IBinding binding = name.resolveBinding();
if (!(binding instanceof ICPPVariable))
return null;
IType type;
try {
type = SemanticUtil.getSimplifiedType(((ICPPVariable) binding).getType());
if (!(type instanceof ICPPClassType))
return null;
ICPPClassType classType = (ICPPClassType) type;
CPPASTName astName = new CPPASTName();
astName.setName(classType.getNameCharArray());
astName.setOffsetAndLength((ASTNode) name);
CPPASTIdExpression idExp = new CPPASTIdExpression(astName);
idExp.setParent(name.getParent());
idExp.setPropertyInParent(IASTFunctionCallExpression.FUNCTION_NAME);
LookupData data = new LookupData(astName);
if (initializer == null) {
data.setFunctionArguments(IASTExpression.EMPTY_EXPRESSION_ARRAY);
} else if (initializer instanceof IASTEqualsInitializer) {
data.setFunctionArguments(((IASTEqualsInitializer) initializer).getInitializerClause());
} else if (initializer instanceof ICPPASTConstructorInitializer) {
data.setFunctionArguments(((ICPPASTConstructorInitializer) initializer).getArguments());
} else {
return null;
}
data.forceQualified = true;
data.foundItems = classType.getConstructors();
binding = resolveAmbiguities(data, astName);
if (binding instanceof ICPPConstructor)
return (ICPPConstructor) binding;
} catch (DOMException e) {
}
return null;
}
public static ICPPFunction findImplicitlyCalledDestructor(ICPPASTDeleteExpression expr) {
ICPPClassType cls = getNestedClassType(expr);
if (cls == null)
return null;