blob: 92e9829b44535be11aa6ae3b4e360c0d6197cbcf [file] [log] [blame]
/*
* Copyright (c) 2013, 2015 QNX Software Systems 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
*/
package org.eclipse.cdt.internal.qt.core;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
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.IASTTranslationUnit;
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.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluationOwner;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.qt.core.index.IQMethod;
import org.eclipse.cdt.internal.qt.core.index.IQObject;
import org.eclipse.core.resources.IProject;
@SuppressWarnings("restriction")
public class ASTUtil {
/**
* A convenience method to find the project that contains the given node. Returns null if
* the project cannot be found.
*/
public static IProject getProject(IASTNode node) {
IASTTranslationUnit astTU = node.getTranslationUnit();
if (astTU == null)
return null;
ITranslationUnit tu = astTU.getOriginatingTranslationUnit();
if (tu == null)
return null;
ICProject cProject = tu.getCProject();
if (cProject == null)
return null;
return cProject.getProject();
}
/**
* Return the fully qualified name of the binding for the given name. Returns null
* if the name has no binding. Tries to resolve the binding if needed.
*/
public static String getFullyQualifiedName(IASTName name) {
return getFullyQualifiedName(name.resolveBinding());
}
/**
* Return the fully qualified name of the given binding. Returns null if there
* is no binding.
*/
public static String getFullyQualifiedName(IBinding binding) {
if (binding == null)
return null;
if (binding instanceof ICPPBinding)
try {
return getFullyQualifiedName(((ICPPBinding) binding).getQualifiedName());
} catch(DOMException e) {
Activator.log(e);
return null;
}
String ownerName = getFullyQualifiedName(binding.getOwner());
return (ownerName == null ? "" : ownerName) + "::" + binding.getName();
}
/**
* Create and return a string representation of the fully qualified name in the
* input array's elements.
*/
public static String getFullyQualifiedName(String[] qualName) {
boolean first = true;
StringBuilder str = new StringBuilder();
for(String name : qualName) {
if (first)
first = false;
else
str.append("::");
str.append(name);
}
return str.toString();
}
// NOTE: This expression allows embedded line terminators (?s) for cases where the code looks like:
// QObject::connect( &a, SIGNAL(
// sig1(
// int
// ), ...
// The regex trims leading and trailing whitespace within the expansion parameter. This is needed
// so that the start of the capture group provides the proper offset into the expansion.
public static final Pattern Regex_MacroExpansion = Pattern.compile("(?s)([_a-zA-Z]\\w*)\\s*\\(\\s*(.*?)\\s*\\)\\s*");
public static IType getBaseType(IType type) {
while (type instanceof ITypeContainer)
type = ((ITypeContainer) type).getType();
return type;
}
public static IType getBaseType(IASTNode node) {
if (node instanceof IASTIdExpression)
return getBaseType((IASTIdExpression) node);
if (node instanceof IASTFunctionCallExpression)
return getBaseType((IASTFunctionCallExpression) node);
if (node instanceof IASTExpression)
return getBaseType(((IASTExpression) node).getExpressionType());
return null;
}
public static IType getBaseType(IASTInitializerClause init) {
if (!(init instanceof ICPPASTInitializerClause))
return null;
ICPPASTInitializerClause cppInit = (ICPPASTInitializerClause) init;
ICPPEvaluation eval = ((ICPPEvaluationOwner)cppInit).getEvaluation();
return eval == null ? null : getBaseType(eval.getType(cppInit));
}
public static ICPPClassType getReceiverType(IASTFunctionCallExpression fncall) {
// If the expression is calling a member function then find the type of the
// receiver.
IASTExpression fnName = fncall.getFunctionNameExpression();
if (fnName instanceof ICPPASTFieldReference) {
ICPPASTFieldReference fieldRef = (ICPPASTFieldReference) fnName;
ICPPASTExpression receiver = fieldRef.getFieldOwner();
IType recvType = getBaseType(receiver);
if (recvType instanceof ICPPClassType)
return (ICPPClassType) recvType;
}
// Otherwise check for a call to implicit 'this'. See details in the thread that
// starts at http://dev.eclipse.org/mhonarc/lists/cdt-dev/msg26972.html
try {
for(IScope scope = CPPVisitor.getContainingScope(fncall); scope != null; scope = scope.getParent())
if (scope instanceof ICPPClassScope)
return ((ICPPClassScope) scope).getClassType();
} catch (DOMException e) {
Activator.log(e);
}
return null;
}
/**
* Does not return null.
*/
public static Collection<IQMethod> findMethods(IQObject qobj, QtMethodReference ref) {
Set<IQMethod> bindings = new LinkedHashSet<IQMethod>();
Iterable<IQMethod> methods = null;
switch(ref.getType())
{
case Signal:
methods = qobj.getSignals().withoutOverrides();
break;
case Slot:
methods = qobj.getSlots().withoutOverrides();
break;
}
if (methods != null) {
String qtNormalizedSig = QtMethodUtil.getQtNormalizedMethodSignature(ref.getRawSignature());
if (qtNormalizedSig == null)
return bindings;
for (IQMethod method : methods)
for(String signature : method.getSignatures())
if (signature.equals(qtNormalizedSig))
bindings.add(method);
}
return bindings;
}
public static <T extends IBinding> T resolveFunctionBinding(Class<T> cls, IASTFunctionCallExpression fnCall) {
IASTName fnName = null;
IASTExpression fnNameExpr = fnCall.getFunctionNameExpression();
if (fnNameExpr instanceof IASTIdExpression)
fnName = ((IASTIdExpression) fnNameExpr).getName();
else if (fnNameExpr instanceof ICPPASTFieldReference)
fnName = ((ICPPASTFieldReference) fnNameExpr).getFieldName();
IBinding binding = fnName == null ? null : fnName.resolveBinding();
if (binding == null)
return null;
return cls.isAssignableFrom(binding.getClass()) ? cls.cast(binding) : null;
}
public static ICPPASTVisibilityLabel findVisibilityLabel(ICPPMethod method, IASTNode ast) {
// the visibility cannot be found without an ast
if (ast == null)
return null;
// We need to get the CompTypeSpec in order to see the token that created the method's
// visibility specifier. The ast parameter will be either the method definition or a
// declaration. If it happens to be a declaration, then the CompTypeSpec is a parent of
// the AST and it can be accessed through public API. However, if the ast parameter happens
// to be a definition, then there isn't any public API (that I've found) to get to the
// CompTypeSpec. Instead, we cheat and use the InternalBinding.
MethodSpec methodSpec = new MethodSpec(ast);
if (methodSpec.clsSpec == null
&& method instanceof ICPPInternalBinding)
{
ICPPInternalBinding internalBinding = (ICPPInternalBinding) method;
IASTNode[] decls = internalBinding.getDeclarations();
for (int i = 0; methodSpec.clsSpec == null && i < decls.length; ++i)
methodSpec = new MethodSpec(decls[i]);
}
if(methodSpec.clsSpec == null)
return null;
ICPPASTVisibilityLabel lastLabel = null;
for (IASTDeclaration decl : methodSpec.clsSpec.getMembers()) {
if (decl instanceof ICPPASTVisibilityLabel)
lastLabel = (ICPPASTVisibilityLabel) decl;
else if (decl == methodSpec.methodDecl)
return lastLabel;
}
return null;
}
private static class MethodSpec
{
public final ICPPASTCompositeTypeSpecifier clsSpec;
public final IASTNode methodDecl;
public MethodSpec( IASTNode node )
{
ICPPASTCompositeTypeSpecifier cls = null;
IASTNode mth = node;
while( mth != null && cls == null )
{
IASTNode parent = mth.getParent();
if (parent instanceof ICPPASTCompositeTypeSpecifier)
cls = (ICPPASTCompositeTypeSpecifier) parent;
else
mth = parent;
}
clsSpec = cls;
methodDecl = mth;
}
}
}