/* * 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; } } }