1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 08:55:25 +02:00

Bug 424499: Find References does not work for Qt signals and slots

The QtASTVisitor is trying to use the QtIndex during indexing.  Any
results available at this time are based on the state of the AST the
last time the code was indexed.

This adds a test case to reproduce the problem.  The test cases indexes
the project one time.  It should find two references to the signal.  If
the QtIndex data is stale, then it will find 0 references.

This also replaces the code that looks for QObject::connect function
calls.  The proper behaviour is to find all overloads of #connect as
well as references with QObject::disconnect (all overloads) function
calls.

A new test case checks for references in all overloads of #connect and
#disconnect function calls.

Change-Id: I28fc4213d6dddff10f81a6bd3ef01e24c74f31db
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/20223
Tested-by: Hudson CI
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2014-01-02 14:18:10 -05:00 committed by Doug Schaefer
parent 281d5ddf79
commit a512bbb011
14 changed files with 727 additions and 562 deletions

View file

@ -66,10 +66,9 @@ public class ASTUtil {
// sig1(
// int
// ), ...
// The two patterns are nearly identical. The difference is because the first is for matching SIGNAL/
// SLOT expansions. The second is for matching the argument to that expansion.
public static final Pattern Regex_SignalSlotExpansion = Pattern.compile("(?s)(SIGNAL|SLOT)\\s*\\(\\s*(.*?)\\s*\\)\\s*");
public static final Pattern Regex_FunctionCall = Pattern.compile("(?s)\\s*(.*)\\s*\\(\\s*(.*?)\\s*\\)\\s*");
// 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)
@ -118,11 +117,11 @@ public class ASTUtil {
/**
* Does not return null.
*/
public static Collection<IQMethod> findMethods(IQObject qobj, QtSignalSlotReference ref) {
public static Collection<IQMethod> findMethods(IQObject qobj, QtMethodReference ref) {
Set<IQMethod> bindings = new LinkedHashSet<IQMethod>();
Iterable<IQMethod> methods = null;
switch(ref.type)
switch(ref.getType())
{
case Signal:
methods = qobj.getSignals().withoutOverrides();
@ -133,7 +132,7 @@ public class ASTUtil {
}
if (methods != null) {
String qtNormalizedSig = QtMethodUtil.getQtNormalizedMethodSignature(ref.signature);
String qtNormalizedSig = QtMethodUtil.getQtNormalizedMethodSignature(ref.getRawSignature());
if (qtNormalizedSig == null)
return bindings;
@ -145,7 +144,7 @@ public class ASTUtil {
return bindings;
}
public static IBinding resolveFunctionBinding(IASTFunctionCallExpression fnCall) {
public static <T extends IBinding> T resolveFunctionBinding(Class<T> cls, IASTFunctionCallExpression fnCall) {
IASTName fnName = null;
IASTExpression fnNameExpr = fnCall.getFunctionNameExpression();
if (fnNameExpr instanceof IASTIdExpression)
@ -153,7 +152,11 @@ public class ASTUtil {
else if (fnNameExpr instanceof ICPPASTFieldReference)
fnName = ((ICPPASTFieldReference) fnNameExpr).getFieldName();
return fnName == null ? null : fnName.resolveBinding();
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) {

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2014 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.Arrays;
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.IASTName;
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;
import org.eclipse.cdt.qt.core.QtKeywords;
/**
* 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() {
}
protected static IASTNode safeArgsAt(IASTNode[] args, int index) {
return args.length > index ? args[index] : null;
}
/**
* 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<IASTName> 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<IASTName> 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.
IASTName signal = null;
IASTName 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));
}
// Merge non-null signal and slot to return a list of 0, 1, or 2 elements.
if (signal == null) {
if (member == null)
return null;
return Collections.singletonList(member);
}
if (member == null)
return Collections.singletonList(signal);
return Arrays.asList(signal, member);
}
private static Collection<IASTName> 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.
IASTName signal = null;
IASTName 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));
}
// Merge non-null signal and slot to return a list of 0, 1, or 2 elements.
if (signal == null) {
if (member == null)
return null;
return Collections.singletonList(member);
}
if (member == null)
return Collections.singletonList(signal);
return Arrays.asList(signal, member);
}
}

View file

@ -0,0 +1,179 @@
/*
* Copyright (c) 2014 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 org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.qt.core.pdom.ASTNameReference;
import org.eclipse.cdt.internal.qt.core.pdom.QtASTImageLocation;
import org.eclipse.cdt.qt.core.QtKeywords;
/**
* Qt signals and slots are referenced using the SIGNAL and SLOT macros. The expansion
* parameter is the signature of the signal or slot and they are associated with a type.
* This utility class is used to convert from these AST nodes to an IASTName that can be
* used as a reference to the IBinding for the C++ method.
*/
@SuppressWarnings("restriction")
public class QtMethodReference extends ASTNameReference {
public static enum Type {
Signal("sender", "SIGNAL", "signal"),
Slot("receiver", "SLOT", "member");
public final String roleName;
public final String macroName;
public final String paramName;
public boolean matches(Type other) {
if (other == null)
return false;
// The signal parameter must be a SIGNAL, but the slot could be a
// SLOT or a SIGNAL.
return this == Signal ? other == Signal : true;
}
/**
* Return the type of method reference within the expansion of the given macro name.
*/
public static Type from(IASTName name) {
String nameStr = String.valueOf(name);
if (QtKeywords.SIGNAL.equals(nameStr))
return Signal;
if (QtKeywords.SLOT.equals(nameStr))
return Slot;
return null;
}
private Type(String roleName, String macroName, String paramName) {
this.roleName = roleName;
this.macroName = macroName;
this.paramName = paramName;
}
}
private final Type type;
private final ICPPClassType cls;
private final String expansionParam;
private QtMethodReference(Type type, ICPPClassType cls, IASTName macroRefName, String expansionParam, IASTFileLocation location) {
super(macroRefName, location);
this.type = type;
this.cls = cls;
this.expansionParam = expansionParam;
}
/**
* Look for SIGNAL or SLOT macro expansions at the location of the given node. Return the
* QMethod reference is an expansion is found and null otherwise.
* <p>
* QMetaMethod references cannot be statically resolved so null will be returned in this case.
*/
public static QtMethodReference parse(IASTNode parent, IType cppType, IASTNode arg) {
if (!(cppType instanceof ICPPClassType) || arg == null)
return null;
ICPPClassType cls = (ICPPClassType) cppType;
// Look for a SIGNAL or SLOT expansion as this location.
Type type = null;
IASTName macroReferenceName = null;
for(IASTNodeLocation location : arg.getNodeLocations()) {
if (!(location instanceof IASTMacroExpansionLocation))
continue;
IASTPreprocessorMacroExpansion expansion = ((IASTMacroExpansionLocation) location).getExpansion();
macroReferenceName = expansion.getMacroReference();
IASTPreprocessorMacroDefinition macroDefn = expansion.getMacroDefinition();
type = Type.from(macroDefn.getName());
if (type != null)
break;
}
// There is nothing to do if the expected type of expansion is not found.
if (macroReferenceName == null || type == null)
return null;
// This check will miss cases like:
// #define MY_SIG1 SIGNAL
// #define MY_SIG2(s) SIGNAL(s)
// #define MY_SIG3(s) SIGNAL(signal())
// connect( &a, MY_SIG1(signal()), ...
// connect( &a, MY_SIG2(signal()), ...
// connect( &a, MY_SIG2, ...
// This could be improved by adding tests when arg represents a macro expansion. However, I'm
// not sure if we would be able to follow the more complicated case of macros that call functions
// that use the SIGNAL macro. For now I've implemented the simpler check of forcing the call to
// use the SIGNAL/SLOT macro directly.
String raw = arg.getRawSignature();
Matcher m = ASTUtil.Regex_MacroExpansion.matcher( raw );
if( ! m.matches() )
return null;
// Get the argument to the SIGNAL/SLOT macro and the offset/length of that argument within the
// complete function argument. E.g., with this argument to QObject::connect
// SIGNAL( signal(int) )
// the values are
// expansionArgs: "signal(int)"
// expansionOffset: 8
// expansionLength: 11
String expansionArgs = m.group( 2 );
int expansionOffset = m.start( 2 );
int expansionLength = m.end( 2 ) - expansionOffset;
IASTFileLocation location = new QtASTImageLocation(macroReferenceName.getFileLocation(), expansionOffset, expansionLength);
return new QtMethodReference(type, cls, macroReferenceName, expansionArgs, location);
}
public Type getType() {
return type;
}
@Override
public String getRawSignature() {
return expansionParam;
}
@Override
public char[] toCharArray() {
return expansionParam.toCharArray();
}
@Override
public IBinding resolveBinding() {
if (binding != null)
return binding;
// Qt method references return the C++ method that is being referenced in the SIGNAL or
// SLOT macro expansion.
String methodName = expansionParam;
int paren = methodName.indexOf('(');
if (paren > 0)
methodName = methodName.substring(0, paren);
IBinding[] methods = CPPSemantics.findBindings(cls.getCompositeScope(), methodName.trim(), false);
// TODO find the one binding that matches the parameter of the macro expansion
// 1) Normalize expansionParam
// 2) Use it to filter the matching methods
binding = methods.length > 0 ? methods[0] : null;
return binding;
}
}

View file

@ -1,108 +0,0 @@
/*
* Copyright (c) 2013 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 org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.qt.core.QtKeywords;
public class QtSignalSlotReference
{
public static enum Type
{
Signal( "sender", "SIGNAL", "signal" ),
Slot ( "receiver", "SLOT", "slot" );
public final String roleName;
public final String macroName;
public final String paramName;
public boolean matches( Type other )
{
if( other == null )
return false;
// The signal parameter must be a SIGNAL, but the slot could be a
// SLOT or a SIGNAL.
return this == Signal ? other == Signal : true;
}
private Type( String roleName, String macroName, String paramName )
{
this.roleName = roleName;
this.macroName = macroName;
this.paramName = paramName;
}
}
public final IASTNode parent;
public final IASTNode node;
public final Type type;
public final String signature;
public final int offset;
public final int length;
private QtSignalSlotReference( IASTNode parent, IASTNode node, Type type, String signature, int offset, int length )
{
this.parent = parent;
this.node = node;
this.type = type;
this.signature = signature;
this.offset = offset;
this.length = length;
}
public IASTName createName( IBinding binding )
{
return new QtSignalSlotReferenceName( parent, node, signature, offset, length, binding );
}
public static QtSignalSlotReference parse( IASTNode parent, IASTNode arg )
{
// This check will miss cases like:
// #define MY_SIG1 SIGNAL
// #define MY_SIG2(s) SIGNAL(s)
// #define MY_SIG3(s) SIGNAL(signal())
// connect( &a, MY_SIG1(signal()), ...
// connect( &a, MY_SIG2(signal()), ...
// connect( &a, MY_SIG2, ...
// This could be improved by adding tests when arg represents a macro expansion. However, I'm
// not sure if we would be able to follow the more complicated case of macros that call functions
// that use the SIGNAL macro. For now I've implemented the simpler check of forcing the call to
// use the SIGNAL/SLOT macro directly.
String raw = arg.getRawSignature();
Matcher m = ASTUtil.Regex_SignalSlotExpansion.matcher( raw );
if( ! m.matches() )
return null;
Type type;
String macroName = m.group( 1 );
if( QtKeywords.SIGNAL.equals( macroName ) )
type = Type.Signal;
else if( QtKeywords.SLOT.equals( macroName ) )
type = Type.Slot;
else
return null;
// Get the argument to the SIGNAL/SLOT macro and the offset/length of that argument within the
// complete function argument. E.g., with this argument to QObject::connect
// SIGNAL( signal(int) )
// the values are
// expansionArgs: "signal(int)"
// expansionOffset: 8
// expansionLength: 11
String expansionArgs = m.group( 2 );
int expansionOffset = m.start( 2 );
int expansionLength = m.end( 2 ) - expansionOffset;
return new QtSignalSlotReference( parent, arg, type, expansionArgs, expansionOffset, expansionLength );
}
}

View file

@ -1,82 +0,0 @@
/*
* Copyright (c) 2013 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 org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
/**
* The location of the signal/slot reference is stored as the location of the parent
* macro expansion + an offset, which is the number of characters between the start
* of the expansion and the start of the argument (including whitespace). E.g. in,
*
* <pre>
* SIGNAL( signal1( int ) )
* ^ ^ ^ c: end of reference name
* | +------------- b: start of reference name
* +--------------------- a: start of macro expansion
* </pre>
*
* The offset is b - a and length is c - a. This means that the result of 'Find
* References' will highlight just "signal( int )".
*
* @see QtSignalSlotReferenceName
*/
public class QtSignalSlotReferenceLocation implements IASTImageLocation {
private final IASTFileLocation referenceLocation;
private final int offset;
private final int length;
public QtSignalSlotReferenceLocation(IASTFileLocation referenceLocation, int offset, int length) {
this.referenceLocation = referenceLocation;
this.offset = offset;
this.length = length;
}
@Override
public int getLocationKind() {
return IASTImageLocation.ARGUMENT_TO_MACRO_EXPANSION;
}
@Override
public int getNodeOffset() {
return referenceLocation.getNodeOffset() + offset;
}
@Override
public int getNodeLength() {
return length;
}
@Override
public String getFileName() {
return referenceLocation.getFileName();
}
@Override
public IASTFileLocation asFileLocation() {
return referenceLocation;
}
@Override
public int getEndingLineNumber() {
return referenceLocation.getEndingLineNumber();
}
@Override
public int getStartingLineNumber() {
return referenceLocation.getStartingLineNumber();
}
@Override
public IASTPreprocessorIncludeStatement getContextInclusionStatement() {
return referenceLocation.getContextInclusionStatement();
}
}

View file

@ -1,287 +0,0 @@
/*
* Copyright (c) 2013 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 org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.parser.IToken;
/**
* Signals are connected to slots by referencing them within an expansion of SIGNAL
* or SLOT. E.g.,
*
* <pre>
* class A : public QObject
* {
* Q_SIGNAL void signal1( int );
* Q_SLOT void slot1();
* };
* A a;
* QObject::connect( &a, SIGNAL( signal1( int ) ), &a, SLOT( slot1() ) );
* </pre>
*
* The goal is for 'Find References' on the function declarations to find the references
* in the macro expansions. The PDOM stores references as a linked list from the binding
* for the function.
*
* This class represents the name within the expansion, i.e., "signal1( int )" within
* "SIGNAL( signal1( int ) )" and "slot1()" within "SLOT( slot1() )".
*/
public class QtSignalSlotReferenceName implements IASTName {
private final IASTNode referenceNode;
private final String argument;
private final IBinding binding;
private final IASTImageLocation location;
private IASTNode parent;
private ASTNodeProperty propertyInParent;
public QtSignalSlotReferenceName(IASTNode parent, IASTNode referenceNode, String argument, int offset, int length, IBinding binding) {
this.parent = parent;
this.referenceNode = referenceNode;
this.argument = argument;
this.binding = binding;
IASTFileLocation referenceLocation = referenceNode.getFileLocation();
this.location
= referenceLocation == null
? null
: new QtSignalSlotReferenceLocation(referenceLocation, offset, length);
}
@Override
public char[] toCharArray() {
return argument.toCharArray();
}
@Override
public char[] getSimpleID() {
return toCharArray();
}
@Override
public char[] getLookupKey() {
return toCharArray();
}
@Override
public IASTTranslationUnit getTranslationUnit() {
return referenceNode.getTranslationUnit();
}
@Override
public IASTFileLocation getFileLocation() {
return getImageLocation();
}
@Override
public IASTNodeLocation[] getNodeLocations() {
// The javadoc says that locations that are completely enclosed within a
// macro expansion return only the location of that expansion.
return referenceNode.getNodeLocations();
}
@Override
public String getContainingFilename() {
return referenceNode.getContainingFilename();
}
@Override
public boolean isPartOfTranslationUnitFile() {
return referenceNode.isPartOfTranslationUnitFile();
}
@Override
public IASTNode[] getChildren() {
return new IASTNode[0];
}
@Override
public IASTNode getParent() {
return parent;
}
@Override
public void setParent(IASTNode node) {
this.parent = node;
}
@Override
public ASTNodeProperty getPropertyInParent() {
return propertyInParent;
}
@Override
public void setPropertyInParent(ASTNodeProperty property) {
propertyInParent = property;
}
@Override
public boolean accept(ASTVisitor visitor) {
// The signal/slot reference has nothing to visit. It will have been
// reached by the reference node, so we can't visit that, and there is
// nothing else.
return false;
}
@Override
public String getRawSignature() {
// The raw signature of the reference is the text of the argument.
return argument;
}
@Override
public boolean contains(IASTNode node) {
// There aren't any nodes contained within the signal/slot reference.
return false;
}
@Override
public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException {
// The parent is the macro reference name, and this is the entire
// content of the arguments. Since there is nothing between these, there
// will not be any leading syntax.
return null;
}
@Override
public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException {
// The parent is the macro reference name, and this is the entire
// content of the arguments. Since there is nothing between these, there
// will not be any leading syntax.
return null;
}
@Override
public IToken getSyntax() throws ExpansionOverlapsBoundaryException {
// This reference to the signal/slot function is fully contained within
// a preprocessor node, which does not support syntax.
throw new UnsupportedOperationException();
}
@Override
public boolean isFrozen() {
return referenceNode.isFrozen();
}
@Override
public boolean isActive() {
return referenceNode.isActive();
}
@Override
public int getRoleOfName(boolean allowResolution) {
return IASTNameOwner.r_reference;
}
@Override
public boolean isDeclaration() {
return false;
}
@Override
public boolean isReference() {
return true;
}
@Override
public boolean isDefinition() {
return false;
}
@Override
public IBinding getBinding() {
return binding;
}
@Override
public IBinding resolveBinding() {
return getBinding();
}
@Override
public IASTCompletionContext getCompletionContext() {
// Signal/slot references are fully contained within a macro expansion,
// so there is no completion context.
return null;
}
@Override
public ILinkage getLinkage() {
return referenceNode instanceof IASTName ? ((IASTName) referenceNode).getLinkage() : null;
}
@Override
public IASTImageLocation getImageLocation() {
return location;
}
@Override
public IASTName getLastName() {
// Signal/slot references are not qualified, so return itself.
return this;
}
@Override
public boolean isQualified() {
return false;
}
@Override
public IASTName copy() {
// Signal/slot references are preprocessor nodes, so they don't support
// copying.
throw new UnsupportedOperationException();
}
@Override
public IASTName copy(CopyStyle style) {
// Signal/slot references are preprocessor nodes, so they don't support
// copying.
throw new UnsupportedOperationException();
}
@Override
public IASTNode getOriginalNode() {
return this;
}
@Override
public void setBinding(IBinding binding) {
// Signal/slot references find their binding on instantiation, they
// never allow it to be replaced.
throw new UnsupportedOperationException();
}
@Override
public IBinding getPreBinding() {
return getBinding();
}
@Override
public IBinding resolvePreBinding() {
return getBinding();
}
@Override
public String toString() {
return "QtSignalSlotReference(" + new String(toCharArray()) + ')';
}
}

View file

@ -133,11 +133,6 @@ public abstract class ASTDelegatedName implements IASTName {
return delegate.getOriginalNode();
}
@Override
public char[] getSimpleID() {
return delegate.getSimpleID();
}
@Override
public boolean isDeclaration() {
return delegate.isDeclaration();
@ -158,9 +153,19 @@ public abstract class ASTDelegatedName implements IASTName {
return delegate.toCharArray();
}
@Override
public char[] getSimpleID() {
return toCharArray();
}
@Override
public char[] getLookupKey() {
return toCharArray();
}
@Override
public IBinding getBinding() {
return null;
return binding;
}
@Override
@ -208,11 +213,6 @@ public abstract class ASTDelegatedName implements IASTName {
this.binding = binding;
}
@Override
public char[] getLookupKey() {
return delegate.getLookupKey();
}
@Override
public IBinding getPreBinding() {
return binding;

View file

@ -74,12 +74,9 @@ public class ASTNameReference extends ASTDelegatedName {
@Override
public IBinding resolveBinding() {
return delegate.resolveBinding();
}
@Override
public IBinding getBinding() {
return delegate.getBinding();
if (binding == null)
binding = delegate.resolveBinding();
return binding;
}
@Override

View file

@ -8,6 +8,7 @@
package org.eclipse.cdt.internal.qt.core.pdom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -19,8 +20,9 @@ import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
@ -35,6 +37,7 @@ import org.eclipse.cdt.core.index.IIndexSymbols;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.parser.scanner.LocationMap;
import org.eclipse.cdt.internal.qt.core.QtFunctionCall;
import org.eclipse.cdt.internal.qt.core.QtMethodUtil;
import org.eclipse.cdt.internal.qt.core.index.QProperty;
import org.eclipse.cdt.qt.core.QtKeywords;
@ -92,6 +95,7 @@ public class QtASTVisitor extends ASTVisitor {
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
shouldVisitExpressions = true;
this.symbols = symbols;
this.locationMap = locationMap;
@ -114,6 +118,23 @@ public class QtASTVisitor extends ASTVisitor {
return super.visit(declSpec);
}
@Override
public int visit(IASTExpression expr) {
if (expr instanceof IASTFunctionCallExpression) {
Collection<IASTName> refs = QtFunctionCall.getReferences((IASTFunctionCallExpression) expr);
if (refs != null)
for (IASTName ref : refs) {
IASTFileLocation nameLoc = ref.getFileLocation();
if (nameLoc != null) {
IASTPreprocessorIncludeStatement owner = nameLoc.getContextInclusionStatement();
symbols.add(owner, ref, null);
}
}
}
return super.visit(expr);
}
private boolean isQObject(ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) {
// The class definition must contain a Q_OBJECT expansion.
@ -434,21 +455,4 @@ public class QtASTVisitor extends ASTVisitor {
node = node.getParent();
return node instanceof IASTSimpleDeclaration ? (IASTSimpleDeclaration) node : null;
}
/**
* Return the node's IASTName if it is a function and null otherwise.
*/
private static IASTName getFunctionName(IASTDeclaration decl) {
IASTSimpleDeclaration simpleDecl = getSimpleDecl(decl);
if (simpleDecl == null)
return null;
// Only functions can be signals or slots. Return the name of the first declarator
// that is a function.
for( IASTDeclarator decltor : simpleDecl.getDeclarators())
if (decltor instanceof IASTFunctionDeclarator)
return decltor.getName();
return null;
}
}

View file

@ -10,6 +10,8 @@ package org.eclipse.cdt.qt.core;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IBinding;
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;
/**
@ -41,27 +43,38 @@ public class QtKeywords {
public static final String SLOT = "SLOT";
public static final String SLOTS = "slots";
/**
* Returns true if the argument type is for Qt's QObject class and false otherwise.
*/
public static boolean isQObject(IType type) {
if (!(type instanceof ICPPClassType))
return false;
ICPPClassType clsType = (ICPPClassType)type;
return QtKeywords.QOBJECT.equals(clsType.getName());
}
/**
* Returns true if the argument type is for Qt's QMetaMethod class and false otherwise.
*/
public static boolean isQMetaMethod(IType type) {
if (!(type instanceof ICPPClassType))
return false;
ICPPClassType clsType = (ICPPClassType)type;
return QMETAMETHOD.equals(clsType.getName());
}
/**
* Returns true if the argument binding is for the QObject::connect function
* and false otherwise.
*/
public static boolean is_QObject_connect(IBinding binding) {
if (binding == null)
return false;
// IBinding#getAdapter returns null when binding is an instance of
// PDOMCPPMethod.
if (!(binding instanceof ICPPFunction))
return false;
try {
String[] qualName = ((ICPPFunction) binding).getQualifiedName();
return qualName.length == 2
&& QOBJECT.equals(qualName[0])
&& CONNECT.equals(qualName[1]);
} catch (DOMException e) {
return false;
}
String[] qualName = getFunctionQualifiedName(binding);
return qualName != null
&& qualName.length == 2
&& QOBJECT.equals(qualName[0])
&& CONNECT.equals(qualName[1]);
}
/**
@ -69,21 +82,22 @@ public class QtKeywords {
* and false otherwise.
*/
public static boolean is_QObject_disconnect(IBinding binding) {
if (binding == null)
return false;
String[] qualName = getFunctionQualifiedName(binding);
return qualName != null
&& qualName.length == 2
&& QOBJECT.equals(qualName[0])
&& DISCONNECT.equals(qualName[1]);
}
private static String[] getFunctionQualifiedName(IBinding binding) {
// IBinding#getAdapter returns null when binding is an instance of
// PDOMCPPMethod.
if (!(binding instanceof ICPPFunction))
return false;
try {
String[] qualName = ((ICPPFunction) binding).getQualifiedName();
return qualName.length == 2
&& QOBJECT.equals(qualName[0])
&& DISCONNECT.equals(qualName[1]);
} catch (DOMException e) {
return false;
}
if (binding instanceof ICPPFunction)
try {
return ((ICPPFunction) binding).getQualifiedName();
} catch (DOMException e) {
QtPlugin.log(e);
}
return null;
}
}

View file

@ -14,7 +14,10 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.cdt.codan.core,
org.eclipse.cdt.qt.ui,
org.eclipse.jface.text,
org.eclipse.cdt.ui
org.eclipse.cdt.ui,
org.eclipse.ui.ide,
org.eclipse.ui.editors,
org.eclipse.search
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Vendor: %vendorName

View file

@ -88,14 +88,23 @@ public class BaseQtTestCase extends BaseTestCase {
// #define SIGNAL(a) qFlagLocation("2"#a)
// #define QML_DECLARE_TYPEINFO( T, F ) template <> struct QDeclarativeTypeInfo<T> { enum { H = F }; };
// enum { QML_HAS_ATTACHED_PROPERTIES = 0x01 };
// namespace Qt { enum ConnectionType { AutoConnection }; }
// class QMetaMethod { };
// class QObject
// {
// Q_OBJECT
// Q_SIGNAL void destroyed( QObject * );
// public:
// static bool connect( QObject *, const char *, QObject *, const char * );
// static bool connect( const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType = Qt::AutoConnection );
// static bool connect( const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &, Qt::ConnectionType = Qt::AutoConnection );
// bool connect( const QObject *, const char *, const char *, Qt::ConnectionType = Qt::AutoConnection );
// static bool disconnect( const QObject *, const char *, const QObject *, const char * );
// static bool disconnect( const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod & );
// bool disconnect( const char * = 0, const QObject * = 0, const char * = 0 );
// bool disconnect( const QObject *, const char * = 0 );
// };
// class QString { public: QString( const char * ch ); };
// template<typename T> class QList { };
// template<typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
// template<typename T, int metaObjectRevision> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
// template<typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason);

View file

@ -15,6 +15,9 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQObject;
@ -57,8 +60,6 @@ public class QObjectTests extends BaseQtTestCase {
}
// #include "junit-QObject.hh"
// template <typename T> class QList {};
// class QString {};
// class Q0 : public QObject
// {
// Q_OBJECT
@ -137,7 +138,6 @@ public class QObjectTests extends BaseQtTestCase {
}
// #include "junit-QObject.hh"
// template <typename T> class QList {};
// class Q : public QObject
// {
// Q_OBJECT
@ -452,9 +452,14 @@ public class QObjectTests extends BaseQtTestCase {
assertFalse(i.hasNext());
}
private static void assert_checkQMethod(IQMethod method, IQObject expectedOwner, String expectedName, IQMethod.Kind expectedKind, Long expectedRevision) throws Exception {
assertEquals(expectedKind, method.getKind());
assertEquals(expectedName, method.getName());
assertSame(method.getName(), expectedOwner, method.getOwner());
assertEquals(expectedRevision, method.getRevision());
}
// #include "junit-QObject.hh"
// template <typename T> class QList {};
// class QString {};
// class Q : public QObject
// {
// Q_OBJECT
@ -512,10 +517,173 @@ public class QObjectTests extends BaseQtTestCase {
}
}
private static void assert_checkQMethod(IQMethod method, IQObject expectedOwner, String expectedName, IQMethod.Kind expectedKind, Long expectedRevision) throws Exception {
assertEquals(expectedKind, method.getKind());
assertEquals(expectedName, method.getName());
assertSame(method.getName(), expectedOwner, method.getOwner());
assertEquals(expectedRevision, method.getRevision());
// #include "junit-QObject.hh"
// class QUnrelated : public QObject { Q_OBJECT };
// class Q : public QObject
// {
// Q_OBJECT
// Q_SIGNAL void signal1();
// Q_SLOT void slot1();
//
// void f1();
// void f2()
// {
// Q q, *q_sender = this, *q_receiver = this;
// QUnrelated *q_unrelated;
// QMetaMethod meta = q.metaObject()->method( 0 );
//
// // static bool connect(const QObject *sender, const char *signal,
// // const QObject *receiver, const char *member,
// // Qt::ConnectionType = Qt::AutoConnection);
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()), Qt::AutoConnection );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()), Qt::AutoConnection );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()), Qt::AutoConnection );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()), Qt::AutoConnection );
//
// // static bool connect(const QObject *sender, const QMetaMethod &signal,
// // const QObject *receiver, const QMetaMethod &method,
// // Qt::ConnectionType type = Qt::AutoConnection);
// QObject::connect( q_sender, meta, q_receiver, meta );
// QObject::connect( q_sender, meta, q_receiver, meta, Qt::AutoConnection );
// q_unrelated->connect( q_sender, meta, q_receiver, meta );
// q_unrelated->connect( q_sender, meta, q_receiver, meta, Qt::AutoConnection );
//
// // inline bool connect(const QObject *sender, const char *signal,
// // const char *member,
// // Qt::ConnectionType type = Qt::AutoConnection) const;
// q_receiver->connect( q_sender, SIGNAL(signal1()), SIGNAL(signal1()) );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SLOT(slot1()) );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SIGNAL(signal1()), Qt::AutoConnection );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SLOT(slot1()), Qt::AutoConnection );
//
// // static bool disconnect(const QObject *sender, const char *signal,
// // const QObject *receiver, const char *member);
// QObject::disconnect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// QObject::disconnect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_unrelated->disconnect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_unrelated->disconnect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
//
// // static bool disconnect(const QObject *sender, const QMetaMethod &signal,
// // const QObject *receiver, const QMetaMethod &member);
// QObject::disconnect( q_sender, meta, q_receiver, meta );
// q_unrelated->disconnect( q_sender, meta, q_receiver, meta );
//
// // inline bool disconnect(const char *signal = 0,
// // const QObject *receiver = 0, const char *member = 0);
// q_sender->disconnect( SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_sender->disconnect( SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_sender->disconnect( SIGNAL(signal1()), q_receiver );
// q_sender->disconnect( SIGNAL(signal1()) );
// q_sender->disconnect();
//
// // inline bool disconnect(const QObject *receiver, const char *member = 0);
// q_sender->disconnect( q_receiver, SIGNAL(signal1()) );
// q_sender->disconnect( q_receiver, SLOT(slot1()) );
// q_sender->disconnect( q_receiver );
// }
// };
// // This is exactly the same as the inline function body. It is duplicated here because of
// // an old bug where the function definitions were not properly indexed (for Qt).
// void Q::f1()
// {
// Q q, *q_sender = this, *q_receiver = this;
// QUnrelated *q_unrelated;
// QMetaMethod meta = q.metaObject()->method( 0 );
//
// // static bool connect(const QObject *sender, const char *signal,
// // const QObject *receiver, const char *member,
// // Qt::ConnectionType = Qt::AutoConnection);
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()), Qt::AutoConnection );
// QObject::connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()), Qt::AutoConnection );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()), Qt::AutoConnection );
// q_unrelated->connect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()), Qt::AutoConnection );
//
// // static bool connect(const QObject *sender, const QMetaMethod &signal,
// // const QObject *receiver, const QMetaMethod &method,
// // Qt::ConnectionType type = Qt::AutoConnection);
// QObject::connect( q_sender, meta, q_receiver, meta );
// QObject::connect( q_sender, meta, q_receiver, meta, Qt::AutoConnection );
// q_unrelated->connect( q_sender, meta, q_receiver, meta );
// q_unrelated->connect( q_sender, meta, q_receiver, meta, Qt::AutoConnection );
//
// // inline bool connect(const QObject *sender, const char *signal,
// // const char *member,
// // Qt::ConnectionType type = Qt::AutoConnection) const;
// q_receiver->connect( q_sender, SIGNAL(signal1()), SIGNAL(signal1()) );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SLOT(slot1()) );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SIGNAL(signal1()), Qt::AutoConnection );
// q_receiver->connect( q_sender, SIGNAL(signal1()), SLOT(slot1()), Qt::AutoConnection );
//
// // static bool disconnect(const QObject *sender, const char *signal,
// // const QObject *receiver, const char *member);
// QObject::disconnect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// QObject::disconnect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_unrelated->disconnect( q_sender, SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_unrelated->disconnect( q_sender, SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
//
// // static bool disconnect(const QObject *sender, const QMetaMethod &signal,
// // const QObject *receiver, const QMetaMethod &member);
// QObject::disconnect( q_sender, meta, q_receiver, meta );
// q_unrelated->disconnect( q_sender, meta, q_receiver, meta );
//
// // inline bool disconnect(const char *signal = 0,
// // const QObject *receiver = 0, const char *member = 0);
// q_sender->disconnect( SIGNAL(signal1()), q_receiver, SIGNAL(signal1()) );
// q_sender->disconnect( SIGNAL(signal1()), q_receiver, SLOT(slot1()) );
// q_sender->disconnect( SIGNAL(signal1()), q_receiver );
// q_sender->disconnect( SIGNAL(signal1()) );
// q_sender->disconnect();
//
// // inline bool disconnect(const QObject *receiver, const char *member = 0);
// q_sender->disconnect( q_receiver, SIGNAL(signal1()) );
// q_sender->disconnect( q_receiver, SLOT(slot1()) );
// q_sender->disconnect( q_receiver );
// }
public void testSignalSlotReferences() throws Exception {
loadComment("sig_slot_refs.hh");
waitForIndexer(fCProject);
// References are from the function's IBinding.
assertNotNull(fIndex);
fIndex.acquireReadLock();
try {
char[][] Q_signal1_qn = new char[][]{ "Q".toCharArray(), "signal1".toCharArray() };
IIndexBinding[] Q_signal1s = fIndex.findBindings(Q_signal1_qn, IndexFilter.CPP_DECLARED_OR_IMPLICIT, npm());
assertNotNull(Q_signal1s);
assertEquals(1, Q_signal1s.length);
IIndexBinding Q_signal1 = Q_signal1s[0];
assertNotNull(Q_signal1);
char[][] Q_slot1_qn = new char[][]{ "Q".toCharArray(), "slot1".toCharArray() };
IIndexBinding[] Q_slot1s = fIndex.findBindings(Q_slot1_qn, IndexFilter.CPP_DECLARED_OR_IMPLICIT, npm());
assertNotNull(Q_slot1s);
assertEquals(1, Q_slot1s.length);
IIndexBinding Q_slot1 = Q_slot1s[0];
assertNotNull(Q_slot1);
// Each valid variant of the connect function call should have one reference
// in the inline function (f2) and one reference in the function with a separate
// definition (f1).
int expectedSignalRefs = 2 * 30;
int expectedSlotRefs = 2 * 10;
IIndexName[] signalRefs = fIndex.findReferences(Q_signal1);
assertNotNull(signalRefs);
assertEquals(expectedSignalRefs, signalRefs.length);
IIndexName[] slotRefs = fIndex.findReferences(Q_slot1);
assertNotNull(slotRefs);
assertEquals(expectedSlotRefs, slotRefs.length);
} finally {
fIndex.releaseReadLock();
}
}
}

View file

@ -14,10 +14,27 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.internal.core.model.ext.SourceRange;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.search.CSearchResult;
import org.eclipse.cdt.internal.ui.search.CSearchTextSelectionQuery;
import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.QtIndex;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
@SuppressWarnings("restriction")
public class QtRegressionTests extends BaseQtTestCase {
private static Map<String, Set<String>> buildExpectedMap(String mocOutput) {
@ -334,4 +351,70 @@ public class QtRegressionTests extends BaseQtTestCase {
assertTrue("unexpected slot " + method.getName(), expected.remove(method.getName()));
assertEquals("missing slots " + expected.toString(), 0, expected.size());
}
// #include "junit-QObject.hh"
// class Q : public QObject
// {
// Q_OBJECT
// Q_SIGNAL void signal1();
// Q_SLOT void slot1();
// void function()
// {
// signal1();
// QObject::connect(
// this, SIGNAL( signal1() ),
// this, SLOT( slot1() ) );
// }
// };
public void testBug424499_FindQMethodReferences() throws Exception {
String filename = "signalRefs.hh";
loadComment(filename);
waitForIndexer(fCProject);
// The search query uses the ASTProvider which relies on the target translation unit being
// loaded in a CEditor. The following opens a CEditor for the test file so that it will
// be the one used by the ASTProvider.
IFile file = fProject.getFile(filename);
assertNotNull(file);
assertTrue(file.exists());
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
assertNotNull(page);
IEditorPart editor = IDE.openEditor(page, file, CUIPlugin.EDITOR_ID);
assertNotNull(editor);
CEditor ceditor = (CEditor) editor.getAdapter(CEditor.class);
assertNotNull(ceditor);
// NOTE: This offset relies on the above comment being exactly as expected. If it is edited,
// then this offset should be adjusted to match. It needs to put the cursor in the
// declaration for signal1.
ceditor.setSelection(new SourceRange(86, 0), true);
ISelection sel = ceditor.getSelectionProvider().getSelection();
assertNotNull(sel);
assertTrue(sel instanceof ITextSelection);
// Now a query is created and executed.
CSearchTextSelectionQuery query = new CSearchTextSelectionQuery(null, ceditor.getInputCElement(), (ITextSelection) sel, IIndex.FIND_REFERENCES);
// The query sometimes fails (with Status.CANCEL_STATUS) if the TU is not open. I
// haven't found a way to be notified when the TU gets "opened" -- the test case just
// looks that case and then try again.
IStatus status = null;
long end_ms = System.currentTimeMillis() + 1000;
do {
status = query.run(npm());
if (status == Status.CANCEL_STATUS) {
Thread.sleep(100);
}
} while(!status.isOK() && System.currentTimeMillis() < end_ms);
assertTrue("query failed: " + status.getMessage(), status.isOK());
// The query should have found two references, one for the function call and another
// for the SIGNAL expansion.
ISearchResult result = query.getSearchResult();
assertNotNull(result);
assertTrue(result instanceof CSearchResult);
CSearchResult searchResult = (CSearchResult) result;
assertEquals(2, searchResult.getMatchCount());
}
}