/* * Copyright (c) 2014, 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.ArrayList; import java.util.Collection; import java.util.Collections; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IBasicType; import org.eclipse.cdt.core.dom.ast.IEnumeration; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter; /** * Extracts required information from FunctionCallExpressions that call * QObject::connect. This implementation handles all overloads of QObject::connect * except the QMetaMethod related ones. QMetaMethods cannot be statically analyzed * so they are ignored. * <p> * The binding is found by identifying the overload and then looking at the appropriate * parameters. */ public class QtFunctionCall { private QtFunctionCall() { } /** * Returns a collection of all Qt method references within the given function call. Returns * null if there are no Qt method references. */ public static Collection<QtMethodReference> getReferences(IASTFunctionCallExpression call) { ICPPFunction function = ASTUtil.resolveFunctionBinding(ICPPFunction.class, call); if (function == null) return null; if (QtKeywords.is_QObject_connect(function)) return getReferencesInConnect(function, call); if (QtKeywords.is_QObject_disconnect(function)) return getReferencesInDisconnect(function, call); return null; } private static Collection<QtMethodReference> getReferencesInConnect(ICPPFunction function, IASTFunctionCallExpression call) { if (function == null) return null; // There are 3 overloads of QObject::connect (Qt 4.8.4). They can be // distinguished by examining // the type of the forth parameter. // connect( , , , const char *, ) // connect( , , , QMetaMethod&, ) // connect( , , , Qt::ConnectionType = ) ICPPParameter[] params = function.getParameters(); if (params.length < 4) return null; IASTInitializerClause[] args = call.getArguments(); IType type3 = ASTUtil.getBaseType(params[3].getType()); // static bool connect( const QObject *sender, const QMetaMethod &signal, // const QObject *receiver, const QMetaMethod &method, // Qt::ConnectionType type = Qt::AutoConnection ) if (QtKeywords.isQMetaMethod(type3)) { // QMetaMethod cannot be statically analyzed. return null; } // Otherwise find the signal and member parameters based on the overload. QtMethodReference signal = null; QtMethodReference member = null; // static bool connect( const QObject *sender, const char *signal, // const QObject *receiver, const char *member, // Qt::ConnectionType = Qt::AutoConnection ); if (type3 instanceof IBasicType && ((IBasicType) type3).getKind() == IBasicType.Kind.eChar) { signal = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 0)), safeArgsAt(args, 1)); member = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 2)), safeArgsAt(args, 3)); } // inline bool connect( const QObject *sender, const char *signal, // const char *member, // Qt::ConnectionType type = Qt::AutoConnection ) const; else if (type3 instanceof IEnumeration) { signal = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 0)), safeArgsAt(args, 1)); member = QtMethodReference.parse(call, ASTUtil.getReceiverType(call), safeArgsAt(args, 2)); } return mergeNonNull(signal, member); } private static Collection<QtMethodReference> getReferencesInDisconnect(ICPPFunction function, IASTFunctionCallExpression call) { if (function == null) return null; // There are 4 overloads of QObject::disconnect (Qt 4.8.4). They can be distinguished by examining // the type of the second parameter. The number of parameters is used to disambiguate one conflict. // disconnect( , const char *, , ) && 4 params // disconnect( , QMetaMethod&, , ) // disconnect( , const QObject *, ) // disconnect( , const char * ) && 2 params ICPPParameter[] params = function.getParameters(); if (params.length < 2) return null; IASTInitializerClause[] args = call.getArguments(); IType type1 = ASTUtil.getBaseType(params[1].getType()); // static bool disconnect( const QObject *sender, const QMetaMethod &signal, // const QObject *receiver, const QMetaMethod &member ); if (QtKeywords.isQMetaMethod(type1)) { // QMetaMethod cannot be statically analyzed. return Collections.emptyList(); } // Otherwise find the signal and member parameters based on the overload. QtMethodReference signal = null; QtMethodReference member = null; if (type1 instanceof IBasicType && ( (IBasicType)type1 ).getKind() == IBasicType.Kind.eChar ) { switch(params.length) { // static bool disconnect( const QObject *sender, const char *signal, // const QObject *receiver, const char *member ); case 4: signal = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 0)), safeArgsAt(args, 1)); member = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 2)), safeArgsAt(args, 3)); break; // inline bool disconnect( const QObject *receiver, const char *member = 0 ); case 2: member = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 0)), safeArgsAt(args, 1)); break; } } // inline bool disconnect( const char *signal = 0, // const QObject *receiver = 0, const char *member = 0 ); else if (QtKeywords.isQObject(type1)) { ICPPClassType recvr = ASTUtil.getReceiverType(call); signal = QtMethodReference.parse(call, recvr, safeArgsAt(args, 0)); member = QtMethodReference.parse(call, ASTUtil.getBaseType(safeArgsAt(args, 1)), safeArgsAt(args, 2)); } return mergeNonNull(signal, member); } private static IASTNode safeArgsAt(IASTNode[] args, int index) { return args.length > index ? args[index] : null; } private static <T> Collection<T> mergeNonNull(T...withNulls) { T firstNonNull = null; ArrayList<T> list = null; for(T t : withNulls) { if (t == null) continue; else if(list != null) list.add(t); else if(firstNonNull == null) firstNonNull = t; else { list = new ArrayList<T>(withNulls.length); list.add(firstNonNull); list.add(t); } } if (list != null) return list; if (firstNonNull != null) return Collections.singletonList(firstNonNull); return null; } }