| /* |
| * 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.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.cdt.core.dom.ast.IASTCompletionContext; |
| import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; |
| import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.IType; |
| |
| /** |
| * Utility for managing interaction with QObject::connect and QObject::disconnect function |
| * calls. These function calls can contain two expansions. The first is always SIGNAL, |
| * the second is either SIGNAL (which will cause the second signal to be emitted when the |
| * first is received) or SLOT (which will cause the slot to be evaluate when the signal |
| * is received). This class follows the Qt convention of calling the first SIGNAL expansion |
| * the sender and the second (which could be SIGNAL or SLOT) the receiver. |
| * |
| * In the following examples, the type of the signal is the type of the q_sender variable. |
| * The type of the method is the type of the q_receiver variable. The variable q_unrelated is |
| * some instance that is not needed for either case. |
| * <pre> |
| * QObject::connect( q_sender, SIGNAL(destroyed()), q_receiver, SIGNAL() ); |
| * QObject::connect( q_sender, SIGNAL(destroyed()), q_receiver, SLOT(deleteLater()) ); |
| * QObject::connect( q_sender, SIGNAL(destroyed()), q_receiver, SIGNAL(), Qt::AutoConnection ); |
| * QObject::connect( q_sender, SIGNAL(destroyed()), q_receiver, SLOT(deleteLater()), Qt::AutoConnection ); |
| * q_unrelated->connect( q_sender, SIGNAL(destroyed()), q_receiver, SIGNAL() ); |
| * q_unrelated->connect( q_sender, SIGNAL(destroyed()), q_receiver, SLOT(deleteLater()) ); |
| * q_unrelated->connect( q_sender, SIGNAL(destroyed()), q_receiver, SIGNAL(), Qt::AutoConnection ); |
| * q_unrelated->connect( q_sender, SIGNAL(destroyed()), q_receiver, SLOT(deleteLater()), Qt::AutoConnection ); |
| * |
| * q_receiver->connect( q_sender, SIGNAL(destroyed()), SIGNAL() ); |
| * q_receiver->connect( q_sender, SIGNAL(destroyed()), SLOT() ); |
| * q_receiver->connect( q_sender, SIGNAL(destroyed()), SIGNAL(), Qt::AutoConnection ); |
| * q_receiver->connect( q_sender, SIGNAL(destroyed()), SLOT(), Qt::AutoConnection ); |
| * |
| * QObject::disconnect( q_sender, SIGNAL(), q_receiver, SIGNAL() ); |
| * QObject::disconnect( q_sender, SIGNAL(), q_receiver, SLOT() ); |
| * q_unrelated->disconnect( q_sender, SIGNAL(), q_receiver, SIGNAL() ); |
| * q_unrelated->disconnect( q_sender, SIGNAL(), q_receiver, SLOT() ); |
| * |
| * q_sender->disconnect( SIGNAL(), q_receiver, SIGNAL() ); |
| * q_sender->disconnect( SIGNAL(), q_receiver, SLOT() ); |
| * q_sender->disconnect( SIGNAL(), q_receiver ); |
| * q_sender->disconnect( SIGNAL() ); |
| * q_sender->disconnect(); |
| * </pre> |
| */ |
| public class QtFunctionCallUtil { |
| |
| private static final Pattern SignalRegex = Pattern.compile("^\\s*" + QtKeywords.SIGNAL + ".*"); |
| private static final Pattern MethodRegex = Pattern.compile("^\\s*(?:" + QtKeywords.SIGNAL + '|' + QtKeywords.SLOT + ").*"); |
| |
| /** |
| * Return true if the specified name is a QObject::connect or QObject::disconnect function |
| * and false otherwise. |
| */ |
| public static boolean isQObjectFunctionCall(IASTCompletionContext astContext, boolean isPrefix, IASTName name) { |
| if (name == null |
| || name.getSimpleID() == null |
| || name.getSimpleID().length <= 0) |
| return false; |
| |
| // Bug332201: Qt content assist should always be applied to the most specific part of |
| // the target name. |
| IBinding[] funcBindings = astContext.findBindings(name.getLastName(), isPrefix); |
| for (IBinding funcBinding : funcBindings) |
| if (QtKeywords.is_QObject_connect(funcBinding) |
| || QtKeywords.is_QObject_disconnect(funcBinding)) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * If the given argument is a SIGNAL or SLOT expansion then find and return the node in the AST |
| * that will be used for this method. Returns null if the argument is not a Qt method call or |
| * if the associated node cannot be found. |
| */ |
| public static IType getTargetType(IASTFunctionCallExpression call, IASTInitializerClause[] args, int argIndex) { |
| int sigExpIndex = getExpansionArgIndex(args, 0, SignalRegex); |
| if (argIndex == sigExpIndex) |
| return getSignalTargetType(sigExpIndex, call, args); |
| |
| int methodExpIndex = getExpansionArgIndex(args, sigExpIndex + 1, MethodRegex); |
| if (argIndex == methodExpIndex) |
| return getMethodTargetType(methodExpIndex, sigExpIndex, call, args); |
| |
| // Otherwise the given argument is not a SIGNAL or SLOT expansion. |
| return null; |
| } |
| |
| private static IType getSignalTargetType(int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) { |
| // When the SIGNAL expansion is first, the type is based on the receiver of |
| // the function call. Otherwise the type is the previous argument. |
| return ASTUtil.getBaseType(sigExpIndex == 0 ? call : args[sigExpIndex - 1]); |
| } |
| |
| private static IType getMethodTargetType(int methodExpIndex, int sigExpIndex, IASTFunctionCallExpression call, IASTInitializerClause[] args) { |
| // If the method is right after the signal, then the type is based on the receiver |
| // of the function call. Otherwise the method type is based on the parameter right |
| // before the expansion. |
| if (methodExpIndex == (sigExpIndex + 1)) |
| return ASTUtil.getReceiverType(call); |
| return ASTUtil.getBaseType(args[methodExpIndex - 1]); |
| } |
| |
| private static int getExpansionArgIndex(IASTInitializerClause[] args, int begin, Pattern macroNameRegex) { |
| for(int i = begin; i < args.length; ++i) { |
| IASTInitializerClause arg = args[i]; |
| String raw = arg.getRawSignature(); |
| Matcher m = macroNameRegex.matcher(raw); |
| if (m.matches()) |
| return i; |
| } |
| return -1; |
| } |
| } |