diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java
index 843ce659c20..3968b84c1fb 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java
@@ -165,7 +165,10 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser {
private static final int DEFAULT_PARM_LIST_SIZE = 4;
private static final int DEFAULT_CATCH_HANDLER_LIST_SIZE= 4;
- private static enum DtorStrategy {PREFER_FUNCTION, PREFER_NESTED}
+
+ // This is a parameter to the protected function {@link #declarator(DtorStrategy, DeclarationOptions)}
+ // so it needs to be protected too.
+ protected static enum DtorStrategy {PREFER_FUNCTION, PREFER_NESTED}
private final boolean allowCPPRestrict;
private final boolean supportExtendedTemplateSyntax;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/QtParser.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/QtParser.java
new file mode 100644
index 00000000000..626e82056ae
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/QtParser.java
@@ -0,0 +1,116 @@
+/*
+ * 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.parser;
+
+import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId;
+import org.eclipse.cdt.core.dom.parser.cpp.GPPParserExtensionConfiguration;
+import org.eclipse.cdt.core.parser.EndOfFileException;
+import org.eclipse.cdt.core.parser.IToken;
+import org.eclipse.cdt.core.parser.NullLogService;
+import org.eclipse.cdt.core.parser.ParserMode;
+import org.eclipse.cdt.internal.core.dom.parser.BacktrackException;
+import org.eclipse.cdt.internal.core.dom.parser.DeclarationOptions;
+import org.eclipse.cdt.internal.core.dom.parser.cpp.GNUCPPSourceParser;
+
+/**
+ * A parser that use a special StringScanner to extract small sections of C++ syntax that
+ * are used in Qt macro expansions.
+ *
+ * @see StringScanner
+ */
+@SuppressWarnings("restriction")
+public class QtParser extends GNUCPPSourceParser {
+
+ private QtParser(String str) {
+ super(new StringScanner(str), ParserMode.QUICK_PARSE, new NullLogService(), GPPParserExtensionConfiguration.getInstance());
+ }
+
+ /**
+ * The argument String is the expansion parameter for SIGNAL and SLOT macros. The text
+ * is parsed and the function declarator is returned if possible. Returns null if the
+ * string is not a valid function declarator reference.
+ */
+ public static ICPPASTFunctionDeclarator parseQtMethodReference(String str) {
+ // Reject strings that have embedded line terminators. This is needed to properly check that
+ // one that is about to be added.
+ if (str == null
+ || str.contains(";"))
+ return null;
+
+ QtParser parser = new QtParser(str + ';');
+ try {
+ IASTDeclarator declarator
+ = parser.declarator(GNUCPPSourceParser.DtorStrategy.PREFER_FUNCTION, DeclarationOptions.CPP_MEMBER);
+ if (!(declarator instanceof ICPPASTFunctionDeclarator))
+ return null;
+
+ // JI 439374: Make sure the ; was the last token read to prevent errors where extra strings
+ // appear in the expansion parameter.
+ if (parser.lastTokenFromScanner == null
+ || parser.lastTokenFromScanner.getType() != IToken.tSEMI)
+ return null;
+
+ // JI 439374: Make sure the ; was the last token read to prevent errors where extra strings
+ // appear in the expansion parameter.
+ if (parser.lastTokenFromScanner == null
+ || parser.lastTokenFromScanner.getType() != IToken.tSEMI)
+ return null;
+
+ // make sure this is a legal declarator for a Qt method reference
+ ICPPASTFunctionDeclarator function = (ICPPASTFunctionDeclarator) declarator;
+
+ // 1) parameters must not have names
+ for(ICPPASTParameterDeclaration param : function.getParameters()) {
+ ICPPASTDeclarator decltor = param.getDeclarator();
+ if (decltor == null)
+ continue;
+
+ IASTName paramName = decltor.getName();
+ if (paramName == null)
+ continue;
+
+ char[] name = paramName.getSimpleID();
+ if (name == null
+ || name.length <= 0)
+ continue;
+
+ // The Qt normalization code treats a reference with a trailing const as a special case (this
+ // seems to be a bug in the way they normalize const pointers. We could support this case by
+ // allowing reference parameters to be named 'const'. However, since this seems to be a bug
+ // in Qt they will likely fix it at some point, and there doesn't seem to be a case where the
+ // user would need to reference the Qt method in this way.
+
+ // the parameter has a non-empty name, so reject the declarator
+ return null;
+ }
+
+ // All tests have passed, so return this declarator.
+ return function;
+ } catch(BacktrackException e) {
+ return null;
+ } catch(EndOfFileException e) {
+ return null;
+ }
+ }
+
+ public static ICPPASTTypeId parseTypeId(String str) {
+ QtParser parser = new QtParser(str);
+ try {
+ return parser.typeId(new DeclarationOptions(DeclarationOptions.NO_INITIALIZER));
+ } catch(BacktrackException e) {
+ return null;
+ } catch(EndOfFileException e) {
+ return null;
+ }
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/StringScanner.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/StringScanner.java
new file mode 100644
index 00000000000..6112addcecf
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/parser/StringScanner.java
@@ -0,0 +1,118 @@
+/*
+ * 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.parser;
+
+import java.util.Map;
+
+import org.eclipse.cdt.core.dom.ast.IMacroBinding;
+import org.eclipse.cdt.core.parser.EndOfFileException;
+import org.eclipse.cdt.core.parser.IScanner;
+import org.eclipse.cdt.core.parser.IToken;
+import org.eclipse.cdt.core.parser.IncludeExportPatterns;
+import org.eclipse.cdt.core.parser.Keywords;
+import org.eclipse.cdt.core.parser.OffsetLimitReachedException;
+import org.eclipse.cdt.core.parser.util.CharArrayIntMap;
+import org.eclipse.cdt.internal.core.parser.scanner.ILexerLog;
+import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver;
+import org.eclipse.cdt.internal.core.parser.scanner.Lexer;
+import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
+
+/**
+ * The standard CDT scanner is CPreprocessor, which uses a Lexer to read from a file. The
+ * relationships look like:
+ *
+ * GNUCPPSourceParser - CPreprocessor - Lexer
+ *
+ * The implementation of CPreprocessor depends on reading from a file. It might be possible
+ * to configure it to get content from a String instead, but it seems like a complex change.
+ * This simpler solution replaces the CPreprocessor with a simple scanner. In this context,
+ * the only part of CPreprocessor that seems to be needed is replacing the token type for
+ * keywords. In this case the relationships look like:
+ *
+ * QtParser - StringScanner - Lexer
+ */
+@SuppressWarnings("restriction")
+public class StringScanner implements IScanner {
+
+ private final Lexer lexer;
+ private final CharArrayIntMap keywords;
+
+ public StringScanner(String str) {
+ this.lexer = new Lexer(str.toCharArray(), new LexerOptions(), ILexerLog.NULL, null);
+ keywords = new CharArrayIntMap(40, -1);
+ Keywords.addKeywordsCpp(keywords);
+ }
+
+ @Override
+ public IToken nextToken() throws EndOfFileException {
+
+ IToken token = lexer.nextToken();
+ if (token.getType() != IToken.tIDENTIFIER)
+ return token;
+
+ char[] name= token.getCharImage();
+ int tokenType = keywords.get(name);
+ if (tokenType != keywords.undefined)
+ token.setType(tokenType);
+
+ return token;
+ }
+
+ @Override
+ public Map getMacroDefinitions() {
+ return null;
+ }
+
+ @Override
+ public boolean isOnTopContext() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+ }
+
+ @Override
+ public ILocationResolver getLocationResolver() {
+ return null;
+ }
+
+ @Override
+ public void setTrackIncludeExport(IncludeExportPatterns patterns) {
+ }
+
+ @Override
+ public void setContentAssistMode(int offset) {
+ }
+
+ @Override
+ public void setSplitShiftROperator(boolean val) {
+ }
+
+ @Override
+ public void setComputeImageLocations(boolean val) {
+ }
+
+ @Override
+ public void setProcessInactiveCode(boolean val) {
+ }
+
+ @Override
+ public void skipInactiveCode() throws OffsetLimitReachedException {
+ }
+
+ @Override
+ public int getCodeBranchNesting() {
+ return 0;
+ }
+
+ @Override
+ @Deprecated
+ public void setScanComments(boolean val) {
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMethod.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMethod.java
new file mode 100644
index 00000000000..c2efe891e5f
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQMethod.java
@@ -0,0 +1,69 @@
+/*
+ * 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.qt.core.index;
+
+import java.util.Collection;
+
+/**
+ * Qt provides macros for marking member functions as special. The moc compiler
+ * recognizes these annotations and generates extra code to implement the special
+ * behaviour.
+ *
+ * This interface is used to represent these methods in the Qt index. It is used
+ * for member functions that have been marked as signals, slots, and invokables.
+ */
+public interface IQMethod extends IQElement, IQObject.IMember {
+
+ /**
+ * The kind of Qt annotation that has been applied to this member function.
+ * Signals and slots are implicitly invokable, if a single member function
+ * has been tagged with both signal/slot and invokable, the kind will be
+ * Signal or Slot.
+ */
+ public static enum Kind {
+ Unspecified,
+ Invokable,
+ Signal,
+ Slot;
+ }
+
+ /**
+ * The kind of Qt annotation that has been applied to this member function. Signals and
+ * slots are implicitly invokable, if a single member function has been tagged with both
+ * signal and invokable, the kind will be Signal (and likewise for Slot).
+ */
+ public Kind getKind();
+
+ /**
+ * Returns the function name of the method.
+ */
+ public String getName();
+
+ /**
+ * Returns the normalized C++ function signatures of the receiver method. There is
+ * more than one signature only when at least one parameter has a default value.
+ * E.g., for
+ * #signal1 in:
+ *
+ * class T : public QObject
+ * {
+ * Q_OBJECT
+ * Q_SIGNAL void signal1( int = 5 );
+ * };
+ *
+ * This would return "{ signal1(int), signal1() }".
+ */
+ public Collection getSignatures();
+
+ /**
+ * Return the revision if this method was tagged with the Q_REVISION macro and null
+ * otherwise. The return type is Long in order to accommodate unsigned C++ 32-bit
+ * values.
+ */
+ public Long getRevision();
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java
index 17670e26a5e..383ca73112b 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java
@@ -86,6 +86,21 @@ public interface IQObject extends IQElement {
*/
public List getBases();
+ /**
+ * Returns the methods that have been tagged as Qt slots. Does not return null.
+ */
+ public IMembers getSlots();
+
+ /**
+ * Returns the methods that have been tagged as Qt signals. Does not return null.
+ */
+ public IMembers getSignals();
+
+ /**
+ * Returns the methods that have been tagged with Q_INVOKABLE. Does not return null.
+ */
+ public IMembers getInvokables();
+
/**
* Returns the expansions of the Q_PROPERTY macro. Does not return null.
*/
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtMethodUtil.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtMethodUtil.java
new file mode 100644
index 00000000000..c25a7e511c1
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtMethodUtil.java
@@ -0,0 +1,345 @@
+/*
+ * 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.qt.internal.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.IASTNode;
+import org.eclipse.cdt.core.dom.ast.IASTPointer;
+import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId;
+import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode;
+import org.eclipse.cdt.internal.qt.core.parser.QtParser;
+
+/**
+ * A collection of utility functions for dealing with Qt methods. A Qt method is a normal
+ * C++ method that has been annotated with empty macro expansions.
+ */
+@SuppressWarnings("restriction")
+public class QtMethodUtil {
+
+ /**
+ * The Qt implementation uses specific rules for generating a signature that is used
+ * to map between invokable function declarations and their use. This function has
+ * be implemented by comparing the output of moc to the various test cases in the
+ * qt test suite.
+ */
+ public static String getQtNormalizedMethodSignature(String signature) {
+
+ ICPPASTFunctionDeclarator function = QtParser.parseQtMethodReference(signature);
+ if (function == null)
+ return null;
+
+ // NOTE: This implementation (both here and in methods that are invoked) used call #getRawSignature
+ // to get the original tokens. This has been changed to use #toString instead. They seem to
+ // provide the same value, so this should be OK. The problem with #getRawSignature is that it
+ // looks for the characters in the file (using offset and length). There isn't a file backing
+ // the StringScanner, so the result is the empty String. If we find cases where #toString
+ // returns the wrong value, then this can be changed back to #getRawSignature. Implement the
+ // AST and LocationResolver to work with ASTNode#getRawSignatureChars:
+ // protected char[] getRawSignatureChars() {
+ // final IASTFileLocation floc= getFileLocation();
+ // final IASTTranslationUnit ast = getTranslationUnit();
+ // if (floc != null && ast != null) {
+ // ILocationResolver lr= (ILocationResolver) ast.getAdapter(ILocationResolver.class);
+ // if (lr != null) {
+ // return lr.getUnpreprocessedSignature(getFileLocation());
+ // }
+ // }
+
+ StringBuilder result = new StringBuilder();
+
+ // raw sig tries to find the file
+ String fnName = function.getName().getLastName().toString();
+ result.append(stripWS(fnName));
+ result.append('(');
+
+ boolean first = true;
+ for(ICPPASTParameterDeclaration param : function.getParameters()) {
+ if (first)
+ first = false;
+ else
+ result.append(',');
+
+ IASTDeclSpecifier spec = param.getDeclSpecifier();
+ ICPPASTDeclarator declarator = param.getDeclarator();
+
+ // The parameters are encoded so that we can rely on , being used to separate
+ // parameters. All other commas (e.g., to separate template arguments within
+ // the parameter type) will be encoded.
+ StringBuilder paramSig = new StringBuilder();
+ append(paramSig, spec, declarator, true);
+
+ result.append(stripWS(paramSig.toString()));
+ }
+
+ result.append(')');
+
+ // Whitespace around operators is not needed, remove it to normalize the signature.
+ return result.toString();
+ }
+
+ public static Collection getDecodedQtMethodSignatures(String qtEncSignatures) {
+ if (qtEncSignatures == null)
+ return null;
+
+ StringBuilder signature = new StringBuilder();
+ int i = qtEncSignatures.indexOf('(');
+ String name = qtEncSignatures.substring(0, i);
+
+ signature.append(name);
+ signature.append('(');
+
+ boolean first = true;
+ List signatures = new ArrayList();
+ qtEncSignatures = qtEncSignatures.substring(i + 1);
+ Pattern p = Pattern.compile("^([a-zA-Z0-9+/=]*)(@?).*$");
+ while(!qtEncSignatures.isEmpty()) {
+ Matcher m = p.matcher(qtEncSignatures);
+ if (!m.matches())
+ break;
+
+ int next = m.end(2) + 1;
+ qtEncSignatures = qtEncSignatures.substring(next);
+
+ String param = new String(DatatypeConverter.parseBase64Binary(m.group(1)));
+
+ // If this parameter has a default value, then add a signature for the method up
+ // to this point.
+ if (!m.group(2).isEmpty())
+ signatures.add(signature.toString() + ')');
+
+ if (first)
+ first = false;
+ else
+ signature.append(',');
+ signature.append(param);
+ }
+
+ signature.append(')');
+ signatures.add(signature.toString());
+ return signatures;
+ }
+
+ /**
+ * The Qt implementation has specific rules for generating a signature that is used
+ * to map between invokable function declarations and their use. This function has
+ * been implemented by comparing the output of moc to the various test cases in the
+ * Qt test suite.
+ */
+ public static String getEncodedQtMethodSignatures(ICPPASTFunctionDeclarator function) {
+ StringBuilder result = new StringBuilder();
+
+ String fnName = function.getName().getLastName().toString();
+ result.append(stripWS(fnName));
+ result.append('(');
+
+ boolean first = true;
+ for(ICPPASTParameterDeclaration param : function.getParameters()) {
+ if (first)
+ first = false;
+ else
+ result.append(',');
+
+ IASTDeclSpecifier spec = param.getDeclSpecifier();
+ ICPPASTDeclarator declarator = param.getDeclarator();
+
+ // The parameters are encoded so that we can rely on , being used to separate
+ // parameters. All other commas (e.g., to separate template arguments within
+ // the parameter type) will be encoded.
+ StringBuilder paramSig = new StringBuilder();
+ append(paramSig, spec, declarator, true);
+
+ String paramStr = stripWS(paramSig.toString());
+ result.append(DatatypeConverter.printBase64Binary(paramStr.getBytes()));
+
+ // A special character is used as a suffix on parameters that have a default value.
+ // A previous version of this implementation used '=' within the Base64 encoded
+ // payload. Now that the initializer flag is outside of the payload, '=' is a bad
+ // choice because it is also a valid Base64 encoded character.
+ // Like all the other parts of this encoder, the @ must match the value that is used
+ // in the decoder.
+ if (declarator.getInitializer() != null)
+ result.append('@');
+ }
+
+ result.append(')');
+
+ // Whitespace around operators is not needed, remove it to normalize the signature.
+ return result.toString();
+ }
+
+ private static String stripWS(String str) {
+ return str
+ .trim()
+ .replaceAll("\\s+", " ")
+ .replaceAll(" ([\\*&,()<>]+)", "$1")
+ .replaceAll("([\\*&,()<>]+) ", "$1");
+ }
+
+ private static String asString(IASTPointerOperator ptr) {
+ if (ptr instanceof ICPPASTReferenceOperator)
+ return "&";
+ if (ptr instanceof IASTPointer) {
+ StringBuilder str = new StringBuilder();
+ IASTPointer astPtr = (IASTPointer) ptr;
+ str.append('*');
+ if (astPtr.isConst())
+ str.append(" const");
+ if (astPtr.isVolatile())
+ str.append(" volatile");
+ return str.toString();
+ }
+
+ return ptr.toString();
+ }
+
+ private static void append(StringBuilder result, IASTDeclSpecifier spec, IASTDeclarator declarator, boolean pruneConst) {
+ IASTPointerOperator[] ptrs = declarator.getPointerOperators();
+ if (ptrs == null)
+ ptrs = new IASTPointerOperator[0];
+
+ if (!(spec instanceof ICPPASTDeclSpecifier)) {
+ result.append(spec.toString());
+ return;
+ }
+
+ ICPPASTDeclSpecifier cppSpec = (ICPPASTDeclSpecifier) spec;
+
+ // Qt considers the type const if it is marked as const, or if it is a reference
+ // and the previous pointer is const. I.e., we need this:
+ // const T& -> T
+ // const T* const & -> T*
+ boolean isConst = cppSpec.isConst();
+ boolean stripLastPtrConst
+ = pruneConst
+ && !isConst
+ && (ptrs.length >= 2
+ && ptrs[ptrs.length - 1] instanceof ICPPASTReferenceOperator
+ && ptrs[ptrs.length - 2] instanceof IASTPointer
+ && ((IASTPointer) ptrs[ptrs.length - 2]).isConst());
+
+ if (isConst || stripLastPtrConst) {
+ if (!pruneConst)
+ result.append("const ");
+ else {
+ // Qt signature generation converts const value and const reference types
+ // into simple value types. E.g.,
+ // const T => T
+ // const T & => T
+ // From observation, they also convert const pointer to const to const
+ // pointers although I think that is a bug, because simple pointer to
+ // const are not converted to simple pointers. E.g.,
+ // const T * => const T *
+ // const T * const => T * const
+ if (ptrs.length > 0) {
+ IASTPointerOperator lastPtr = ptrs[ptrs.length - 1];
+ if (lastPtr instanceof ICPPASTReferenceOperator)
+ ptrs = Arrays.copyOf(ptrs, ptrs.length - 1);
+ else if (!(lastPtr instanceof IASTPointer)
+ || !((IASTPointer) lastPtr).isConst())
+ result.append("const ");
+ }
+ }
+ }
+
+ // Qt does no special handling for volatile. This is likely an oversight.
+ if (cppSpec.isVolatile())
+ result.append("volatile ");
+
+ IASTNode[] children = cppSpec.getChildren();
+ if (children == null || children.length <= 0) {
+ // We use the raw signature to get the text that was used to reference the
+ // type (without following typedefs, etc.), and then strip out all const
+ // which has already been handled.
+ String raw = cppSpec.toString();
+ raw = raw.replaceAll("const\\s", "");
+ raw = raw.replaceAll("\\sconst", "");
+ result.append(raw);
+ } else {
+ for(IASTNode child : children) {
+ result.append( ' ' );
+ if (child instanceof ICPPASTTemplateId) {
+ ICPPASTTemplateId templId = (ICPPASTTemplateId) child;
+ result.append(templId.getTemplateName());
+ result.append('<');
+ for(IASTNode templArg : templId.getTemplateArguments()) {
+ append(result, templArg);
+ }
+ result.append('>');
+ } else
+ result.append(child.toString());
+ }
+ }
+
+ // exclude param name, use '=' to indicate an initial value
+ for(int i = 0; i < ptrs.length; ++i) {
+ if (!stripLastPtrConst
+ || i < ptrs.length - 1)
+ result.append(asString(ptrs[i]));
+ else
+ result.append(asString(ptrs[i]).replaceAll("const", ""));
+ }
+ }
+
+ private static void append(StringBuilder result, IASTNode node) {
+
+ // JI476551: When the code is parsed without full context, e.g., when parsing a Qt method ref, an
+ // ambiguous node could be created. Since we only need the original text, we can use
+ // any of the nodes that triggered the ambiguity. Arbitrarily choose the first one.
+ if (node instanceof ASTAmbiguousNode) {
+ IASTNode[] nodes = ((ASTAmbiguousNode) node).getNodes();
+ if (nodes != null
+ && nodes.length > 0) {
+ append(result, nodes[0]);
+ return;
+ }
+ }
+
+ if (node instanceof ICPPASTTypeId) {
+ ICPPASTTypeId typeId = (ICPPASTTypeId) node;
+ IASTDeclSpecifier spec = typeId.getDeclSpecifier();
+ IASTDeclarator declarator = typeId.getAbstractDeclarator();
+ append(result, spec, declarator, false);
+ return;
+ }
+
+ if (!(node instanceof ICPPASTTemplateId)) {
+ result.append(node.toString());
+ return;
+ }
+
+ ICPPASTTemplateId templId = (ICPPASTTemplateId) node;
+ result.append(templId.getTemplateName());
+ result.append('<');
+ boolean first = true;
+ for (IASTNode child : templId.getTemplateArguments()) {
+ if (first)
+ first = false;
+ else
+ result.append(", ");
+ append(result, child);
+ }
+ result.append('>');
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java
index 9d11ebb2621..90c97e0bd12 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java
@@ -15,14 +15,6 @@ public abstract class AbstractQField implements IQObject.IMember {
private final IQObject owner;
protected String name;
- /**
- * Scan the given field and extracts the strings defining the attributes of the
- * field. Returns false if the expansion parameter, does not represent a Q_PROPERTY,
- * does not have related information, or if the information does not match the
- * expected format.
- */
- protected abstract boolean scanDefn(String expansionParam);
-
protected AbstractQField(IQObject owner) {
this.owner = owner;
}
@@ -37,8 +29,8 @@ public abstract class AbstractQField implements IQObject.IMember {
if (!AbstractQField.class.isAssignableFrom(member.getClass()))
return false;
- // I haven't been able to find Qt documentation describing how Q_PROPERY is overridden,
- // but the docs suggest it is just by name.
+ // I haven't been able to find Qt documentation describing how things like
+ // Q_PROPERY are overridden, but the docs suggest it is just by name.
AbstractQField other = (AbstractQField) member;
return name == null ? other.name == null : name.equals(other.name);
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QMethod.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QMethod.java
new file mode 100644
index 00000000000..8c5bc7574aa
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QMethod.java
@@ -0,0 +1,99 @@
+/*
+ * 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.qt.internal.core.index;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.qt.core.index.IQMethod;
+import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.IQObject.IMember;
+import org.eclipse.cdt.qt.internal.core.QtMethodUtil;
+import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQMethod;
+import org.eclipse.core.runtime.CoreException;
+
+public class QMethod implements IQMethod {
+
+ private final IQObject owner;
+ private final String name;
+ private final IQMethod.Kind kind;
+ private final Collection signatures;
+ private final Long revision;
+
+ public QMethod(IQObject owner, QtPDOMQMethod pdom) throws CoreException {
+ this.owner = owner;
+ this.name = pdom.getName();
+ this.kind = pdom.getKind();
+ this.signatures = QtMethodUtil.getDecodedQtMethodSignatures(pdom.getQtEncodedSignatures());
+ this.revision = pdom.getRevision();
+ }
+
+ @Override
+ public boolean isOverride(IMember member) {
+ if (!IQMethod.class.isAssignableFrom(member.getClass()))
+ return false;
+
+ // Methods override when they have the same name and type.
+
+ IQMethod other = (IQMethod) member;
+
+ if (name == null) {
+ if (other.getName() != null)
+ return false;
+ } else if (!name.equals(other.getName()))
+ return false;
+
+ IBinding otherBinding = other.getBinding();
+ if (otherBinding == null)
+ return getBinding() == null;
+
+ return false ;// TODO
+// if (!ICPPMethod.class.isAssignableFrom(otherBinding.getClass()))
+// return false;
+//
+// IType thisType = method.getType();
+// IType otherType = ((ICPPMethod) otherBinding).getType();
+// return thisType == null ? otherType == null : thisType.isSameType(otherType);
+ }
+
+ @Override
+ public IBinding getBinding() {
+ return null; // TODO method;
+ }
+
+ @Override
+ public IQObject getOwner() {
+ return owner;
+ }
+
+ @Override
+ public Kind getKind() {
+ return kind;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Collection getSignatures() {
+ return signatures == null ? Collections.emptyList() : signatures;
+ }
+
+ @Override
+ public Long getRevision() {
+ return revision;
+ }
+
+ @Override
+ public String toString() {
+ return kind.toString() + ' ' + signatures;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java
index 9a050d5b638..0f5b83d3faf 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java
@@ -14,10 +14,12 @@ import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.qt.core.index.IQEnum;
+import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMProperty;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQEnum;
+import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQMethod;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
@@ -26,6 +28,9 @@ public class QObject implements IQObject {
private final String name;
private final QtPDOMQObject pdomQObject;
private final List bases;
+ private final IQObject.IMembers slots;
+ private final IQObject.IMembers signals;
+ private final IQObject.IMembers invokables;
private final IQObject.IMembers properties;
private final List enums;
private final Map classInfos;
@@ -34,6 +39,9 @@ public class QObject implements IQObject {
this.name = pdomQObject.getName();
this.pdomQObject = pdomQObject;
+ List baseSlots = new ArrayList();
+ List baseSignals = new ArrayList();
+ List baseInvokables = new ArrayList();
List baseProps = new ArrayList();
this.bases = new ArrayList();
@@ -45,13 +53,35 @@ public class QObject implements IQObject {
this.classInfos = pdomQObject.getClassInfos();
+ List slots = new ArrayList();
+ List signals = new ArrayList();
+ List invokables = new ArrayList();
+ for(QtPDOMQMethod pdom : pdomQObject.getChildren(QtPDOMQMethod.class))
+ switch(pdom.getKind()) {
+ case Slot:
+ slots.add(new QMethod(this, pdom));
+ break;
+ case Signal:
+ signals.add(new QMethod(this, pdom));
+ break;
+ case Invokable:
+ invokables.add(new QMethod(this, pdom));
+ break;
+ case Unspecified:
+ break;
+ }
+
+ this.slots = QObjectMembers.create(slots, baseSlots);
+ this.signals = QObjectMembers.create(signals, baseSignals);
+ this.invokables = QObjectMembers.create(invokables, baseInvokables);
+
this.enums = new ArrayList();
- for(QtPDOMQEnum pdom : pdomQObject.getFields(QtPDOMQEnum.class))
+ for(QtPDOMQEnum pdom : pdomQObject.getChildren(QtPDOMQEnum.class))
this.enums.add(new QEnum(pdom.getName(), pdom.isFlag(), pdom.getEnumerators()));
List props = new ArrayList();
- for(QtPDOMProperty pdom : pdomQObject.getFields(QtPDOMProperty.class)) {
- QProperty qProp = new QProperty(this, pdom.getTypeStr(), pdom.getName());
+ for(QtPDOMProperty pdom : pdomQObject.getChildren(QtPDOMProperty.class)) {
+ QProperty qProp = new QProperty(this, pdom.getType(), pdom.getName());
for(QtPDOMProperty.Attribute attr : pdom.getAttributes())
qProp.setAttribute(attr.attr, attr.value);
props.add(qProp);
@@ -74,6 +104,21 @@ public class QObject implements IQObject {
return bases;
}
+ @Override
+ public IMembers getSlots() {
+ return slots;
+ }
+
+ @Override
+ public IMembers getSignals() {
+ return signals;
+ }
+
+ @Override
+ public IMembers getInvokables() {
+ return invokables;
+ }
+
@Override
public IQObject.IMembers getProperties() {
return properties;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java
index 8d0bf81f606..f8e2f957718 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java
@@ -7,9 +7,6 @@
*/
package org.eclipse.cdt.qt.internal.core.index;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
@@ -24,110 +21,6 @@ public class QProperty extends AbstractQField implements IQProperty {
this.name = name;
}
- /**
- * A regular expression for scanning the full Q_PROPERTY expansion and extracting the
- * expansion parameter. It provides the following capture groups:
- * 1 - the type
- * 2 - the name
- * 3 - the trimmed remainder of the expansion parameter (starting with READ)
- *
- * This REGEX handles cases like:
- * Q_PROPERTY(T* t READ ... )
- * Q_PROPERTY(T * t READ ... )
- * Q_PROPERTY(T *t READ ... )
- * This REGEX assumes that READ will directly follow the property name. This is implied,
- * although not explicitly stated in the Qt documentation.
- *
- * It also allows the option of having no other attribute (just type and name). The Qt
- * documentation forbids this, but it is used in QtSensors/
- */
- private static final Pattern EXPANSION_REGEX = Pattern.compile("^(.+?)\\s*([a-zA-Z_][\\w]*+)(?:(?:\\s+(READ\\s+.*))|\\s*)$");
-
- /**
- * A regular expression for scanning Q_PROPERTY attributes. The regular expression is built
- * from the values defined in IQProperty#Attribute. It looks like:
- *
- * (:?READ)|(?:WRITE)|(:?RESET)|...
- *
- * This regular expression is used to recognize valid attributes while scanning the
- * Q_PROPERTY macro expansion.
- *
- * @see QProperty#scanAttributes(String)
- */
- private static final Pattern ATTRIBUTE_REGEX;
- static {
- StringBuilder regexBuilder = new StringBuilder();
- for(IQProperty.Attribute attr : IQProperty.Attribute.values()) {
- if (attr.ordinal() > 0)
- regexBuilder.append('|');
- regexBuilder.append("(:?");
- regexBuilder.append(attr.identifier);
- regexBuilder.append(")");
- }
- ATTRIBUTE_REGEX = Pattern.compile(regexBuilder.toString());
- }
-
- /**
- * Scans the given field and extracts the strings defining the attributes of the
- * Q_PROPERTY. Returns false if the field is does not represent a Q_PROPERTY, does
- * not have attribute-related information, or if the information does not match the
- * expected format.
- * @param field
- * @return
- */
- @Override
- protected boolean scanDefn(String expansionParam) {
- Matcher m = EXPANSION_REGEX.matcher(expansionParam);
- if (!m.matches())
- return false;
-
- this.type = m.group(1);
- this.name = m.group(2);
- return scanAttributes(m.group(3));
- }
-
- /**
- * Scans the given string to extract values for all recognized attributes. A regular expression
- * is used to find the attributes, substrings between attributes are assigned as values.
- * Attributes that don't expect a value (as determined by {@link IQProperty#Attribute#hasValue}),
- * as assigned "true".
- */
- private boolean scanAttributes(String attributes) {
- if (attributes == null)
- return true;
-
- int lastEnd = 0;
- IQProperty.Attribute lastAttr = null;
- for(Matcher attributeMatcher = ATTRIBUTE_REGEX.matcher(attributes); attributeMatcher.find(); lastEnd = attributeMatcher.end()) {
- // set the value of attribute found in the previous iteration to the substring between
- // the end of that attribute and the start of this one
- if (lastAttr != null) {
- String value = attributes.substring(lastEnd, attributeMatcher.start());
- values[lastAttr.ordinal()] = value.trim();
- }
-
- // the regex is built from the definition of the enum, so none of the strings that it
- // finds will throw an exception
- lastAttr = IQProperty.Attribute.valueOf(IQProperty.Attribute.class, attributeMatcher.group(0));
-
- // if this attribute doesn't have a value, then put it into the value map immediately
- // and make sure it is not used later in this scan
- if (!lastAttr.hasValue) {
- values[lastAttr.ordinal()] = Boolean.TRUE.toString();
- lastAttr = null;
- }
- }
-
- // the value of the last attribute in the expansion is the substring between the end of
- // the attribute identifier and the end of the string
- if (lastAttr != null) {
- String value = attributes.substring(lastEnd);
- values[lastAttr.ordinal()] = value.trim();
- }
-
- return true;
- }
-
public void setAttribute(IQProperty.Attribute attr, String value) {
values[attr.ordinal()] = ( value == null ? "" : value );
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectMemberName.java
similarity index 91%
rename from qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java
rename to qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectMemberName.java
index a9bf082364d..94eff5904e7 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectMemberName.java
@@ -19,14 +19,14 @@ import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
-public abstract class AbstractQObjectFieldName extends ASTDelegatedName {
+public abstract class AbstractQObjectMemberName extends ASTDelegatedName {
private final QObjectName owner;
private final String name;
- private final QtASTImageLocation location;
+ private final IASTImageLocation location;
private ASTNodeProperty propertyInParent;
- protected AbstractQObjectFieldName(QObjectName owner, IASTName ast, String name, QtASTImageLocation location) {
+ protected AbstractQObjectMemberName(QObjectName owner, IASTName ast, String name, IASTImageLocation location) {
super(ast);
this.owner = owner;
this.name = name;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QMethodName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QMethodName.java
new file mode 100644
index 00000000000..59c08673d6b
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QMethodName.java
@@ -0,0 +1,31 @@
+/*
+ * 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.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.qt.core.index.IQMethod;
+import org.eclipse.core.runtime.CoreException;
+
+public class QMethodName extends AbstractQObjectMemberName implements IQtASTName {
+
+ private final IQMethod.Kind kind;
+ private final String qtEncSignatures;
+ private final Long revision;
+
+ public QMethodName(QObjectName qobjName, IASTName cppName, IQMethod.Kind kind, String qtEncSignatures, Long revision) {
+ super(qobjName, cppName, cppName.getLastName().toString(), cppName.getImageLocation());
+ this.kind = kind;
+ this.qtEncSignatures = qtEncSignatures;
+ this.revision = revision;
+ }
+
+ @Override
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
+ return new QtPDOMQMethod(linkage, getOwner(linkage), this, delegate, kind, qtEncSignatures, revision);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTClass.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTClass.java
new file mode 100644
index 00000000000..7f873cf29c8
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTClass.java
@@ -0,0 +1,380 @@
+/*
+ * 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.qt.internal.core.pdom;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
+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.IASTNodeLocation;
+import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
+import org.eclipse.cdt.qt.core.QtKeywords;
+import org.eclipse.cdt.qt.core.index.IQMethod;
+
+/**
+ * The AST for a QObject is separated into regions based on macro expansions. These
+ * regions determine the Qt kind for methods that are declared within them.
+ *
+ * This utility class makes one pass over the C++ class specification to identify
+ * all such regions. It also provides an iterator that can be used while examining
+ * the class spec's members.
+ */
+public class QtASTClass {
+
+ private final Iterator regions;
+ private final Iterator tags;
+ private final Iterator revisions;
+ private Region region;
+ private Tag tag;
+ private Revision revision;
+
+ /**
+ * Must only be called with increasing offset. Internal pointers may be advanced on
+ * each call.
+ */
+ public IQMethod.Kind getKindFor(int offset) {
+
+ // There are 3 steps:
+ // 1) The tags counter must always be advanced. Tags only apply to the next declaration
+ // and therefore the internal counter must always be advanced. Multiple tags are
+ // collapsed to find the highest precedence value.
+ // 2) The region counter is advanced to find a region that either contains the offset
+ // or is the first region after the offset. Regions override tags, so we use the
+ // region kind if one is found.
+ // 3) The final result is based on tags (if they were present).
+ //
+ // This precedence is based on experimentation with the moc (ver 63). It
+ // ignores macros tagging a single method when that method is declared within
+ // a signal/slot region. E.g., the following example has two signals and one slot:
+ //
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // signals: void signal1();
+ // Q_SLOT void signal2(); /* Tagged with Q_SLOT, but the declaration is within the
+ // * signals region, so the moc considers it a signal. */
+ // public:
+ // Q_SLOT void slot1();
+ // };
+
+
+ // Consume all tags since the last declaration to find the highest precedence tag.
+ IQMethod.Kind kind = IQMethod.Kind.Unspecified;
+ while(tag != null && tag.offset < offset) {
+ kind = getHigherPrecedence(kind, tag.kind);
+ tag = tags.hasNext() ? tags.next() : null;
+ }
+
+ // Advance regions to find one that does not end before this offset.
+ while(region != null && region.end < offset)
+ region = regions.hasNext() ? regions.next() : null;
+
+ // If the offset is within this region, then use its kind.
+ if (region != null && region.contains(offset))
+ kind = region.kind;
+
+ return kind;
+ }
+
+ /**
+ * Must only be called with increasing offset. Internal pointers may be advanced on
+ * each call.
+ */
+ public Long getRevisionFor(int offset) {
+
+ // Consume all revisions since the last declaration to find one (if any) that applies
+ // to this declaration.
+ Long rev = null;
+ while(revision != null && revision.offset < offset) {
+ rev = revision.revision;
+ revision = revisions.hasNext() ? revisions.next() : null;
+ }
+
+ return rev;
+ }
+
+ private static IQMethod.Kind getHigherPrecedence(IQMethod.Kind kind1, IQMethod.Kind kind2) {
+ switch(kind1) {
+ case Unspecified:
+ return kind2;
+ case Invokable:
+ switch(kind2) {
+ case Slot:
+ case Signal:
+ return kind2;
+ default:
+ return kind1;
+ }
+ case Signal:
+ if (kind2 == IQMethod.Kind.Slot)
+ return kind2;
+ return kind2;
+ case Slot:
+ return kind1;
+ }
+ return IQMethod.Kind.Unspecified;
+ }
+
+ public static QtASTClass create(ICPPASTCompositeTypeSpecifier spec) {
+
+ // There is more detail in Bug 401696 describing why this needs to look at all
+ // the node locations. Briefly, the CDT parser does not associate empty macros
+ // with the function when they are the first thing in the declaration. E.g.,
+ //
+ // #define X
+ // void func1() {}
+ // X void func2() {}
+ //
+ // Could also look like:
+ // void func1() {} X
+ // void func2() {}
+ //
+ // The nodes are processed in three stages which are described in detail below. Only
+ // the first stage looks at the nodes, the later stages just cleanup results from the
+ // first walk over all node locations.
+
+ // 1) Examine the locations to find all macro expansions. This finds a beginning and
+ // highest possible end for the regions. It also locates the offset for single-method
+ // tags (including resolving precedence).
+ // This allows single-method tags to overlap regions because regions may be shortened
+ // by a later step.
+ ArrayList tags = new ArrayList();
+ ArrayList revisions = new ArrayList();
+ ArrayList regions = new ArrayList();
+ Region currRegion = null;
+ for(IASTNodeLocation location : spec.getNodeLocations()) {
+
+ Tag tag = Tag.create(location);
+ if (tag != null)
+ tags.add(tag);
+
+ Revision revision = Revision.create(location);
+ if (revision != null)
+ revisions.add(revision);
+
+ Region region = Region.create(location);
+ if (region != null) {
+ if (currRegion != null)
+ currRegion.end = region.begin;
+
+ currRegion = region;
+ regions.add(region);
+ }
+ }
+
+ // 2) Make the regions smaller where visibility labels are introduced.
+ if (!regions.isEmpty()) {
+ Iterator iterator = regions.iterator();
+ Region region = iterator.next();
+ for (IASTDeclaration decl : spec.getMembers()) {
+
+ // Ignore everything other than visibility labels.
+ if (!(decl instanceof ICPPASTVisibilityLabel))
+ continue;
+
+ int offset = decl.getFileLocation().getNodeOffset();
+
+ // Otherwise terminate all regions that start before this label and advance
+ // to the first one that follows.
+ while(region != null && region.begin < offset) {
+ region.end = offset;
+ region = iterator.hasNext() ? iterator.next() : null;
+ }
+
+ // Stop searching for visibility labels after the last region has been terminated.
+ if (region == null)
+ break;
+ }
+ }
+
+ // 3) Eliminate tags that are within regions.
+ if (!tags.isEmpty()) {
+ Iterator iterator = tags.iterator();
+ Tag tag = iterator.next();
+ for(Region region : regions) {
+
+ // Keep all tags that are before the start of this region.
+ while(tag != null && tag.offset < region.begin)
+ tag = iterator.hasNext() ? iterator.next() : null;
+
+ // Delete all tags that are within this region.
+ while(tag != null && region.contains(tag.offset)) {
+ iterator.remove();
+ tag = iterator.hasNext() ? iterator.next() : null;
+ }
+
+ // Stop searching when there are no more tags to be examined.
+ if (tag == null)
+ break;
+ }
+ }
+
+ return new QtASTClass(regions, tags, revisions);
+ }
+
+ private QtASTClass(List regions, List tags, List revisions) {
+ this.regions = regions.iterator();
+ this.tags = tags.iterator();
+ this.revisions = revisions.iterator();
+
+ this.region = this.regions.hasNext() ? this.regions.next() : null;
+ this.tag = this.tags.hasNext() ? this.tags.next() : null;
+ this.revision = this.revisions.hasNext() ? this.revisions.next() : null;
+ }
+
+ private static class Region {
+ public final int begin;
+ public int end = Integer.MAX_VALUE;
+ public final IQMethod.Kind kind;
+
+ public Region(int begin, IQMethod.Kind kind) {
+ this.begin = begin;
+ this.kind = kind;
+ }
+
+ public boolean contains(int offset) {
+ return offset >= begin
+ && offset < end;
+ }
+
+ /**
+ * Return a region for the given location or null if the location does not
+ * introduce a region.
+ */
+ public static Region create(IASTNodeLocation location) {
+ if (!(location instanceof IASTMacroExpansionLocation))
+ return null;
+
+ IASTMacroExpansionLocation macroLocation = (IASTMacroExpansionLocation) location;
+ IASTFileLocation fileLocation = macroLocation.asFileLocation();
+ if (fileLocation == null)
+ return null;
+
+ int offset = fileLocation.getNodeOffset();
+ IASTPreprocessorMacroExpansion expansion = macroLocation.getExpansion();
+ String macroName = getMacroName(expansion);
+ if (QtKeywords.Q_SLOTS.equals(macroName)
+ || QtKeywords.SLOTS.equals(macroName))
+ return new Region(offset, IQMethod.Kind.Slot);
+ if (QtKeywords.Q_SIGNALS.equals(macroName)
+ || QtKeywords.SIGNALS.equals(macroName))
+ return new Region(offset, IQMethod.Kind.Signal);
+ return null;
+ }
+ }
+
+ private static class Tag {
+ public final int offset;
+ public IQMethod.Kind kind;
+
+ private Tag(int begin, IQMethod.Kind kind) {
+ this.offset = begin;
+ this.kind = kind;
+ }
+
+ /**
+ * Return a tag for the given location or null if the location does not
+ * introduce a tag.
+ */
+ public static Tag create(IASTNodeLocation location) {
+ if (!(location instanceof IASTMacroExpansionLocation))
+ return null;
+
+ IASTMacroExpansionLocation macroLocation = (IASTMacroExpansionLocation) location;
+ IASTFileLocation fileLocation = macroLocation.asFileLocation();
+ if (fileLocation == null)
+ return null;
+
+ int offset = fileLocation.getNodeOffset();
+ IASTPreprocessorMacroExpansion expansion = macroLocation.getExpansion();
+ String macroName = getMacroName(expansion);
+ if (QtKeywords.Q_SLOT.equals(macroName))
+ return new Tag(offset, IQMethod.Kind.Slot);
+ if (QtKeywords.Q_SIGNAL.equals(macroName))
+ return new Tag(offset, IQMethod.Kind.Signal);
+ if (QtKeywords.Q_INVOKABLE.equals(macroName))
+ return new Tag(offset, IQMethod.Kind.Invokable);
+ return null;
+ }
+ }
+
+ private static class Revision {
+ private final int offset;
+ private final Long revision;
+
+ // This regular expression matches Q_REVISION macro expansions. It allows C++ integer
+ // literals as the expansion parameter. The integer literal is provided in capture
+ // group 1. Hexadecimal and octal prefixes are included in the capture group. Unsigned
+ // and long suffixes are allowed but are excluded from the capture group. The matcher's
+ // input string should be trimmed and have all newlines replaced.
+ private static final Pattern QREVISION_REGEX = Pattern.compile("^Q_REVISION\\s*\\(\\s*((?:0x)?[\\da-fA-F]+)[ulUL]*\\s*\\)$");
+
+ public Revision(int offset, Long revision) {
+ this.offset = offset;
+ this.revision = revision;
+ }
+
+ /**
+ * Return a tag for the given location or null if the location does not
+ * introduce a tag.
+ */
+ public static Revision create(IASTNodeLocation location) {
+ if (!(location instanceof IASTMacroExpansionLocation))
+ return null;
+
+ IASTMacroExpansionLocation macroLocation = (IASTMacroExpansionLocation) location;
+ IASTFileLocation fileLocation = macroLocation.asFileLocation();
+ if (fileLocation == null)
+ return null;
+
+ int offset = fileLocation.getNodeOffset();
+ IASTPreprocessorMacroExpansion expansion = macroLocation.getExpansion();
+ String macroName = getMacroName(expansion);
+ if (!QtKeywords.Q_REVISION.equals(macroName))
+ return null;
+
+ String raw = expansion.getRawSignature();
+ if (raw == null)
+ return null;
+
+ // Trim leading and trailing whitespace and remove all newlines.
+ Matcher m = QREVISION_REGEX.matcher(raw.trim().replaceAll("\\s+", ""));
+ if (m.matches()) {
+ try {
+ return new Revision(offset, Long.parseLong(m.group(1)));
+ } catch(NumberFormatException e) {
+ // The number will be parsed incorrectly when the C++ client code does not
+ // contain a valid integer. We can't do anything about that, so the exception
+ // is ignored. A codan checker could notify the user of this problem.
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Find and return the simple name of the macro that is being expanded or null if the name
+ * cannot be found.
+ */
+ private static String getMacroName(IASTPreprocessorMacroExpansion expansion) {
+ if (expansion == null)
+ return null;
+
+ IASTName name = expansion.getMacroReference();
+ return name == null ? null : name.toString();
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java
index 6b5256e966b..32c2b12234e 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java
@@ -17,22 +17,28 @@ import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
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.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
+import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
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.qt.core.QtKeywords;
+import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.cdt.qt.internal.core.QtMethodUtil;
import org.eclipse.cdt.qt.internal.core.index.QProperty;
@SuppressWarnings("restriction")
@@ -140,12 +146,7 @@ public class QtASTVisitor extends ASTVisitor {
IBinding[] bindings = CPPSemantics.findBindingsForQualifiedName(spec.getScope(), alias == null ? name : alias);
for(IBinding binding : bindings) {
// Create a reference from this Qt name to the target enum's definition.
- IASTName cppName = null;
- if (binding instanceof ICPPInternalBinding) {
- IASTNode node = ((ICPPInternalBinding) binding).getDefinition();
- cppName = node instanceof IASTName ? (IASTName) node : null;
- }
-
+ IASTName cppName = findASTName(binding);
QtEnumName astName = new QtEnumName(qobjName, refName, name, cppName, location, isFlag);
symbols.add(owner, astName, qobjName);
@@ -173,7 +174,9 @@ public class QtASTVisitor extends ASTVisitor {
Map flagAliases = new HashMap();
for (IASTPreprocessorMacroExpansion expansion : expansions) {
- String macroName = String.valueOf(expansion.getMacroReference());
+
+ IASTName name = expansion.getMacroReference();
+ String macroName = name == null ? null : name.toString();
if (QtKeywords.Q_OBJECT.equals(macroName))
continue;
@@ -199,6 +202,9 @@ public class QtASTVisitor extends ASTVisitor {
handleQPropertyDefn(owner, qobjName, expansion);
}
+ // Process the slot, signal, and invokable method declarations.
+ extractQMethods(owner, spec, qobjName);
+
for(EnumDecl decl : enumDecls)
decl.handle(owner, spec, qobjName, flagAliases);
}
@@ -386,4 +392,63 @@ public class QtASTVisitor extends ASTVisitor {
}
public static final AttrValue None = new AttrValue(0, null);
}
+
+ private void extractQMethods(IASTPreprocessorIncludeStatement owner, ICPPASTCompositeTypeSpecifier spec, QObjectName qobjName) {
+ QtASTClass qtASTClass = QtASTClass.create(spec);
+ for (IASTDeclaration decl : spec.getMembers()) {
+
+ // We only care about this node if it is within a signal/slot region or if it
+ // has been tagged with a Qt annotating tag.
+ int offset = decl.getFileLocation().getNodeOffset();
+ IQMethod.Kind kind = qtASTClass.getKindFor(offset);
+ Long revision = qtASTClass.getRevisionFor(offset);
+ if (kind == IQMethod.Kind.Unspecified)
+ continue;
+
+ // Only named methods are processed, so skip this node if it is not a function or
+ // if it does not have a name.
+ IASTSimpleDeclaration simpleDecl = getSimpleDecl(decl);
+ if (simpleDecl == null)
+ continue;
+
+ ICPPASTFunctionDeclarator decltor = null;
+ for(IASTDeclarator d : simpleDecl.getDeclarators())
+ if (d instanceof ICPPASTFunctionDeclarator) {
+ decltor = (ICPPASTFunctionDeclarator) d;
+ break;
+ }
+ if (decltor == null)
+ continue;
+
+ IASTName cppName = decltor.getName();
+ if (cppName == null)
+ continue;
+
+ String qtEncSignatures = QtMethodUtil.getEncodedQtMethodSignatures(decltor);
+ symbols.add(owner, new QMethodName(qobjName, cppName, kind, qtEncSignatures, revision), qobjName);
+ }
+ }
+
+ private static IASTSimpleDeclaration getSimpleDecl(IASTNode node) {
+ while (node != null && !(node instanceof IASTSimpleDeclaration))
+ 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/internal/core/pdom/QtEnumName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
index fa766ffb697..8827fb0af3e 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
@@ -10,7 +10,7 @@ package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.core.runtime.CoreException;
-public class QtEnumName extends AbstractQObjectFieldName implements IQtASTName {
+public class QtEnumName extends AbstractQObjectMemberName implements IQtASTName {
private final IASTName cppEnumName;
private final boolean isFlag;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
index 293b797f216..d06054aefec 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
@@ -8,7 +8,6 @@
package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTName;
-import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
@@ -17,9 +16,12 @@ import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public abstract class QtPDOMBinding extends PDOMBinding {
+ // The offsetInitializer is initialized with the size of the parent. It is incremented
+ // during loading of the Fields enum. This value does not reliably store the size of
+ // the QtPDOMBinding record because the enum will not be initialized until it is needed.
+ // The record size is retrieved as the offset of the special terminal enumerator Last.
private static int offsetInitializer = RECORD_SIZE;
protected static enum Field {
- CppRecord(Database.PTR_SIZE),
Last(0);
public final int offset;
@@ -46,28 +48,6 @@ public abstract class QtPDOMBinding extends PDOMBinding {
protected int getRecordSize() {
return Field.Last.offset;
}
-//
-// public long getCppRecord() {
-// try {
-// return getDB().getRecPtr(Field.CppRecord.getRecord(record));
-// } catch (CoreException e) {
-// QtPlugin.log(e);
-// }
-//
-// return 0;
-// }
-//
-// public IBinding getCppBinding() throws CoreException {
-// long cppRec = getCppRecord();
-// if (cppRec == 0)
-// return null;
-//
-// PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
-// if (cppLinkage == null)
-// return null;
-//
-// return cppLinkage.getBinding(cppRec);
-// }
protected QtPDOMLinkage getQtLinkage() {
PDOMLinkage pdomLinkage = getLinkage();
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java
index ab4f4ec2649..86734c22aa1 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java
@@ -17,7 +17,7 @@ public enum QtPDOMNodeType {
QObject,
QEnum,
QProperty,
- QPropertyAttribute;
+ QMethod;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
@@ -28,7 +28,7 @@ public enum QtPDOMNodeType {
*
* The version is needed because ordinals for these enumerators are written to the file.
*/
- public static final int VERSION = 3;
+ public static final int VERSION = 4;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
@@ -52,6 +52,8 @@ public enum QtPDOMNodeType {
return new QtPDOMQEnum(linkage, record);
case QProperty:
return new QtPDOMProperty(linkage, record);
+ case QMethod:
+ return new QtPDOMQMethod(linkage, record);
}
return null;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java
index a414295a5e5..48823bab01f 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java
@@ -7,11 +7,6 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
-import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.core.dom.ast.ICompositeType;
-import org.eclipse.cdt.core.dom.ast.IField;
-import org.eclipse.cdt.core.dom.ast.IType;
-import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IString;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
@@ -20,7 +15,7 @@ import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
-public class QtPDOMProperty extends QtPDOMBinding implements IField {
+public class QtPDOMProperty extends QtPDOMBinding {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
@@ -40,8 +35,6 @@ public class QtPDOMProperty extends QtPDOMBinding implements IField {
}
}
- private QtPDOMQObject qobj;
-
public QtPDOMProperty(QtPDOMLinkage linkage, long record) {
super(linkage, record);
}
@@ -51,12 +44,8 @@ public class QtPDOMProperty extends QtPDOMBinding implements IField {
setType(qtName.getType());
- if (!(parent instanceof QtPDOMQObject))
- this.qobj = null;
- else {
- this.qobj = (QtPDOMQObject) parent;
- this.qobj.addChild(this);
- }
+ if (parent instanceof QtPDOMQObject)
+ ((QtPDOMQObject) parent).addChild(this);
}
@Override
@@ -93,8 +82,8 @@ public class QtPDOMProperty extends QtPDOMBinding implements IField {
getDB().putRecPtr(Field.Type.getRecord(record), getDB().newString(type).getRecord());
}
- // TODO IType?
- public String getTypeStr() throws CoreException {
+ // IType?
+ public String getType() throws CoreException {
long rec = getDB().getRecPtr(Field.Type.getRecord(record));
if (rec == 0)
return null;
@@ -115,66 +104,6 @@ public class QtPDOMProperty extends QtPDOMBinding implements IField {
return pdomArray.get();
}
- @Override
- public ICompositeType getCompositeTypeOwner() {
- if (qobj == null)
- try {
- IBinding parent = getParentBinding();
- if (parent instanceof QtPDOMQObject)
- qobj = (QtPDOMQObject) parent;
- } catch(CoreException e) {
- QtPlugin.log(e);
- }
-
- return qobj;
- }
-
- /**
- * TODO use the real type?
- */
- private static final IType Type = new IType() {
- @Override
- public Object clone() {
- // This is a stateless singleton instance, there is nothing to clone.
- return this;
- }
-
- @Override
- public boolean isSameType(IType type) {
- return type == this;
- }
- };
-
- @Override
- public IType getType() {
- return Type;
- }
-
- @Override
- public IValue getInitialValue() {
- return null;
- }
-
- @Override
- public boolean isStatic() {
- return false;
- }
-
- @Override
- public boolean isExtern() {
- return false;
- }
-
- @Override
- public boolean isAuto() {
- return false;
- }
-
- @Override
- public boolean isRegister() {
- return false;
- }
-
public static class Attribute {
public final IQProperty.Attribute attr;
public final String value;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
index 753f5f0c257..ddf2e974278 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
@@ -13,21 +13,15 @@ import java.util.List;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
-import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
-import org.eclipse.cdt.core.dom.ast.IField;
-import org.eclipse.cdt.core.dom.ast.IType;
-import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
-import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
-public class QtPDOMQEnum extends QtPDOMBinding implements IField {
+public class QtPDOMQEnum extends QtPDOMBinding {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
@@ -112,68 +106,8 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
return QtPDOMNodeType.QEnum.Type;
}
- @Override
- public ICompositeType getCompositeTypeOwner() {
- if (qobj == null)
- try {
- IBinding parent = getParentBinding();
- if (parent instanceof QtPDOMQObject)
- qobj = (QtPDOMQObject) parent;
- } catch(CoreException e) {
- QtPlugin.log(e);
- }
-
- return qobj;
- }
-
public List getEnumerators() throws CoreException {
IEnumeration cppEnum = getCppEnumeration();
return cppEnum == null ? Collections.emptyList() : Arrays.asList(cppEnum.getEnumerators());
}
-
- /**
- * A singleton that is used as the type for all instances of the QtEnum.
- */
- private static final IType Type = new IType() {
- @Override
- public Object clone() {
- // This is a stateless singleton instance, there is nothing to clone.
- return this;
- }
-
- @Override
- public boolean isSameType(IType type) {
- return type == this;
- }
- };
-
- @Override
- public IType getType() {
- return Type;
- }
-
- @Override
- public IValue getInitialValue() {
- return null;
- }
-
- @Override
- public boolean isStatic() {
- return false;
- }
-
- @Override
- public boolean isExtern() {
- return false;
- }
-
- @Override
- public boolean isAuto() {
- return false;
- }
-
- @Override
- public boolean isRegister() {
- return false;
- }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQMethod.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQMethod.java
new file mode 100644
index 00000000000..98657eadf4c
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQMethod.java
@@ -0,0 +1,115 @@
+/*
+ * 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.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.internal.core.pdom.db.Database;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
+import org.eclipse.cdt.qt.core.index.IQMethod;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public class QtPDOMQMethod extends QtPDOMBinding {
+
+ private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
+ protected static enum Field {
+ Signature(Database.PTR_SIZE),
+ Revision(8),
+ Flags(1),
+ Last(0);
+
+ public final int offset;
+
+ private Field(int sizeof) {
+ this.offset = offsetInitializer;
+ offsetInitializer += sizeof;
+ }
+
+ public long getRecord(long baseRec) {
+ return baseRec + offset;
+ }
+ }
+
+ private static final int KIND_IS_INVOKABLE = 1;
+ private static final int KIND_IS_SIGNAL = 2;
+ private static final int KIND_IS_SLOT = 3;
+ private static final int KIND_MASK = 3;
+ private static final int HAS_REVISION = 4;
+
+ public QtPDOMQMethod(QtPDOMLinkage linkage, long record) throws CoreException {
+ super(linkage, record);
+ }
+
+ public QtPDOMQMethod(QtPDOMLinkage linkage, PDOMBinding parent, IASTName qtName, IASTName cppName, IQMethod.Kind kind, String qtEncSignatures, Long revision) throws CoreException {
+ super(linkage, parent, qtName);
+
+ byte flag = 0;
+ switch(kind) {
+ case Invokable:
+ flag |= KIND_IS_INVOKABLE;
+ break;
+ case Signal:
+ flag |= KIND_IS_SIGNAL;
+ break;
+ case Slot:
+ flag |= KIND_IS_SLOT;
+ break;
+ case Unspecified:
+ break;
+ }
+
+ if (revision != null) {
+ flag |= HAS_REVISION;
+ getDB().putLong(Field.Revision.getRecord(record), revision.longValue());
+ }
+
+ getDB().putByte(Field.Flags.getRecord(record), flag);
+
+ long rec = qtEncSignatures == null ? 0 : getDB().newString(qtEncSignatures).getRecord();
+ getDB().putRecPtr(Field.Signature.getRecord(record), rec);
+
+ if (parent instanceof QtPDOMQObject)
+ ((QtPDOMQObject) parent).addChild(this);
+ }
+
+ @Override
+ protected int getRecordSize() {
+ return Field.Last.offset;
+ }
+
+ public IQMethod.Kind getKind() throws CoreException {
+ switch(getDB().getByte(Field.Flags.getRecord(record)) & KIND_MASK) {
+ case KIND_IS_INVOKABLE:
+ return IQMethod.Kind.Invokable;
+ case KIND_IS_SIGNAL:
+ return IQMethod.Kind.Signal;
+ case KIND_IS_SLOT:
+ return IQMethod.Kind.Slot;
+ default:
+ return IQMethod.Kind.Unspecified;
+ }
+ }
+
+ public String getQtEncodedSignatures() throws CoreException {
+ long rec = getDB().getRecPtr(Field.Signature.getRecord(record));
+ return rec == 0 ? null : getDB().getString(rec).getString();
+ }
+
+ public Long getRevision() throws CoreException {
+ byte flag = getDB().getByte(Field.Flags.getRecord(record));
+ if ((flag & HAS_REVISION) == 0)
+ return null;
+
+ return Long.valueOf(getDB().getLong(Field.Revision.getRecord(record)));
+ }
+
+ @Override
+ public int getNodeType() {
+ return QtPDOMNodeType.QMethod.Type;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java
index 675b053d27a..e8c0e54026e 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java
@@ -17,10 +17,6 @@ import java.util.Map;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.core.dom.ast.ICompositeType;
-import org.eclipse.cdt.core.dom.ast.IField;
-import org.eclipse.cdt.core.dom.ast.IScope;
-import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.internal.core.pdom.db.Database;
@@ -36,18 +32,13 @@ import org.eclipse.core.runtime.CoreException;
* The persisted form of QObjects.
*/
@SuppressWarnings("restriction")
-public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
+public class QtPDOMQObject extends QtPDOMBinding {
- // The RecordSize is initialized with the size of the parent. It is incremented during
- // loading of the Fields enum. This value does not reliably store the size of the
- // QtPDOMQObject record because the enum will not be initialized until it is needed.
- // The record size is retrieved as the offset of the special terminal enumerator Last.
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
CppRecord(Database.PTR_SIZE, 3),
Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */, 0),
ClassInfos(Database.PTR_SIZE, 2),
- Properties(Database.PTR_SIZE, 3),
Last(0, 0);
private final int offset;
@@ -115,12 +106,6 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
return null;
PDOMBinding cppBinding = cppLinkage.getBinding(cppRec);
-
- // TODO
- if (cppBinding == null)
- return null;
- cppBinding.getAdapter(ICPPClassType.class);
-
return cppBinding instanceof ICPPClassType ? (ICPPClassType) cppBinding : null;
}
@@ -208,35 +193,12 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
return bases;
}
- @Override
- public boolean isSameType(IType type) {
- if (type == this)
- return true;
-
- if (!(type instanceof QtPDOMQObject))
- return false;
-
- QtPDOMQObject other = (QtPDOMQObject) type;
- return getRecord() == other.getRecord()
- && getLinkage().equals(other.getLinkage());
- }
-
- @Override
- public int getKey() {
- return ICPPClassType.k_class;
- }
-
- @Override
- public boolean isAnonymous() {
- return false;
- }
-
@Override
public void addChild(PDOMNode child) throws CoreException {
children.addMember(child);
}
- public List getFields(Class cls) throws CoreException {
+ public List getChildren(Class cls) throws CoreException {
QtPDOMVisitor.All collector = new QtPDOMVisitor.All(cls);
try {
children.accept(collector);
@@ -248,47 +210,6 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
return collector.list;
}
- @Override
- public IField[] getFields() {
- QtPDOMVisitor.All collector = new QtPDOMVisitor.All(IField.class);
- try {
- children.accept(collector);
- } catch(CoreException e) {
- QtPlugin.log(e);
- return IField.EMPTY_FIELD_ARRAY;
- }
-
- return collector.list.toArray(new IField[collector.list.size()]);
- }
-
- @Override
- public IField findField(String name) {
- QtPDOMVisitor.IFilter filter = new QtPDOMVisitor.PDOMNamedNodeFilter(name);
- QtPDOMVisitor.Find finder = new QtPDOMVisitor.Find(IField.class, filter);
- try {
- accept(finder);
- } catch(CoreException e) {
- QtPlugin.log(e);
- }
- return finder.element;
- }
-
- @Override
- public IScope getCompositeScope() {
- try {
- return getCppClassType().getCompositeScope();
- } catch(CoreException e) {
- QtPlugin.log(e);
- }
-
- return null;
- }
-
- @Override
- public Object clone() {
- throw new UnsupportedOperationException();
- }
-
private static class ClassInfo {
public final String key;
public final String value;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java
index 5f3ce6ff806..36b19fef9e7 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java
@@ -17,9 +17,10 @@ import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
-public class QtPropertyName extends AbstractQObjectFieldName implements IQtASTName {
+public class QtPropertyName extends AbstractQObjectMemberName implements IQtASTName {
private String type;
+ // TODO The PDOM attrs should only be created in #createPDOMBinding
private List attributes = new ArrayList();
public QtPropertyName(QObjectName qobjName, IASTName ast, String name, QtASTImageLocation location) {
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java
index 55a6c6f45c3..fb9ee92b740 100644
--- a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java
@@ -17,6 +17,7 @@ public class AllQtTests extends TestSuite {
new TestSuite(
SimpleTests.class,
QObjectTests.class,
- QtIndexTests.class);
+ QtIndexTests.class,
+ QtRegressionTests.class);
}
}
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 cd5ea297696..5a3dfcd8071 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
@@ -16,6 +16,7 @@ import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.qt.core.index.IQEnum;
+import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.cdt.qt.core.index.IQProperty.Attribute;
@@ -415,4 +416,106 @@ public class QObjectTests extends BaseQtTestCase {
}
assertTrue("missing properties " + missingAttrs.toString(), missingAttrs.length() == 0);
}
+
+ // #include "junit-QObject.hh"
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // signals:
+ // public: void notASignal();
+ // Q_SIGNALS: void signal();
+ // public: void notAnotherSignal();
+ // Q_SIGNAL void anotherSignal();
+ // };
+ public void testSimpleSignal() throws Exception {
+ loadComment("simple_signal.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ IQObject.IMembers signals = qobj.getSignals();
+ assertNotNull(signals);
+
+ Collection locals = signals.locals();
+ assertNotNull(locals);
+
+ Iterator i = locals.iterator();
+ assertTrue(i.hasNext());
+ assert_checkQMethod(i.next(), qobj, "signal", IQMethod.Kind.Signal, null);
+ assertTrue(i.hasNext());
+ assert_checkQMethod(i.next(), qobj, "anotherSignal", IQMethod.Kind.Signal, null);
+ assertFalse(i.hasNext());
+ }
+
+ // #include "junit-QObject.hh"
+ // template class QList {};
+ // class QString {};
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ //
+ // // From the QML test suite -- this is not valid C++. The Qt moc generates duplicate const,
+ // // but our CDT-based implementation is not able to do the same. Instead we generate what
+ // // would be the correct C++ signature.
+ // Q_INVOKABLE void someFunc(const QList const &p1, QString p2 = "Hello");
+ //
+ // // variations on the above
+ // Q_INVOKABLE void someFunc1(const QList &p1, QString p2 = "Hello");
+ // Q_INVOKABLE void someFunc2(QList const &p1, QString p2 = "Hello");
+ // Q_INVOKABLE void someFunc3(const QList &p1, QString p2 = "Hello");
+ // Q_INVOKABLE void someFunc4(const QList &p1, QString p2 = "Hello");
+ // Q_INVOKABLE void someFunc5(const QList &p1, QString p2 = "Hello") const;
+ // Q_INVOKABLE void someFunc6(const QList &p1, QString p2 = "Hello");
+ // };
+ public void testInvokables() throws Exception {
+ loadComment("invokables.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ IQObject.IMembers invokables = qobj.getInvokables();
+ assertNotNull(invokables);
+ assertEquals(7, invokables.locals().size());
+
+ for(IQMethod invokable : invokables.locals()) {
+
+ assertTrue(invokable.getName(), qobj == invokable.getOwner());
+ assertEquals(invokable.getName(), IQMethod.Kind.Invokable, invokable.getKind());
+ assertNull(invokable.getRevision());
+
+ if ("someFunc".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc(QList,QString)"));
+ else if ("someFunc1".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc1(QList,QString)"));
+ else if ("someFunc2".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc2(QList,QString)"));
+ else if ("someFunc3".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc3(QList,QString)"));
+ else if ("someFunc4".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc4(QList,QString)"));
+ else if ("someFunc5".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc5(QList,QString)"));
+ else if ("someFunc6".equals(invokable.getName()))
+ assertTrue(invokable.getSignatures().contains("someFunc6(QList,QString)"));
+ else
+ fail("unexpected invokable " + invokable.getName());
+ }
+ }
+
+ 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());
+ }
}
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
new file mode 100644
index 00000000000..bf168b7c588
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtRegressionTests.java
@@ -0,0 +1,337 @@
+/*
+ * 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.qt.tests;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.qt.core.index.IQMethod;
+import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.QtIndex;
+
+public class QtRegressionTests extends BaseQtTestCase {
+
+ private static Map> buildExpectedMap(String mocOutput) {
+ Map> expected = new HashMap>();
+ for(String moc_signature : mocOutput.split("\0")) {
+ String name = moc_signature.split("\\(")[0];
+ Set set = expected.get(name);
+ if (set == null) {
+ set = new HashSet();
+ expected.put(name, set);
+ }
+ set.add(moc_signature);
+ }
+
+ return expected;
+ }
+
+ // #include "junit-QObject.hh"
+ // struct T {};
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // public:
+ // void func();
+ // signals:
+ // void sig_int(int i = 5);
+ // void sig_const_int(const int i = 5);
+ // void sig_T(T * t = 0);
+ // void sig_const_T(const T * const t = 0);
+ // };
+ public void testDefaultParameters() throws Exception {
+ loadComment("defaultParams.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ // Based on the moc output, but modified to manage our handling for default parameters. The
+ // moc generates two signatures, sig(N::TS::M) and sig(), we just mark optional parameters
+ // with a trailing =. However, QMethod#getSignature is currently modified to strip the
+ // default value indication. So, we're only dealing with the full signature here.
+ String moc = "sig_int(int)\0sig_int()\0sig_const_int(int)\0"
+ + "sig_const_int()\0sig_T(T*)\0sig_T()\0"
+ + "sig_const_T(T*const)\0sig_const_T()\0";
+ Map> expected = buildExpectedMap(moc);
+
+ IQObject.IMembers sigs = qobj.getSignals();
+ assertNotNull(sigs);
+ Collection locals = sigs.locals();
+ assertNotNull(locals);
+ for(IQMethod method : locals) {
+ Set set = expected.get(method.getName());
+ assertNotNull("unexpected method " + method.getName() + " (" + method.getSignatures() + ')', set);
+ for(String signature : method.getSignatures()) {
+ assertTrue(set.remove(signature));
+ }
+ assertTrue("did not find all signatures for " + method.getName(), set.isEmpty());
+ expected.remove(method.getName());
+ }
+ assertEquals(0, expected.size());
+ }
+
+ // #include "junit-QObject.hh"
+ // typedef int Tl1;
+ // typedef Tl1 Tl2;
+ // enum _E {};
+ // struct S { typedef int M; typedef char Mb; };
+ // template struct S_TEMPLATE { };
+ // namespace N
+ // {
+ // typedef int Ib;
+ // typedef _E Eb;
+ // enum E {};
+ // namespace N2 { enum E2 {}; typedef E2 TE2; }
+ // typedef E TEa;
+ // typedef TEa TEb;
+ // typedef N2::E2 N2_E2;
+ // typedef N2::TE2 N2_TE2;
+ // typedef S TS;
+ // }
+ // typedef N::E N_E;
+ // namespace N2 { typedef N::Ib Ic; }
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // public:
+ // void func();
+ // signals:
+ // void sig_int(int);
+ // void sig_Tl1(Tl1);
+ // void sig_Tl2(Tl2);
+ // void sig_int_ptr(int *);
+ // void sig_Tl1_ptr(Tl1 *);
+ // void sig_Tl2_ptr(Tl2 *);
+ //
+ // void sig_qual1(N::E);
+ // void sig_qual2(N::N2::E2);
+ // void sig_typedef1(N_E);
+ // void sig_typedef2(N::TEa);
+ // void sig_typedef3(N::TEb);
+ // void sig_typedef4(N::N2_E2);
+ // void sig_typedef5(N::N2_TE2);
+ // void sig_typedef6(N::N2::TE2);
+ //
+ // void sig_nested1(S::Mb);
+ // void sig_nested2(N::TS::M);
+ // void sig_nested3(N::Ib);
+ // void sig_nested4(N::Eb);
+ // void sig_nested5(N2::Ic);
+ //
+ // void sig_S ( S ); // sig_S ( S )
+ // void const_S (const S ); // const_S ( S )
+ // void S_const ( S const ); // S_const ( S )
+ // void S_ref ( S & ); // S_ref ( S & )
+ // void const_S_ref (const S & ); // const_S_ref ( S )
+ // void S_const_ref( S const &); // S_const_ref( S )
+ // void S_ptr ( S * );
+ // void S_ptr_const( S * const);
+ // void const_S_ptr (const S * );
+ // void const_S_ptr_const(const S * const);
+ // void const_S_ptr_const_def(const S * const s = 0);
+ // void S_ptr_ref ( S * &);
+ // void S_ptr_const_ref( S * const &);
+ // void const_S_ptr_const_ref(const S * const &);
+ // void S_ptr_ptr ( S * *);
+ // void S_ptr_const_ptr( S * const *);
+ // void const_S_ptr_ptr (const S * *);
+ // void const_S_ptr_const_ptr(const S * const *);
+ // void S_ptr_ptr_const ( S * * const);
+ // void S_ptr_const_ptr_const( S * const * const);
+ // void const_S_ptr_ptr_const (const S * * const);
+ // void const_S_ptr_const_ptr_const(const S * const * const);
+ //
+ // void S_template_1(const S_TEMPLATE & p);
+ // void S_template_2(const S_TEMPLATE & p);
+ // void S_template_3(S_TEMPLATE const & p);
+ // void S_template_4(S_TEMPLATE const & p);
+ // void S_template_X(const S_TEMPLATE const & p);
+ // };
+ public void testBug338930() throws Exception {
+ loadComment("bug338930.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ // Copy and pasted moc output (signals only) to make sure we're getting an exact match.
+ String moc_output
+ = "sig_int(int)\0"
+ + "sig_Tl1(Tl1)\0sig_Tl2(Tl2)\0sig_int_ptr(int*)\0"
+ + "sig_Tl1_ptr(Tl1*)\0sig_Tl2_ptr(Tl2*)\0"
+ + "sig_qual1(N::E)\0sig_qual2(N::N2::E2)\0"
+ + "sig_typedef1(N_E)\0sig_typedef2(N::TEa)\0"
+ + "sig_typedef3(N::TEb)\0sig_typedef4(N::N2_E2)\0"
+ + "sig_typedef5(N::N2_TE2)\0"
+ + "sig_typedef6(N::N2::TE2)\0sig_nested1(S::Mb)\0"
+ + "sig_nested2(N::TS::M)\0sig_nested3(N::Ib)\0"
+ + "sig_nested4(N::Eb)\0sig_nested5(N2::Ic)\0"
+
+ + "sig_S(S)\0const_S(S)\0S_const(S)\0S_ref(S&)\0"
+ + "const_S_ref(S)\0S_const_ref(S)\0S_ptr(S*)\0"
+ + "S_ptr_const(S*const)\0const_S_ptr(const S*)\0"
+ + "const_S_ptr_const(S*const)\0const_S_ptr_const_def(S*const)\0"
+ + "const_S_ptr_const_def()\0S_ptr_ref(S*&)\0"
+ + "S_ptr_const_ref(S*)\0const_S_ptr_const_ref(S*const)\0"
+ + "S_ptr_ptr(S**)\0S_ptr_const_ptr(S*const*)\0"
+ + "const_S_ptr_ptr(const S**)\0"
+ + "const_S_ptr_const_ptr(const S*const*)\0"
+ + "S_ptr_ptr_const(S**const)\0"
+ + "S_ptr_const_ptr_const(S*const*const)\0"
+ + "const_S_ptr_ptr_const(S**const)\0"
+ + "const_S_ptr_const_ptr_const(S*const*const)\0"
+ + "S_template_1(S_TEMPLATE)\0"
+ + "S_template_2(S_TEMPLATE)\0"
+ + "S_template_3(S_TEMPLATE)\0"
+ + "S_template_4(S_TEMPLATE)\0"
+ + "S_template_X(S_TEMPLATE)";
+
+ Map> expected = buildExpectedMap(moc_output);
+
+ IQObject.IMembers sigs = qobj.getSignals();
+ assertNotNull(sigs);
+ Collection locals = sigs.locals();
+ assertNotNull(locals);
+ for(IQMethod method : locals) {
+ Set set = expected.get(method.getName());
+ assertNotNull("unexpected signal " + method.getName() + " (" + method.getSignatures() + ')', set);
+ for(String signature : method.getSignatures())
+ assertTrue(set.remove(signature));
+ assertTrue("did not find all signatures for " + method.getName(), set.isEmpty());
+ expected.remove(method.getName());
+ }
+ assertEquals(0, expected.size());
+ }
+
+ // #include "junit-QObject.hh"
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // Q_SLOT void const_ref(const QString &);
+ // Q_SLOT void const_val(const QString );
+ // Q_SLOT void reference( QString &);
+ // Q_SLOT void value( QString );
+ // enum E { };
+ // Q_SIGNAL void signalEnum_const_ref(const E &);
+ // Q_SIGNAL void signalEnum_reference(E &);
+ // Q_SIGNAL void signalEnum_qualified(Q::E);
+ // void func()
+ // {
+ // connect(this, SIGNAL(destroyed(QObject*), this, SLOT(const_ref(QString))));
+ // connect(this, SIGNAL(destroyed(QObject*), this, SLOT(const_val(QString))));
+ // connect(this, SIGNAL(signalEnum_const_ref(E), this, SLOT(reference(QString&))));
+ // connect(this, SIGNAL(signalEnum_reference(E&), this, SLOT(value(QString))));
+ // }
+ // };
+ public void testBug344931() throws Exception {
+ loadComment("bug344931.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ IQObject.IMembers slotMembers = qobj.getSlots();
+ assertNotNull(slotMembers);
+
+ Collection slots = slotMembers.locals();
+ assertNotNull(slots);
+ assertEquals(4, slots.size());
+
+ for(IQMethod slot : slots) {
+ if ("const_ref".equals(slot.getName()))
+ assertTrue(slot.getSignatures().contains("const_ref(QString)"));
+ else if ("const_val".equals(slot.getName()))
+ assertTrue(slot.getSignatures().contains("const_val(QString)"));
+ else if ("reference".equals(slot.getName()))
+ assertTrue(slot.getSignatures().contains("reference(QString&)"));
+ else if ("value".equals(slot.getName()))
+ assertTrue(slot.getSignatures().contains("value(QString)"));
+ else
+ fail("unexpected slot " + slot.getName());
+ }
+
+ IQObject.IMembers signalMembers = qobj.getSignals();
+ assertNotNull(signalMembers);
+
+ Collection signals = signalMembers.locals();
+ assertNotNull(signals);
+ assertEquals(3, signals.size());
+
+ for(IQMethod signal : signals) {
+ if ("signalEnum_const_ref".equals(signal.getName()))
+ assertTrue(signal.getSignatures().contains("signalEnum_const_ref(E)"));
+ else if ("signalEnum_reference".equals(signal.getName()))
+ assertTrue(signal.getSignatures().contains("signalEnum_reference(E&)"));
+ else if ("signalEnum_qualified".equals(signal.getName()))
+ assertTrue(signal.getSignatures().contains("signalEnum_qualified(Q::E)"));
+ else
+ fail("unexpected signal " + signal.getName());
+ }
+ }
+
+ // #include "junit-QObject.hh"
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // public:
+ // void func();
+ // private slots:
+ // void slot1();
+ // private:
+ // Q_SLOT void slot2();
+ // Q_SLOT void slot3();
+ // };
+ // void Q::slot1() { }
+ // void Q::slot2() { }
+ // void Q::func()
+ // {
+ // QObject::connect( this, destroyed( QObject * ), this, slot1() );
+ // QObject::connect( this, destroyed( QObject * ), this, slot2() );
+ // QObject::connect( this, destroyed( QObject * ), this, slot3() );
+ // }
+ public void testSlotDefn() throws Exception {
+ loadComment("slotDefn.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ IQObject.IMembers slots = qobj.getSlots();
+ assertNotNull(slots);
+ Collection localSlots = slots.locals();
+ assertNotNull(localSlots);
+
+ // make sure that the three slot functions are found, but none of the inherited or
+ // non-slot functions
+ Set expected = new HashSet(Arrays.asList("slot1", "slot2", "slot3"));
+ for(IQMethod method : localSlots)
+ assertTrue("unexpected slot " + method.getName(), expected.remove(method.getName()));
+ assertEquals("missing slots " + expected.toString(), 0, expected.size());
+ }
+}