Skip to main content
summaryrefslogtreecommitdiffstats
path: root/qt
diff options
context:
space:
mode:
authorAndrew Eidsness2014-01-02 14:18:10 -0500
committerDoug Schaefer2014-01-02 21:49:38 -0500
commita512bbb01128ee2707cf02e3a6e5d871c4466197 (patch)
treea298256a18a45de1f99a1575fcfd1b19773a7888 /qt
parent281d5ddf799dfc632d0af16959960752ad5b2b5a (diff)
downloadorg.eclipse.cdt-a512bbb01128ee2707cf02e3a6e5d871c4466197.tar.gz
org.eclipse.cdt-a512bbb01128ee2707cf02e3a6e5d871c4466197.tar.xz
org.eclipse.cdt-a512bbb01128ee2707cf02e3a6e5d871c4466197.zip
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>
Diffstat (limited to 'qt')
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/ASTUtil.java21
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtFunctionCall.java182
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtMethodReference.java179
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReference.java108
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceLocation.java82
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceName.java287
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTDelegatedName.java22
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTNameReference.java9
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/QtASTVisitor.java40
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java70
-rw-r--r--qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF5
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java11
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java188
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java83
14 files changed, 726 insertions, 561 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/ASTUtil.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/ASTUtil.java
index d75ebe75aa..7fc4bbaa41 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/ASTUtil.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/ASTUtil.java
@@ -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) {
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtFunctionCall.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtFunctionCall.java
new file mode 100644
index 0000000000..62b8954d6d
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtFunctionCall.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtMethodReference.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtMethodReference.java
new file mode 100644
index 0000000000..98d670abc2
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtMethodReference.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReference.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReference.java
deleted file mode 100644
index dac7389bc0..0000000000
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReference.java
+++ /dev/null
@@ -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 );
- }
-}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceLocation.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceLocation.java
deleted file mode 100644
index 5b3679638b..0000000000
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceLocation.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceName.java
deleted file mode 100644
index 46f7259bf9..0000000000
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/QtSignalSlotReferenceName.java
+++ /dev/null
@@ -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()) + ')';
- }
-}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTDelegatedName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTDelegatedName.java
index 271c3f5d3d..3e0cb5b7a1 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTDelegatedName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTDelegatedName.java
@@ -134,11 +134,6 @@ public abstract class ASTDelegatedName implements IASTName {
}
@Override
- public char[] getSimpleID() {
- return delegate.getSimpleID();
- }
-
- @Override
public boolean isDeclaration() {
return delegate.isDeclaration();
}
@@ -159,8 +154,18 @@ public abstract class ASTDelegatedName implements IASTName {
}
@Override
+ public char[] getSimpleID() {
+ return toCharArray();
+ }
+
+ @Override
+ public char[] getLookupKey() {
+ return toCharArray();
+ }
+
+ @Override
public IBinding getBinding() {
- return null;
+ return binding;
}
@Override
@@ -209,11 +214,6 @@ public abstract class ASTDelegatedName implements IASTName {
}
@Override
- public char[] getLookupKey() {
- return delegate.getLookupKey();
- }
-
- @Override
public IBinding getPreBinding() {
return binding;
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTNameReference.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTNameReference.java
index 52d511a833..5903584f04 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTNameReference.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/ASTNameReference.java
@@ -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
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/QtASTVisitor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/QtASTVisitor.java
index 4273ef4d6b..c52503bcfd 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/QtASTVisitor.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/pdom/QtASTVisitor.java
@@ -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;
- }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
index 86e935beb6..20f0e368df 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
@@ -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;
/**
@@ -42,26 +44,37 @@ public class QtKeywords {
public static final String SLOTS = "slots";
/**
- * Returns true if the argument binding is for the QObject::connect function
- * and false otherwise.
+ * Returns true if the argument type is for Qt's QObject class and false otherwise.
*/
- public static boolean is_QObject_connect(IBinding binding) {
- if (binding == null)
+ public static boolean isQObject(IType type) {
+ if (!(type instanceof ICPPClassType))
return false;
- // IBinding#getAdapter returns null when binding is an instance of
- // PDOMCPPMethod.
- if (!(binding instanceof ICPPFunction))
- return false;
+ ICPPClassType clsType = (ICPPClassType)type;
+ return QtKeywords.QOBJECT.equals(clsType.getName());
+ }
- try {
- String[] qualName = ((ICPPFunction) binding).getQualifiedName();
- return qualName.length == 2
- && QOBJECT.equals(qualName[0])
- && CONNECT.equals(qualName[1]);
- } catch (DOMException e) {
+ /**
+ * 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) {
+ 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;
}
}
diff --git a/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF
index a6110d5794..4ceb2632e9 100644
--- a/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF
+++ b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF
@@ -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
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java
index 2c023b29ba..8bc1d1b755 100644
--- a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java
@@ -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);
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java
index 5a3dfcd807..9a13400cb1 100644
--- a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java
@@ -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();
+ }
}
}
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java
index bf168b7c58..cc8b2e619d 100644
--- a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java
@@ -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());
+ }
}

Back to the top