blob: 39d489bb7b9a8dc6e4d3c53bf3446eebcf308f3a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Google, Inc 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTImplicitDestructorName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitDestructorNameOwner;
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.IASTInitializerClause;
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.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable;
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.cpp.CPPASTImplicitDestructorName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper.MethodKind;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPASTInternalScope;
/**
* Finds destructor calls for temporaries and local variables.
*/
public class DestructorCallCollector {
/**
* Returns the implicit names corresponding to the destructor calls for temporaries destroyed at the end
* of the expression. Only expression that is not a subexpression of another expression may contain
* implicit destructor calls.
*
* @param expression the expression to get the destructor calls for
* @return an array of destructor names
*/
public static IASTImplicitDestructorName[] getTemporariesDestructorCalls(ICPPASTExpression expression) {
if (expression.getParent() instanceof ICPPASTExpression)
return IASTImplicitDestructorName.EMPTY_NAME_ARRAY; // Not a full-expression
TemporariesDestructorCollector collector = new TemporariesDestructorCollector(expression);
expression.accept(collector);
return collector.getDestructorCalls();
}
/**
* Returns the implicit names corresponding to the destructor calls for the local variables destroyed at
* the end of the statement.
*
* @param statement the expression to get the destructor calls for
* @return an array of destructor names
*/
public static IASTImplicitDestructorName[] getLocalVariablesDestructorCalls(IASTStatement statement) {
if (!(statement instanceof IASTImplicitDestructorNameOwner))
return IASTImplicitDestructorName.EMPTY_NAME_ARRAY;
LocalVariablesDestructorCollector collector =
new LocalVariablesDestructorCollector((IASTImplicitDestructorNameOwner) statement);
statement.accept(collector);
return collector.getDestructorCalls();
}
// Not instantiatable. All methods are static.
private DestructorCallCollector() {
}
private abstract static class DestructorCollector extends ASTVisitor {
protected final IASTImplicitDestructorNameOwner owner;
private IASTImplicitDestructorName[] destructorNames = IASTImplicitDestructorName.EMPTY_NAME_ARRAY;
DestructorCollector(IASTImplicitDestructorNameOwner owner) {
this.owner = owner;
}
IASTImplicitDestructorName[] getDestructorCalls() {
destructorNames = ArrayUtil.trim(destructorNames);
return destructorNames;
}
static ICPPMethod findDestructor(ICPPClassType classType) {
return ClassTypeHelper.getMethodInClass(classType, MethodKind.DTOR);
}
protected void addDestructorCall(IASTName name, ICPPMethod destructor) {
CPPASTImplicitDestructorName destructorName =
new CPPASTImplicitDestructorName(destructor.getNameCharArray(), owner, name);
destructorName.setBinding(destructor);
ASTNode parentNode = (ASTNode) owner;
int offset = parentNode.getOffset() + parentNode.getLength();
if (!(owner instanceof ICPPASTExpression))
offset--; // Before the closing brace.
destructorName.setOffsetAndLength(offset, 0);
destructorNames = ArrayUtil.prepend(destructorNames, destructorName);
}
}
private static class TemporariesDestructorCollector extends DestructorCollector {
private TemporariesDestructorCollector(ICPPASTExpression owner) {
super(owner);
shouldVisitImplicitNames = true;
}
@Override
public int visit(IASTName name) {
if (name instanceof IASTImplicitName && !(name.getParent() instanceof ICPPASTNewExpression)) {
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPConstructor) {
CPPSemantics.pushLookupPoint(name);
try {
ICPPClassType classType = ((ICPPConstructor) binding).getClassOwner();
ICPPMethod destructor = findDestructor(classType);
if (destructor != null && !isBoundToVariable(name)) {
addDestructorCall(name, destructor);
}
} finally {
CPPSemantics.popLookupPoint();
}
}
}
return PROCESS_CONTINUE;
}
private boolean isBoundToVariable(IASTName name) {
IASTNode parent = name.getParent();
if (!(parent instanceof IASTExpression))
return false;
parent = parent.getParent();
if (!(parent instanceof IASTBinaryExpression))
return false;
IASTBinaryExpression binExpr = (IASTBinaryExpression) parent;
if (binExpr.getOperator() != IASTBinaryExpression.op_assign)
return false;
IASTExpression left = binExpr.getOperand1();
if (left instanceof IASTIdExpression) {
IASTName name2 = ((IASTIdExpression) left).getName();
IBinding binding = name2.resolveBinding();
if (binding instanceof ICPPVariable) {
return true;
}
}
return false;
}
}
private static class LocalVariablesDestructorCollector extends DestructorCollector {
private LocalVariablesDestructorCollector(IASTImplicitDestructorNameOwner owner) {
super(owner);
shouldVisitNames = true;
shouldVisitImplicitNames = true;
}
@Override
public int visit(IASTName name) {
if (name.getPropertyInParent() == IASTDeclarator.DECLARATOR_NAME) {
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPVariable) {
ICPPVariable var = (ICPPVariable) binding;
try {
IScope scope = var.getScope();
if (scope.getKind() == EScopeKind.eLocal && scope instanceof ICPPASTInternalScope) {
IASTNode scopeNode = ((ICPPASTInternalScope) scope).getPhysicalNode();
if (scopeNode.equals(owner)) {
IType type = SemanticUtil.getNestedType(var.getType(), TDEF | CVTYPE);
CPPSemantics.pushLookupPoint(name);
try {
if (type instanceof ICPPClassType) {
ICPPMethod destructor = findDestructor((ICPPClassType) type);
if (destructor != null) {
addDestructorCall(name, destructor);
}
} else if (type instanceof ICPPReferenceType) {
IASTDeclarator decl = (IASTDeclarator) name.getParent();
addDestructorCallForTemporaryBoundToReference(decl);
}
} finally {
CPPSemantics.popLookupPoint();
}
}
}
} catch (DOMException e) {
CCorePlugin.log(e);
}
}
}
return PROCESS_CONTINUE;
}
private void addDestructorCallForTemporaryBoundToReference(IASTDeclarator decl) {
IASTInitializer initializer = decl.getInitializer();
if (initializer instanceof IASTEqualsInitializer) {
IASTInitializerClause clause = ((IASTEqualsInitializer) initializer).getInitializerClause();
if (clause instanceof IASTImplicitNameOwner) {
IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) clause).getImplicitNames();
if (implicitNames.length != 0) {
IASTImplicitName name = implicitNames[0];
IBinding binding = name.resolveBinding();
if (binding instanceof ICPPConstructor) {
ICPPClassType classType = ((ICPPConstructor) binding).getClassOwner();
ICPPMethod destructor = findDestructor(classType);
if (destructor != null) {
addDestructorCall(name, destructor);
}
}
}
}
}
}
}
}