diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java index 05737e8fc7e..d930e042f49 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java @@ -28,6 +28,11 @@ public interface ILinkage { */ final static String OBJC_LINKAGE_NAME= "Objective-C"; //$NON-NLS-1$ + /** + * @since 5.6 + */ + final static String QT_LINKAGE_NAME= "Qt"; //$NON-NLS-1$ + final static int NO_LINKAGE_ID= 0; final static int CPP_LINKAGE_ID= 1; final static int C_LINKAGE_ID= 2; @@ -37,7 +42,12 @@ public interface ILinkage { */ final static int OBJC_LINKAGE_ID= 4; - + /** + * @since 5.6 + */ + final static int QT_LINKAGE_ID= 5; + + /** * Additional linkage ids may be added in future. */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java index 4414f2d530a..f178e9fc2e1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java @@ -20,10 +20,11 @@ public class Linkage implements ILinkage { public static final ILinkage CPP_LINKAGE = new Linkage(CPP_LINKAGE_ID, CPP_LINKAGE_NAME); public static final ILinkage FORTRAN_LINKAGE = new Linkage(FORTRAN_LINKAGE_ID, FORTRAN_LINKAGE_NAME); public static final ILinkage OBJC_LINKAGE = new Linkage(OBJC_LINKAGE_ID, OBJC_LINKAGE_NAME); - - private static final ILinkage[] LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE, OBJC_LINKAGE }; + public static final ILinkage QT_LINKAGE = new Linkage(QT_LINKAGE_ID, QT_LINKAGE_NAME); + + private static final ILinkage[] LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE, OBJC_LINKAGE, QT_LINKAGE }; private static final ILinkage[] INDEX_LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE }; - + public static final ILinkage[] getIndexerLinkages() { return INDEX_LINKAGES; } @@ -39,6 +40,7 @@ public class Linkage implements ILinkage { case CPP_LINKAGE_ID: return CPP_LINKAGE_NAME; case FORTRAN_LINKAGE_ID: return FORTRAN_LINKAGE_NAME; case OBJC_LINKAGE_ID: return OBJC_LINKAGE_NAME; + case QT_LINKAGE_ID: return QT_LINKAGE_NAME; } throw new CoreException(CCorePlugin.createStatus("Unsupported linkage id: " + linkageID)); //$NON-NLS-1$ } diff --git a/qt/org.eclipse.cdt.qt.core/plugin.xml b/qt/org.eclipse.cdt.qt.core/plugin.xml index eb99e065f65..7b3ebd7ffcb 100644 --- a/qt/org.eclipse.cdt.qt.core/plugin.xml +++ b/qt/org.eclipse.cdt.qt.core/plugin.xml @@ -21,8 +21,7 @@ - + @@ -53,12 +52,11 @@ - + point="org.eclipse.cdt.core.PDOMASTProcessor" + id="qt.PDOMASTProcessor" + name="Qt PDOM AST Processor"> + @@ -66,6 +64,11 @@ - + + + + diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java index 2e514ebfbeb..cefcfc148b1 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java @@ -13,11 +13,27 @@ package org.eclipse.cdt.qt.core; */ public class QtKeywords { public static final String CONNECT = "connect"; + public static final String DISCONNECT = "disconnect"; + public static final String Q_CLASSINFO = "Q_CLASSINFO"; + public static final String Q_DECLARE_FLAGS = "Q_DECLARE_FLAGS"; + public static final String Q_ENUMS = "Q_ENUMS"; + public static final String Q_FLAGS = "Q_FLAGS"; + public static final String Q_GADGET = "Q_GADGET"; + public static final String Q_INVOKABLE = "Q_INVOKABLE"; + public static final String Q_OBJECT = "Q_OBJECT"; + public static final String Q_PROPERTY = "Q_PROPERTY"; + public static final String Q_REVISION = "Q_REVISION"; public static final String Q_SIGNAL = "Q_SIGNAL"; public static final String Q_SIGNALS = "Q_SIGNALS"; public static final String Q_SLOT = "Q_SLOT"; public static final String Q_SLOTS = "Q_SLOTS"; + public static final String QMETAMETHOD = "QMetaMethod"; + public static final String QML_ATTACHED_PROPERTIES = "qmlAttachedProperties"; + public static final String QML_REGISTER_TYPE = "qmlRegisterType"; + public static final String QML_REGISTER_UNCREATABLE_TYPE = "qmlRegisterUncreatableType"; public static final String QOBJECT = "QObject"; + public static final String SIGNAL = "SIGNAL"; public static final String SIGNALS = "signals"; + public static final String SLOT = "SLOT"; public static final String SLOTS = "slots"; } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java index f11e1b22b9e..cfe7a36d479 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java @@ -1,11 +1,16 @@ +/* + * 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; -import org.eclipse.cdt.core.model.CModelException; -import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; -import org.osgi.framework.BundleContext; public class QtPlugin extends Plugin { @@ -18,62 +23,50 @@ public class QtPlugin extends Plugin { public static final String QMAKE_ENV_PROVIDER_EXT_POINT_NAME = "qmakeEnvProvider"; //$NON-NLS-1$ public static final String QMAKE_ENV_PROVIDER_ID = ID + "." + QMAKE_ENV_PROVIDER_EXT_POINT_NAME; //$NON-NLS-1$ - private static QtPlugin INSTANCE; - private static BundleContext context; + /** + * Instances of QtIndex are cached within the session properties of the project from + * which they are created. This name is used to store the property. + */ + public static final QualifiedName QTINDEX_PROP_NAME = new QualifiedName(ID, "qtindex"); - static BundleContext getContext() { - return context; + private static QtPlugin instance; + + public static QtPlugin getDefault() { + return instance; + } + + public QtPlugin() { + instance = this; + } + + public static IStatus info(String msg) { + return new Status(IStatus.INFO, ID, msg); } - static QtPlugin getDefault() { - return INSTANCE; + public static IStatus error(String msg) { + return error(msg, null); } - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) - */ - @Override - public void start(BundleContext bundleContext) throws Exception { - INSTANCE = this; - QtPlugin.context = bundleContext; - QMakeProjectInfo.start(); + public static IStatus error(String msg, Throwable e) { + return new Status(IStatus.ERROR, ID, msg, e); } - /* - * (non-Javadoc) - * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) - */ - @Override - public void stop(BundleContext bundleContext) throws Exception { - QMakeProjectInfo.stop(); - QtPlugin.context = null; - INSTANCE = null; + public static IStatus log(String e) { + return log(IStatus.INFO, e, null); } - public static void log(String e) { - log(IStatus.INFO, e, null); + public static IStatus log(Throwable e) { + String msg = e.getMessage(); + return msg == null ? log("Error", e) : log("Error: " + msg, e); } - public static void log(Throwable e) { - String msg= e.getMessage(); - if (msg == null) { - log("Error", e); //$NON-NLS-1$ - } else { - log("Error: " + msg, e); //$NON-NLS-1$ - } + public static IStatus log(String message, Throwable e) { + return log(IStatus.ERROR, message, e); } - public static void log(String message, Throwable e) { - Throwable nestedException; - if (e instanceof CModelException && (nestedException = ((CModelException)e).getException()) != null) { - e = nestedException; - } - log(IStatus.ERROR, message, e); + public static IStatus log(int code, String msg, Throwable e) { + IStatus status = new Status(code, ID, msg, e); + instance.getLog().log(status); + return status; } - - public static void log(int code, String msg, Throwable e) { - getDefault().getLog().log(new Status(code, ID, msg, e)); - } - } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java new file mode 100644 index 00000000000..cdabffdddb9 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java @@ -0,0 +1,20 @@ +/* + * 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 org.eclipse.cdt.core.dom.ast.IBinding; + +/** + * Base interface for things that are accessed from the {@link QtIndex}. + */ +public interface IQElement { + /** + * Returns the IBinding from the CDT index for the receiver element. + */ + public IBinding getBinding(); +} 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 new file mode 100644 index 00000000000..3cbe4a0f4cc --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java @@ -0,0 +1,39 @@ +/* + * 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.List; + +/** + * A class that inherits the Qt QObject and contains an expansion of Q_OBJECT. This + * provides a handle for retrieving signals, slots, and other Qt-related elements. + * @see IQMethod + */ +public interface IQObject extends IQElement { + /** + * Returns the name of the class. + */ + public String getName(); + + /** + * Returns a list of the QObject's that are bases of this class. + * E.g. in: + *
+	 * class T {};
+	 * class B1 : public QObject { Q_OBJECT };
+	 * class B2 : public QObject { Q_OBJECT };
+	 * class B3 : public T, public QObject { };
+	 * class D : public B1, public B2, public B3, public T { Q_OBJECT };
+	 * 
+ * The list of bases for D will contain B1 and B2, but not B3 or T. + *

+ * The list will be ordered as in the C++ code and will include only the directly declared + * base classes. + */ + public List getBases(); +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java new file mode 100644 index 00000000000..90bdde99d2c --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java @@ -0,0 +1,65 @@ +/* + * 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 org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.cdt.qt.internal.core.index.QtFactory; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; + +/** + * The public interface to the Qt index. The Qt index is a small wrapper around the + * core CDT's CIndex that adds Qt specific information. The Qt index is designed to + * interpret multiple versions of Qt, however only 4.8 has been implemented for now. + * + * @see #getIndex(IProject) + */ +public abstract class QtIndex { + /** + * Return an instance of the Qt index for the argument project. The CDT index is + * examined to discover appropriate version of Qt (using the value of QT_VERSION). + * Returns null if the Qt index cannot be created. This could happen if the argument + * project does not have the qtnature, or if the value of QT_VERSION is not supported + * by this implementation. + * + * @param project A Qt enabled project that should be indexed with Qt-specific information. + * @return The Qt index or null if the index cannot be created. + */ + public static QtIndex getIndex(IProject project) { + + if (project == null) + return null; + + try { + Object index = project.getSessionProperty(QtPlugin.QTINDEX_PROP_NAME); + if (index instanceof QtIndex) + return (QtIndex)index; + } catch(CoreException e) { + QtPlugin.log(e); + } + + // create and store a new instance when needed + QtIndex index = QtFactory.create(project); + if (index == null) + return null; + + try { + project.setSessionProperty(QtPlugin.QTINDEX_PROP_NAME, index); + } catch( CoreException e ) { + QtPlugin.log(e); + } + + return index; + } + + /** + * Find and return a subclass of QObject with the given qualified name. Returns null if + * the index does not have a subclass of QObject with the given name. + */ + public abstract IQObject findQObject(String[] qualifiedName); +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java deleted file mode 100644 index 06c2f6f08d7..00000000000 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2013 QNX Software Systems and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ - -package org.eclipse.cdt.qt.internal.core; - -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.IASTNode; -import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion; -import org.eclipse.cdt.core.dom.ast.IASTProblemHolder; -import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; -import org.eclipse.cdt.core.dom.ast.IBinding; -import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; -import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; -import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; -import org.eclipse.cdt.core.dom.ast.tag.IBindingTagger; -import org.eclipse.cdt.core.dom.ast.tag.ITag; -import org.eclipse.cdt.core.dom.ast.tag.ITagWriter; -import org.eclipse.cdt.core.dom.ast.tag.IWritableTag; -import org.eclipse.cdt.qt.core.QtKeywords; -import org.eclipse.cdt.qt.core.QtPlugin; - -/** - * Finds all functions that are marked as Qt signals or slots and tags them in - * the index. There are two ways that Qt understands for marking a function as a - * signal or slot: 1) With a macro in the function's visibility label 2) With a - * macro before the function itself E.g., both of these cases are valid: - * - *

- * class T
- * {
- * private:
- *     Q_SLOT void some_slot();
- * 
- * signals:
- *     void some_signal();
- * };
- * 
- * - * The 6 applicable macros are signals, Q_SIGNALS, Q_SIGNAL, slots, Q_SLOTS, and - * Q_SLOT. - */ -public class QtSignalSlotTagger implements IBindingTagger { - private static ICPPASTVisibilityLabel findVisibilityLabel( - ICPPMethod method, IASTNode ast) { - // the visibility cannot be found without an ast - if (ast == null) - return null; - - IASTNode methodDecl = ast; - ICPPASTCompositeTypeSpecifier classType = null; - while (methodDecl != null && classType == null) { - IASTNode parent = methodDecl.getParent(); - if (parent instanceof ICPPASTCompositeTypeSpecifier) - classType = (ICPPASTCompositeTypeSpecifier) parent; - else - methodDecl = parent; - } - - if (methodDecl == null || classType == null) - return null; - - ICPPASTVisibilityLabel lastLabel = null; - for (IASTDeclaration decl : classType.getMembers()) { - if (decl instanceof ICPPASTVisibilityLabel) - lastLabel = (ICPPASTVisibilityLabel) decl; - else if (decl == methodDecl) - return lastLabel; - } - - return null; - } - - private static byte getBitset(IASTNodeLocation... locations) { - for (IASTNodeLocation location : locations) - if (location instanceof IASTMacroExpansionLocation) { - IASTMacroExpansionLocation macroExpansion = (IASTMacroExpansionLocation) location; - IASTPreprocessorMacroExpansion exp = macroExpansion - .getExpansion(); - String macro = exp.getMacroReference().toString(); - - if (QtKeywords.Q_SIGNAL.equals(macro) - || QtKeywords.Q_SIGNALS.equals(macro) - || QtKeywords.SIGNALS.equals(macro)) - return QtPlugin.SignalSlot_Mask_signal; - if (QtKeywords.Q_SLOT.equals(macro) - || QtKeywords.Q_SLOTS.equals(macro) - || QtKeywords.SLOTS.equals(macro)) - return QtPlugin.SignalSlot_Mask_slot; - } - - return 0; - } - - private static byte getBitset(IASTNode... nodes) { - byte bitset = 0; - for (IASTNode node : nodes) - if (node != null) - for (IASTNodeLocation loc : node.getNodeLocations()) - bitset |= getBitset(loc); - - return bitset; - } - - private static IASTNode getSimpleDecl(IASTNode node) { - while (node != null && !(node instanceof IASTSimpleDeclaration)) - node = node.getParent(); - return node; - } - - private byte getQtMarkers(ICPPMethod method, IASTName ast) { - byte bitset = 0; - if (ast == null) - return bitset; - - // Look for macros on the previous visibility label. - bitset |= getBitset(findVisibilityLabel(method, ast)); - - // Look for macros on this function. See Bug 401696 for a better - // description of why it needs - // to work this why. Briefly, the 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 following code instead looks at the parents and children to find - // all node locations between - // the declarators. - // - // We first look at parents to find the closest SimpleDeclaration. We - // then look at that node's parent - // to find the node that is right before the target. Then we look at all - // node locations between the - // end of that previous node and the end of the target node. - - // find the closest containing SimpleDecl - IASTNode simpleDecl = getSimpleDecl(ast); - IASTNode parent = simpleDecl == null ? null : simpleDecl.getParent(); - if (parent == null) - return bitset; - - // find the declaration before the target - IASTNode previous = null; - IASTNode[] children = parent.getChildren(); - if (children.length > 1) - for (int i = 1; i < children.length; ++i) { - if (children[i] == simpleDecl) { - // if we haven't found a SimpleDecl, then find the nearest - // previous non-problem node - for (int j = i - 1; previous == null && j >= 0; --j) - if (!(children[j] instanceof IASTProblemHolder)) - previous = children[j]; - break; - } - if (children[i] instanceof IASTSimpleDeclaration) - previous = children[i]; - } - - // Signals/slots can only be declared inside of classes, so all cases we - // care about have a - // previous child, even if it is only the Base-class specifier. - if (previous == null) - return bitset; - - IASTFileLocation prevLocation = previous.getFileLocation(); - int prev_off = prevLocation.getNodeOffset(); - int prev_end = prevLocation.getNodeOffset() - + prevLocation.getNodeLength(); - - // Figure out where the target node ends. - int end = ast.getFileLocation().getNodeOffset() - + ast.getFileLocation().getNodeLength(); - - // Examine all locations that appear after the previous node and before - // the target node. - boolean found_previous = false; - for (IASTNodeLocation loc : parent.getNodeLocations()) { - int o = loc.getNodeOffset(); - int e = loc.getNodeOffset() + loc.getNodeLength(); - - // if the previous node has already been found, process this one - if (found_previous) - bitset |= getBitset(loc); - - // otherwise see if this is the previous node - else if (o <= prev_off && e >= prev_end) - found_previous = true; - - // stop processing when we're processed to the end of the target - if (e >= end) - break; - } - - return bitset; - } - - @Override - public ITag process(ITagWriter tagWriter, IBinding binding, IASTName ast) { - // only methods a be signals or slots - if (!(binding instanceof ICPPMethod)) - return null; - - // Find all qt marker macros associated with this node. - ICPPMethod method = (ICPPMethod) binding; - byte bitset = getQtMarkers(method, ast); - - // create and store the bitset if needed - if (bitset != 0) { - IWritableTag tag = tagWriter.createTag( - QtPlugin.SIGNAL_SLOT_TAGGER_ID, 1); - if (tag != null && tag.putByte(0, bitset)) - return tag; - } - - return null; - } -} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java new file mode 100644 index 00000000000..c187dea5881 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java @@ -0,0 +1,62 @@ +/* + * 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 org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.core.runtime.CoreException; + +/** + * A wrapper around the CDT index that manages the read lock. + */ +public class CDTIndex { + + private final IIndex index; + + public CDTIndex(IIndex index) { + this.index = index; + } + + /** + * An object used for reading from the CDT index. The {@link #access(IIndex)} method + * will only be invoked when the index's read lock has been properly acquired. + */ + public static interface Accessor { + /** + * A method that performs the lookup within the CDT index. The read-lock will + * be acquired before invoking this method. + *

+ * The implementation of access must not make calls to {@link CDTIndex#get(Accessor)}. + * If other objects are needed, then have the accessor return a qualified name and + * lookup the object after the implementation of #access completes. + */ + public T access(IIndex index) throws CoreException; + } + + /** + * Use the given accessor to find and return a value from the index. This method ensures + * that the read-lock has been acquired. + */ + public T get(Accessor accessor) { + try { + index.acquireReadLock(); + } catch(InterruptedException e) { + return null; + } + + try { + return accessor.access(index); + } catch(CoreException e) { + QtPlugin.log( e ); + } finally { + index.releaseReadLock(); + } + + return null; + } +} 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 new file mode 100644 index 00000000000..1ba7951e818 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java @@ -0,0 +1,47 @@ +/* + * 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.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.qt.core.index.IQObject; +import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject; +import org.eclipse.core.runtime.CoreException; + +public class QObject implements IQObject { + + private final String name; + private final QtPDOMQObject pdomQObject; + private final List bases; + + public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException { + this.name = pdomQObject.getName(); + this.pdomQObject = pdomQObject; + + this.bases = new ArrayList(); + for(QtPDOMQObject base : pdomQObject.findBases()) + this.bases.add(new QObject(qtIndex, cdtIndex, base)); + } + + @Override + public IBinding getBinding() { + return pdomQObject; + } + + @Override + public String getName() { + return name; + } + + @Override + public List getBases() { + return bases; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java new file mode 100644 index 00000000000..7d95d6e1515 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java @@ -0,0 +1,122 @@ +/* + * 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.SortedSet; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexMacro; +import org.eclipse.cdt.core.index.IndexFilter; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.cdt.qt.core.index.QtIndex; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; + +public class QtFactory { + + private static final char[] QT_VERSION = "QT_VERSION".toCharArray(); + + public static QtIndex create(IProject project) { + CDTIndex cdtIndex = getCDTIndex(project); + if (cdtIndex == null) { + QtPlugin.log("could not get CDT index from project " + project.getName()); + return null; + } + + QtVersion qtVersion = cdtIndex.get(QtVersionAccessor); + if (qtVersion == null) { + QtPlugin.log("could not find Qt version in CDT index from project " + project.getName()); + return null; + } + + if (qtVersion.major == 4 && qtVersion.minor == 8) + return new QtIndexImpl(cdtIndex); + + // Qt 4.8 is the default implementation, 5.0 support will need to come soon + return new QtIndexImpl(cdtIndex); + } + + private static CDTIndex getCDTIndex(IProject project) { + if (project == null) + return null; + + ICProject cProject = CoreModel.getDefault().create(project); + if (cProject == null) + return null; + + IIndex index = null; + try { + index = CCorePlugin.getIndexManager().getIndex(cProject); + } catch( CoreException e ) { + QtPlugin.log(e); + return null; + } + + return index == null ? null : new CDTIndex(index); + } + + /** + * A small wrapper to hold the result of index lookups for the Qt version. + */ + private static class QtVersion { + public final int major; + public final int minor; + @SuppressWarnings("unused") + public final int patch; + + // QT_VERSION looks like 0x040805 + private static final Pattern Version_regex = Pattern.compile( "0x([a-fA-F\\d]{1,2})([a-fA-F\\d]{2})([a-fA-F\\d]{2})" ); + + public static QtVersion create(String version) { + Matcher m = Version_regex.matcher(version); + if (!m.matches()) + return null; + + try { + int major = Integer.parseInt(m.group(1), 16); + int minor = Integer.parseInt(m.group(2), 16); + int patch = Integer.parseInt(m.group(3), 16); + return new QtVersion(major, minor, patch); + } catch(NumberFormatException e) { + QtPlugin.log(e); + } + return null; + } + + private QtVersion(int major, int minor, int patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + } + + private static final CDTIndex.Accessor QtVersionAccessor = new CDTIndex.Accessor() { + @Override + public QtVersion access(IIndex index) throws CoreException { + // Multiple macros might be found, sort the values and choose the highest version. + SortedSet versions = new TreeSet(); + try { + for(IIndexMacro macro : index.findMacros(QT_VERSION, IndexFilter.ALL, null)) + versions.add(new String(macro.getExpansion()).toLowerCase()); + } catch( CoreException e ) { } + + // don't create the Qt index if there is no Qt information in the CDT index + if (versions.size() <= 0) + return null; + + // the highest version has been sorted to the last position + return QtVersion.create(versions.last()); + } + }; +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java new file mode 100644 index 00000000000..d127c133a8e --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java @@ -0,0 +1,66 @@ +/* + * 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 org.eclipse.cdt.core.dom.ILinkage; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexBinding; +import org.eclipse.cdt.core.index.IndexFilter; +import org.eclipse.cdt.qt.core.index.IQObject; +import org.eclipse.cdt.qt.core.index.QtIndex; +import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject; +import org.eclipse.core.runtime.CoreException; + +public class QtIndexImpl extends QtIndex { + + private final CDTIndex cdtIndex; + + private static final IndexFilter QtLinkageFilter = new IndexFilter() { + @Override + public boolean acceptLinkage(ILinkage linkage) { + return linkage.getLinkageID() == ILinkage.QT_LINKAGE_ID; + } + + @Override + public boolean acceptBinding(IBinding binding) throws CoreException { + return true; + } + }; + + public QtIndexImpl(CDTIndex cdtIndex) { + this.cdtIndex = cdtIndex; + } + + @Override + public IQObject findQObject(String[] name) { + return name == null ? null : cdtIndex.get(new QObjectImplAccessor(name)); + } + + private class QObjectImplAccessor implements CDTIndex.Accessor { + + private final char[][] name; + + public QObjectImplAccessor(String[] qualName) { + name = new char[qualName.length][]; + for(int i = 0; i < name.length; ++i) + name[i] = qualName[i].toCharArray(); + } + + @Override + public IQObject access(IIndex index) throws CoreException { + + // TODO can there be more than one result? + for(IIndexBinding binding : index.findBindings(name, QtLinkageFilter, null)) + if (binding instanceof QtPDOMQObject) + return new QObject(QtIndexImpl.this, cdtIndex, (QtPDOMQObject) binding); + + return null; + } + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java new file mode 100644 index 00000000000..d102a466474 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java @@ -0,0 +1,280 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException; +import org.eclipse.cdt.core.dom.ast.IASTCompletionContext; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTImageLocation; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNameOwner; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.parser.IToken; + +/** + * The Qt linkage introduces several names that are based on names from the C++ linkage. This + * utility class is used to delegate operations to that base C++ name. Methods can be overridden + * by implementing in a subclass. + * + * @see QObjectName + */ +public abstract class ASTDelegatedName implements IASTName { + + protected final IASTName delegate; + + protected IBinding binding; + + /** + * Some Qt elements are introduced with empty macro expansions. The Qt linkage handles this + * by creating a new name and then adding it as a reference to the C++ language element. + * This utility helps by containing that C++ name and the location of the Qt name. + */ + public static class Reference extends ASTDelegatedName { + + private final IASTFileLocation location; + + public Reference(IASTName name, IASTFileLocation location) { + super(name); + this.location = location; + } + + @Override + protected IBinding createBinding() { + return delegate.resolveBinding(); + } + + @Override + public IASTFileLocation getFileLocation() { + return location; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public boolean isDefinition() { + return false; + } + + @Override + public boolean isDeclaration() { + return false; + } + + @Override + public int getRoleOfName(boolean allowResolution) { + return IASTNameOwner.r_reference; + } + } + + protected abstract IBinding createBinding(); + + protected ASTDelegatedName(IASTName delegate) { + this.delegate = delegate; + } + + @Override + public IASTTranslationUnit getTranslationUnit() { + return delegate.getTranslationUnit(); + } + + @Override + public IASTNodeLocation[] getNodeLocations() { + return delegate.getNodeLocations(); + } + + @Override + public IASTFileLocation getFileLocation() { + return delegate.getFileLocation(); + } + + @Override + public String getContainingFilename() { + return delegate.getContainingFilename(); + } + + @Override + public boolean isPartOfTranslationUnitFile() { + return delegate.isPartOfTranslationUnitFile(); + } + + @Override + public IASTNode getParent() { + return delegate.getParent(); + } + + @Override + public IASTNode[] getChildren() { + return delegate.getChildren(); + } + + @Override + public void setParent(IASTNode node) { + delegate.setParent(node); + } + + @Override + public ASTNodeProperty getPropertyInParent() { + return delegate.getPropertyInParent(); + } + + @Override + public void setPropertyInParent(ASTNodeProperty property) { + delegate.setPropertyInParent(property); + } + + @Override + public boolean accept(ASTVisitor visitor) { + return delegate.accept(visitor); + } + + @Override + public String getRawSignature() { + return delegate.getRawSignature(); + } + + @Override + public boolean contains(IASTNode node) { + return delegate.contains(node); + } + + @Override + public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException { + return delegate.getLeadingSyntax(); + } + + @Override + public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException { + return delegate.getTrailingSyntax(); + } + + @Override + public IToken getSyntax() throws ExpansionOverlapsBoundaryException { + return delegate.getSyntax(); + } + + @Override + public boolean isFrozen() { + return delegate.isFrozen(); + } + + @Override + public boolean isActive() { + return delegate.isActive(); + } + + @Override + public IASTNode getOriginalNode() { + return delegate.getOriginalNode(); + } + + @Override + public char[] getSimpleID() { + return delegate.getSimpleID(); + } + + @Override + public boolean isDeclaration() { + return delegate.isDeclaration(); + } + + @Override + public boolean isReference() { + return delegate.isReference(); + } + + @Override + public boolean isDefinition() { + return delegate.isDefinition(); + } + + @Override + public char[] toCharArray() { + return delegate.toCharArray(); + } + + @Override + public IBinding getBinding() { + return binding; + } + + @Override + public IBinding resolveBinding() { + if (binding == null) + binding = createBinding(); + return binding; + } + + @Override + public int getRoleOfName(boolean allowResolution) { + return delegate.getRoleOfName(allowResolution); + } + + @Override + public IASTCompletionContext getCompletionContext() { + return delegate.getCompletionContext(); + } + + @Override + public ILinkage getLinkage() { + return delegate.getLinkage(); + } + + @Override + public IASTImageLocation getImageLocation() { + return delegate.getImageLocation(); + } + + @Override + public IASTName getLastName() { + return delegate.getLastName(); + } + + @Override + public IASTName copy() { + return delegate.copy(); + } + + @Override + public IASTName copy(CopyStyle style) { + return delegate.copy(style); + } + + @Override + public void setBinding(IBinding binding) { + this.binding = binding; + } + + @Override + public char[] getLookupKey() { + return delegate.getLookupKey(); + } + + @Override + public IBinding getPreBinding() { + return binding; + } + + @Override + public IBinding resolvePreBinding() { + return resolveBinding(); + } + + @Override + public boolean isQualified() { + return delegate.isQualified(); + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java new file mode 100644 index 00000000000..e2b2c190ae9 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java @@ -0,0 +1,33 @@ +/* + * 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.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMLinkageFactory; +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 PDOMQtLinkageFactory implements IPDOMLinkageFactory { + + @Override + public PDOMLinkage getLinkage(PDOM pdom, long record) { + try { + return new QtPDOMLinkage(pdom, record); + } catch(CoreException e) { + QtPlugin.log(e); + } + return null; + } + + @Override + public PDOMLinkage createLinkage(PDOM pdom) throws CoreException { + return new QtPDOMLinkage(pdom); + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java new file mode 100644 index 00000000000..32048dd88c4 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java @@ -0,0 +1,98 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.internal.core.dom.Linkage; + +/** + * QObjects are C++ classes that have been annotated with Qt marker macros. This class is + * used to introduce the QObject to the Qt linkage. + */ +@SuppressWarnings("restriction") +public class QObjectName extends ASTDelegatedName { + + private final ICPPASTCompositeTypeSpecifier spec; + + private IASTNode parent; + private ASTNodeProperty propertyInParent; + + public QObjectName(ICPPASTCompositeTypeSpecifier spec) { + super(spec.getName()); + this.spec = spec; + this.parent = delegate.getParent(); + this.propertyInParent = delegate.getPropertyInParent(); + } + + @Override + protected IBinding createBinding() { + return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName()); + } + + @Override + public IASTTranslationUnit getTranslationUnit() { + return spec.getTranslationUnit(); + } + + @Override + public IASTNode[] getChildren() { + return IASTNode.EMPTY_NODE_ARRAY; + } + + @Override + public IASTNode getParent() { + return parent; + } + + @Override + public void setParent(IASTNode node) { + parent = node; + } + + @Override + public ASTNodeProperty getPropertyInParent() { + return propertyInParent; + } + + @Override + public void setPropertyInParent(ASTNodeProperty property) { + propertyInParent = property; + } + + @Override + public boolean accept(ASTVisitor visitor) { + return false; + } + + @Override + public boolean contains(IASTNode node) { + return false; + } + + @Override + public ILinkage getLinkage() { + return Linkage.QT_LINKAGE; + } + + @Override + public IASTName copy() { + return copy(CopyStyle.withoutLocations); + } + + @Override + public IASTName copy(CopyStyle style) { + return new QObjectName(spec); + } +} 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 new file mode 100644 index 00000000000..c1cda5330b0 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java @@ -0,0 +1,68 @@ +/* + * 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.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +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.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.index.IIndexSymbols; +import org.eclipse.cdt.internal.core.parser.scanner.LocationMap; +import org.eclipse.cdt.qt.core.QtKeywords; + +@SuppressWarnings("restriction") +public class QtASTVisitor extends ASTVisitor { + + private final IIndexSymbols symbols; + private final LocationMap locationMap; + + public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) { + shouldVisitDeclSpecifiers = true; + + this.symbols = symbols; + this.locationMap = locationMap; + } + + private boolean isQObject(ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) { + + // The class definition must contain a Q_OBJECT expansion. + for (IASTPreprocessorMacroExpansion expansion : expansions) { + IASTPreprocessorMacroDefinition macro = expansion.getMacroDefinition(); + if (QtKeywords.Q_OBJECT.equals(String.valueOf(macro.getName()))) + return true; + } + + return false; + } + + private void handleQObject(IASTPreprocessorIncludeStatement owner, ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) { + // Put the QObject into the symbol map. + QObjectName qobjName = new QObjectName(spec); + symbols.add(owner, qobjName, null); + } + + @Override + public int visit(IASTDeclSpecifier declSpec) { + if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { + ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) declSpec; + + IASTFileLocation loc = spec.getFileLocation(); + IASTPreprocessorIncludeStatement owner = loc == null ? null : loc.getContextInclusionStatement(); + + IASTPreprocessorMacroExpansion[] expansions = locationMap.getMacroExpansions(loc); + + if (isQObject(spec, expansions)) + handleQObject(owner, spec, expansions); + } + + return super.visit(declSpec); + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java new file mode 100644 index 00000000000..1537f83dbde --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java @@ -0,0 +1,89 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IScope; +import org.eclipse.cdt.internal.core.dom.Linkage; + +@SuppressWarnings("restriction") +public class QtBinding implements IBinding { + + private final QtPDOMNodeType type; + private final QtBinding owner; + private final IASTName qtName; + private final IASTName cppName; + + private QtPDOMBinding pdomBinding; + + public QtBinding(QtPDOMNodeType type, IASTName qtName, IASTName cppName) { + this(type, null, qtName, cppName); + } + + public QtBinding(QtPDOMNodeType type, QtBinding owner, IASTName qtName, IASTName cppName) { + this.type = type; + this.owner = owner; + this.qtName = qtName; + this.cppName = cppName; + } + + public QtPDOMNodeType getType() { + return type; + } + + public IASTName getQtName() { + return qtName; + } + + public IASTName getCppName() { + return cppName; + } + + public void setPDOMBinding(QtPDOMBinding pdomBinding) { + this.pdomBinding = pdomBinding; + } + + @Override + @SuppressWarnings({ "rawtypes" }) + public Object getAdapter(Class adapter) { + if (getClass().isAssignableFrom(adapter)) + return this; + if (QtPDOMBinding.class.isAssignableFrom(adapter)) + return pdomBinding; + + return null; + } + + @Override + public String getName() { + return String.valueOf(getNameCharArray()); + } + + @Override + public char[] getNameCharArray() { + return qtName.getSimpleID(); + } + + @Override + public ILinkage getLinkage() { + return Linkage.QT_LINKAGE; + } + + @Override + public IBinding getOwner() { + return owner; + } + + @Override + public IScope getScope() throws DOMException { + return null; + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java new file mode 100644 index 00000000000..ba9b5689d1d --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java @@ -0,0 +1,24 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndexSymbols; +import org.eclipse.cdt.core.index.IPDOMASTProcessor; +import org.eclipse.cdt.internal.core.parser.scanner.LocationMap; +import org.eclipse.core.runtime.CoreException; + +@SuppressWarnings("restriction") +public class QtPDOMASTProcessor extends IPDOMASTProcessor.Abstract { + @Override + public int process(IASTTranslationUnit ast, IIndexSymbols symbols) throws CoreException { + ast.accept(new QtASTVisitor(symbols, (LocationMap) ast.getAdapter(LocationMap.class))); + return ILinkage.QT_LINKAGE_ID; + } +} 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 new file mode 100644 index 00000000000..e5548905467 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java @@ -0,0 +1,98 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.IBinding; +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; +import org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.core.runtime.CoreException; + +@SuppressWarnings("restriction") +public abstract class QtPDOMBinding extends PDOMBinding { + + private static int offsetInitializer = RECORD_SIZE; + protected static enum Field { + CppRecord(Database.PTR_SIZE), + Last(0); + + public final int offset; + + private Field(int sizeof) { + this.offset = offsetInitializer; + offsetInitializer += sizeof; + } + + public long getRecord(long baseRec) { + return baseRec + offset; + } + } + + protected QtPDOMBinding(QtPDOMLinkage linkage, long record) { + super(linkage, record); + } + + protected QtPDOMBinding(QtPDOMLinkage linkage, PDOMNode parent, QtBinding qtBinding) throws CoreException { + super(linkage, parent, qtBinding.getNameCharArray()); + qtBinding.setPDOMBinding(this); + + getDB().putRecPtr(Field.CppRecord.getRecord(record), linkage.getCPPRecord(qtBinding)); + } + + @Override + 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(); + return pdomLinkage instanceof QtPDOMLinkage ? (QtPDOMLinkage) pdomLinkage : null; + } + + // Access to the base class is restricted in the cdt.core plugin. Other classes in the qt.core + // plugin that need the qualified name get an access warning. This forwarding function moves + // those warnings to a single place (this method). + @Override + public String[] getQualifiedName() { + return super.getQualifiedName(); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(getClass())) + return this; + + return super.getAdapter(adapter); + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java new file mode 100644 index 00000000000..ebd0968ddf2 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java @@ -0,0 +1,245 @@ +/* + * 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.ILinkage; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.internal.core.dom.parser.ISerializableEvaluation; +import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer; +import org.eclipse.cdt.internal.core.pdom.PDOM; +import org.eclipse.cdt.internal.core.pdom.db.BTree; +import org.eclipse.cdt.internal.core.pdom.db.Database; +import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator; +import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor; +import org.eclipse.cdt.internal.core.pdom.dom.FindBinding; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding; +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; +import org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.core.runtime.CoreException; + +@SuppressWarnings("restriction") +public class QtPDOMLinkage extends PDOMLinkage { + + private static int offsetInitializer = PDOMLinkage.RECORD_SIZE; + private static enum Field { + Version(Database.INT_SIZE), + CppIndex(Database.PTR_SIZE), + Last(0); + + private final int offset; + + private Field(int sizeof) { + this.offset = offsetInitializer; + offsetInitializer += sizeof; + } + + public long getRecord(long baseRec) { + return baseRec + offset; + } + } + + // The version that has been read from/written to the persisted file. + private int version; + + // An index of C++ -> Qt Bindings. This is used for fast lookup of things like the QObject + // for a C++ class in the base specifier list. Each entry in the index is a pair like + // { CPP_Record, Qt_Record }, the CPP_Record is used for comparison when searching the index. + private final BTree cppIndex; + + public QtPDOMLinkage(PDOM pdom, long record) throws CoreException { + super(pdom, record); + + version = pdom.getDB().getInt(Field.Version.getRecord(record)); + cppIndex = new BTree(pdom.getDB(), Field.CppIndex.getRecord(record), new CppRecordIndexComparator()); + } + + protected QtPDOMLinkage(PDOM pdom) throws CoreException { + super(pdom, ILinkage.QT_LINKAGE_NAME, ILinkage.QT_LINKAGE_NAME.toCharArray()); + + // Initialize the version with whatever is current. + version = QtPDOMNodeType.VERSION; + pdom.getDB().putInt(Field.Version.getRecord(record), version); + + long cppIndexRec = Field.CppIndex.getRecord(record); + Database db = pdom.getDB(); + db.putRecPtr(cppIndexRec, 0); + cppIndex = new BTree(db, cppIndexRec, new CppRecordIndexComparator()); + } + + public int getVersion() { + return version; + } + + @Override + public String getLinkageName() { + return ILinkage.QT_LINKAGE_NAME; + } + + @Override + public int getLinkageID() { + return ILinkage.QT_LINKAGE_ID; + } + + public QtPDOMBinding findFromCppRecord(long cppRec) throws CoreException { + CppRecordIndexFinder finder = new CppRecordIndexFinder(cppRec); + cppIndex.accept(finder); + if (finder.foundQtRec == null) + return null; + + PDOMNode node = getNode(finder.foundQtRec.longValue()); + return node instanceof QtPDOMBinding ? (QtPDOMBinding) node : null; + } + + @Override + public PDOMNode getNode(long record, int nodeType) throws CoreException { + return QtPDOMNodeType.load(this, nodeType, record); + } + + @Override + public IBTreeComparator getIndexComparator() { + return new FindBinding.DefaultBindingBTreeComparator(this); + } + + // IBinding#getAdapter cannot create an instance of PDOMBinding because the Linkage is required. This + // utility method uses #getAdapter to see if an instance has already been create. If not then a new + // is created and stored in the AST binding. + @Override + public PDOMBinding adaptBinding(IBinding binding, boolean includeLocal) throws CoreException { + if (binding == null) + return null; + + // If a binding has already been persisted for this instance then return it now. + QtPDOMBinding pdomBinding = (QtPDOMBinding) binding.getAdapter(QtPDOMBinding.class); + if (pdomBinding != null + && pdomBinding.getLinkage() == this) + return pdomBinding; + + // Otherwise try to create a new PDOMBinding. + QtBinding qtBinding = (QtBinding) binding.getAdapter(QtBinding.class); + if (qtBinding != null) + switch(qtBinding.getType()) { + case QObject: + pdomBinding = new QtPDOMQObject(this, qtBinding); + break; + } + + // If a PDOMBinding was created, then add it to the linkage before returning it. + if (pdomBinding != null) { + addChild(pdomBinding); + return pdomBinding; + } + + // Otherwise fall back to looking in the C++ linkage. + return getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID).adaptBinding(binding); + } + + public void addChild(QtPDOMBinding child) throws CoreException { + super.addChild(child); + + Database db = getDB(); + long pair = db.malloc(Database.PTR_SIZE * 2); + db.putRecPtr(pair, child.getCppRecord()); + db.putRecPtr(pair + Database.PTR_SIZE, child.getRecord()); + cppIndex.insert(pair); + } + + public long getCPPRecord(QtBinding qtBinding) throws CoreException { + + IASTName cppName = qtBinding.getCppName(); + if (cppName == null) + return 0; + + IBinding binding = getPDOM().findBinding(cppName); + if (binding == null) + return 0; + + IPDOMBinding pdomBinding = (IPDOMBinding) binding.getAdapter(IPDOMBinding.class); + if (pdomBinding == null) + return 0; + + if (pdomBinding.getLinkage() == null + || pdomBinding.getLinkage().getLinkageID() != ILinkage.CPP_LINKAGE_ID) + return 0; + + return pdomBinding.getRecord(); + } + + @Override + public PDOMBinding addBinding(IASTName name) throws CoreException { + return name == null ? null : adaptBinding(name.getBinding()); + } + + @Override + public int getBindingType(IBinding binding) { + return binding instanceof QtBinding ? ((QtBinding) binding).getType().Type : 0; + } + + @Override + public PDOMBinding addTypeBinding(IBinding binding) throws CoreException { + throw new CoreException(QtPlugin.error("Qt Linkage does not manage types")); //$NON-NLS-1$ + } + + @Override + public IType unmarshalType(ITypeMarshalBuffer buffer) throws CoreException { + throw new CoreException(QtPlugin.error("Qt Linkage does not marshal types")); //$NON-NLS-1$ + } + + @Override + public IBinding unmarshalBinding(ITypeMarshalBuffer buffer) throws CoreException { + throw new CoreException(QtPlugin.error("Qt Linkage does not marshal bindings")); //$NON-NLS-1$ + } + + @Override + public ISerializableEvaluation unmarshalEvaluation(ITypeMarshalBuffer typeMarshalBuffer) throws CoreException { + throw new CoreException(QtPlugin.error("Qt Linkage does not marshal evaluations")); //$NON-NLS-1$ + } + + private class CppRecordIndexComparator implements IBTreeComparator { + + @Override + public int compare(long record1, long record2) throws CoreException { + Database db = getDB(); + + Long cppRec1 = Long.valueOf(db.getRecPtr(record1)); + long cppRec2 = Long.valueOf(db.getRecPtr(record2)); + return cppRec1.compareTo(cppRec2); + } + } + + private class CppRecordIndexFinder extends CppRecordIndexComparator implements IBTreeVisitor { + + private final Long targetCppRec; + public Long foundQtRec; + + public CppRecordIndexFinder(long targetCppRec) { + this.targetCppRec = Long.valueOf(targetCppRec); + } + + @Override + public int compare(long record) throws CoreException { + Long cppRec = Long.valueOf(getDB().getRecPtr(record)); + return cppRec.compareTo(targetCppRec); + } + + @Override + public boolean visit(long record) throws CoreException { + // Stop searching after the record is found. + if (foundQtRec != null) + return false; + + // The record is the pair, so the Qt rec is the second element. + long qtRec = getDB().getRecPtr(record + Database.PTR_SIZE); + foundQtRec = Long.valueOf(qtRec); + return false; + } + } +} 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 new file mode 100644 index 00000000000..2048afebb6e --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java @@ -0,0 +1,52 @@ +/* + * 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.internal.core.index.IIndexBindingConstants; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; +import org.eclipse.core.runtime.CoreException; + +@SuppressWarnings("restriction") +public enum QtPDOMNodeType { + + QObject; + + public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal(); + + /** + * The current version of the QtPDOMLinkage. This can be used to make sure the persisted + * data matches what is expected by the implementation. Care should be taken to make changes + * backward compatible when possible. + *

+ * The version is needed because ordinals for these enumerators are written to the file. + */ + public static final int VERSION = 1; + + public static QtPDOMNodeType forType(int version, int type) { + // Nothing has been deleted or replaced yet, so the version is ignored. + + for(QtPDOMNodeType node : values()) + if (node.Type == type) + return node; + return null; + } + + // This needs to return PDOMNode so that it can be either QtPDOMNode or QtPDOMBinding. + public static PDOMNode load(QtPDOMLinkage linkage, int nodeType, long record) throws CoreException { + QtPDOMNodeType node = QtPDOMNodeType.forType(linkage.getVersion(), nodeType); + if (node == null) + return null; + + switch(node) { + case QObject: + return new QtPDOMQObject(linkage, record); + } + + return null; + } +} 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 new file mode 100644 index 00000000000..8a6b2a9c176 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java @@ -0,0 +1,182 @@ +/* + * 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.Collections; +import java.util.List; + +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.PDOMNodeLinkedList; +import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; +import org.eclipse.cdt.qt.core.QtPlugin; +import org.eclipse.core.runtime.CoreException; + +/** + * The persisted form of QObjects. + */ +@SuppressWarnings("restriction") +public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType { + + // 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 { + Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */), + Last(0); + + private final int offset; + + private Field(int sizeof) { + this.offset = offsetInitializer; + offsetInitializer += sizeof; + } + + public long getRecord(long baseRec) { + return baseRec + offset; + } + } + + private final PDOMNodeLinkedList children; + + protected QtPDOMQObject(QtPDOMLinkage linkage, long record) throws CoreException { + super(linkage, record); + children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record)); + } + + public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException { + super(linkage, null, binding); + children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record)); + } + + @Override + protected int getRecordSize() { + return Field.Last.offset; + } + + @Override + public int getNodeType() { + return QtPDOMNodeType.QObject.Type; + } + + // This forwarding method is to get rid of compilation warnings when clients try to call + // #getName on the non-accessible parent. + @Override + public String getName() { + return super.getName(); + } + + public List findBases() throws CoreException { + IBinding cppBinding = getCppBinding(); + if (!(cppBinding instanceof ICPPClassType)) + return Collections.emptyList(); + + List bases = new ArrayList(); + for (ICPPBase base : ((ICPPClassType) cppBinding).getBases()) { + if (base.getVisibility() != ICPPBase.v_public) + continue; + + IBinding baseCls = base.getBaseClass(); + if (baseCls == null) + continue; + + IPDOMBinding pdomBase = (IPDOMBinding) baseCls.getAdapter(IPDOMBinding.class); + if (pdomBase == null) + continue; + + QtPDOMBinding qtPDOMBinding = getQtLinkage().findFromCppRecord(pdomBase.getRecord()); + if (qtPDOMBinding == null) + continue; + + QtPDOMQObject pdomQObj = (QtPDOMQObject) qtPDOMBinding.getAdapter(QtPDOMQObject.class); + if (pdomQObj != null) + bases.add(pdomQObj); + } + 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); + } + + @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 { + IBinding cppBinding = getCppBinding(); + if (cppBinding instanceof ICompositeType) + return ((ICompositeType) cppBinding).getCompositeScope(); + } catch(CoreException e) { + QtPlugin.log(e); + } + + return null; + } + + @Override + public Object clone() { + throw new UnsupportedOperationException(); + } +} diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java new file mode 100644 index 00000000000..3dec16f2114 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java @@ -0,0 +1,109 @@ +/* + * 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.Arrays; + +import org.eclipse.cdt.core.dom.IPDOMNode; +import org.eclipse.cdt.core.dom.IPDOMVisitor; +import org.eclipse.cdt.internal.core.pdom.dom.PDOMNamedNode; +import org.eclipse.core.runtime.CoreException; + +/** + * A Qt-specific specialization of the generic PDOMVisitor. This class provides + * an empty implementation of {@link #leave(IPDOMNode)}, but required implementations to + * provide {@link #visit(IPDOMNode)}. The class also provides a few commonly required + * implementations. + */ +@SuppressWarnings("restriction") +public abstract class QtPDOMVisitor implements IPDOMVisitor { + + /** + * Collects all nodes that match the given type. This could be used, for example, to get + * all QtPDOMQObject's from the index. + */ + public static class All extends QtPDOMVisitor { + + private final Class cls; + public final ArrayList list = new ArrayList(); + + public All(Class cls) { + this.cls = cls; + } + + @Override + @SuppressWarnings("unchecked") + public boolean visit(IPDOMNode node) throws CoreException { + if (node != null + && cls.isAssignableFrom(node.getClass())) + list.add((T) node); + return true; + } + } + + /** + * A simple interface that is used to select node's from the index based on specific + * criteria. + */ + public static interface IFilter { + public boolean matches(IPDOMNode node) throws CoreException; + } + + /** + * A filter that selects nodes based on their name. + */ + public static class PDOMNamedNodeFilter implements IFilter { + + private final char[] name; + + public PDOMNamedNodeFilter(String name) { + this.name = name.toCharArray(); + } + + @Override + public boolean matches(IPDOMNode node) throws CoreException { + if (node instanceof PDOMNamedNode) + return Arrays.equals(name, ((PDOMNamedNode) node).getNameCharArray()); + return false; + } + } + + /** + * A utility class that searches the index for all nodes that match the given filter. + */ + public static class Find extends QtPDOMVisitor { + + private final Class cls; + private final IFilter filter; + + public T element; + + public Find(Class cls, IFilter filter) { + this.cls = cls; + this.filter = filter; + } + + @Override + @SuppressWarnings("unchecked") + public boolean visit(IPDOMNode node) throws CoreException { + if (element != null) + return false; + + if (cls.isAssignableFrom(node.getClass()) + && filter.matches(node)) + element = (T) node; + + return element == null; + } + } + + @Override + public void leave(IPDOMNode node) throws CoreException { + } +} diff --git a/qt/org.eclipse.cdt.qt.tests/.classpath b/qt/org.eclipse.cdt.qt.tests/.classpath new file mode 100644 index 00000000000..ad32c83a788 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/qt/org.eclipse.cdt.qt.tests/.project b/qt/org.eclipse.cdt.qt.tests/.project new file mode 100644 index 00000000000..b40b063abdd --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/.project @@ -0,0 +1,28 @@ + + + org.eclipse.cdt.qt.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..2e2cf2a9d36 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Tests +Bundle-SymbolicName: org.eclipse.cdt.qt.tests +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.qt.tests.QtTestPlugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.junit, + org.eclipse.cdt.qt.core, + org.eclipse.cdt.core.tests, + org.eclipse.cdt.core, + org.eclipse.core.resources, + org.eclipse.cdt.codan.core, + org.eclipse.cdt.qt.ui, + org.eclipse.jface.text, + org.eclipse.cdt.ui +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 diff --git a/qt/org.eclipse.cdt.qt.tests/build.properties b/qt/org.eclipse.cdt.qt.tests/build.properties new file mode 100644 index 00000000000..34d2e4d2dad --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . 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 new file mode 100644 index 00000000000..a062abd1423 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java @@ -0,0 +1,21 @@ +/* + * 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 junit.framework.Test; +import junit.framework.TestSuite; + +public class AllQtTests extends TestSuite { + + public static Test suite() throws Exception { + return + new TestSuite( + QObjectTests.class, + QtIndexTests.class); + } +} diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java new file mode 100644 index 00000000000..b2915ff738b --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java @@ -0,0 +1,167 @@ +/* + * 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 org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.qt.core.QtNature; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; + +public class BaseQtTestCase extends BaseTestCase { + + // TODO There is a problem with the unit test framework where it sometimes will not wait + // long enough for the index to be updated. For now mask this problem by stopping + // that test and continuing with the rest. + @Deprecated + protected boolean isIndexOk(String indexName, Object obj) { + if (obj != null) + return true; + + System.err.println(getClass().getSimpleName() + '.' + getName() + ": could not find " + indexName + " in the index, continuing with other tests"); + return false; + } + + protected IProject fProject; + protected IFile fFile; + protected ICProject fCProject; + protected IIndex fIndex; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + String projectName = "__" + getClass().getSimpleName() + "__"; + + fCProject = CProjectHelper.createCCProject(projectName, "bin", IPDOMManager.ID_FAST_INDEXER); + fProject = fCProject.getProject(); + CProjectHelper.addNatureToProject(fProject, QtNature.ID, null); + fIndex = CCorePlugin.getIndexManager().getIndex(fCProject); + + indexQObject_h(); + } + + @Override + protected void tearDown() throws Exception { + if (fCProject != null) + CProjectHelper.delete(fCProject); + + fIndex = null; + fCProject = null; + fProject = null; + super.tearDown(); + } + + /** + * This creates a mock Qt header file, which avoids putting the real Qt headers into the + * include path of this unit test's fake project. + */ + // #define QT_VERSION 0x040805 + // #define Q_PROPERTY(defn) + // #define Q_OBJECT + // #define Q_GADGET + // #define Q_CLASSINFO(k,v) + // #define Q_SIGNAL + // #define Q_SLOT + // #define Q_INVOKABLE + // #define Q_DECLARE_FLAGS(t,e) + // #define Q_ENUMS(e) + // #define Q_FLAGS(e) + // #define slots + // #define signals protected + // #define Q_SLOTS + // #define Q_SIGNALS protected + // const char *qFlagLocation(const char *method); + // #define SLOT(a) qFlagLocation("1"#a) + // #define SIGNAL(a) qFlagLocation("2"#a) + // #define QML_DECLARE_TYPEINFO( T, F ) template <> struct QDeclarativeTypeInfo { enum { H = F }; }; + // enum { QML_HAS_ATTACHED_PROPERTIES = 0x01 }; + // class QObject + // { + // Q_OBJECT + // Q_SIGNAL void destroyed( QObject * ); + // public: + // static bool connect( QObject *, const char *, QObject *, const char * ); + // }; + // class QString { public: QString( const char * ch ); }; + // template int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName); + // template int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName); + // template int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason); + public void indexQObject_h() throws Exception { + loadComment("junit-QObject.hh"); + } + + private String[] getContentsForTest(int blocks) throws Exception { + String callingMethod = Thread.currentThread().getStackTrace()[3].getMethodName(); + CharSequence[] help= TestSourceReader.getContentsForTest( + QtTestPlugin.getDefault().getBundle(), "src", getClass(), callingMethod, blocks); + String[] result= new String[help.length]; + int i= 0; + for (CharSequence buf : help) { + result[i++]= buf.toString(); + } + return result; + } + + /** + * The implementation of TestSourceReader (called from BaseTestCase) imposes some restrictions + * on the caller of #loadComment. + *

    + *
  1. loadComment must be called from a public method
  2. + *
  3. loadComment must be called from a method that does not accept parameters
  4. + *
+ */ + protected void loadComment(String filename) throws Exception { + String[] contents= getContentsForTest(1); + + // get the timestamp of the last change to the index + IIndexManager indexManager = CCorePlugin.getIndexManager(); + long timestamp = indexManager.getIndex(fCProject).getLastWriteAccess(); + + // add the new content + fFile = TestSourceReader.createFile(fProject, filename, contents[0]); + + // wait for the index to change + Thread.yield(); + for(long stopAt = System.currentTimeMillis() + 3000; + System.currentTimeMillis() < stopAt && timestamp == indexManager.getIndex(fCProject).getLastWriteAccess(); + Thread.sleep(100)) { + /* intentionally empty*/ + } + + assertNotSame(timestamp, indexManager.getIndex(fCProject).getLastWriteAccess()); + } + + /** + * A utility method for pausing the JUNIT code. This is helpful when investigating the + * CDT indexer, which runs in a different job. The idea is to use it only while debugging, + * and to change the value of the pause variable in the loop in order to continue. + */ + protected static void pause() throws Exception { + String oldName = Thread.currentThread().getName(); + Thread.currentThread().setName("*** JUNIT PAUSED ***"); + try + { + // pause = false + boolean pause = true; + do + { + Thread.sleep(10000); + } while( pause ); + + } finally { + Thread.currentThread().setName(oldName); + } + } +} 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 new file mode 100644 index 00000000000..a3011bc03fd --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java @@ -0,0 +1,49 @@ +/* + * 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.Collection; +import java.util.Iterator; + +import org.eclipse.cdt.qt.core.index.IQObject; +import org.eclipse.cdt.qt.core.index.QtIndex; + +public class QObjectTests extends BaseQtTestCase { + + // #include "junit-QObject.hh" + // class T {}; + // class B1 : public QObject {Q_OBJECT}; + // class B2 : public QObject {Q_OBJECT}; + // class B3 : public QObject {Q_OBJECT}; + // class D1 : public B1, public B2, private B3, public T {Q_OBJECT}; + // class D2 : public T, public QObject {}; + public void testGetBases() throws Exception { + loadComment("bases.hh"); + + QtIndex qtIndex = QtIndex.getIndex(fProject); + assertNotNull(qtIndex); + + IQObject qobj_B1 = qtIndex.findQObject(new String[]{ "B1" }); + if (!isIndexOk("B1", qobj_B1)) + return; + IQObject qobj_D1 = qtIndex.findQObject(new String[]{ "D1" }); + assertNotNull(qobj_B1); + assertNotNull(qobj_D1); + + Collection d1_bases = qobj_D1.getBases(); + assertNotNull(d1_bases); + assertEquals(2, d1_bases.size()); + Iterator iterator = d1_bases.iterator(); + assertEquals(qobj_B1.getName(), iterator.next().getName()); + assertEquals("B2", iterator.next().getName()); + + // D2 is not a QObject because it doesn't expand the Q_OBJECT macro + IQObject qobj_D2 = qtIndex.findQObject(new String[]{ "D2" }); + assertNull(qobj_D2); + } +} diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java new file mode 100644 index 00000000000..e4fd1852588 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java @@ -0,0 +1,49 @@ +/* + * 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 org.eclipse.cdt.qt.core.index.IQObject; +import org.eclipse.cdt.qt.core.index.QtIndex; + +public class QtIndexTests extends BaseQtTestCase { + + private static final String Filename_testCache = "testCache.hh"; + + // #include "junit-QObject.hh" + // class B : public QObject + // { + // Q_OBJECT + // }; + public void testLookup() throws Exception { + loadComment(Filename_testCache); + + QtIndex qtIndex = QtIndex.getIndex(fProject); + assertNotNull(qtIndex); + + // make sure the instance can be found + IQObject qobj1 = qtIndex.findQObject(new String[]{ "B" }); + assertNotNull(qobj1); + assertEquals("B", qobj1.getName()); + + // make sure the instance is still found after the content changes + changeBDecl(); + IQObject qobj2 = qtIndex.findQObject(new String[]{ "B" }); + assertNotNull(qobj2); + assertEquals("B", qobj2.getName()); + } + + // #include "junit-QObject.hh" + // class B : public QObject + // { + // Q_OBJECT + // Q_PROPERTY(bool allowed READ isAllowed()) + // }; + public void changeBDecl() throws Exception { + loadComment(Filename_testCache); + } +} diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java new file mode 100644 index 00000000000..13222320380 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java @@ -0,0 +1,59 @@ +/* + * 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 org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class QtTestPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.qt.ui.tests"; //$NON-NLS-1$ + + // The shared instance + private static QtTestPlugin plugin; + + /** + * The constructor + */ + public QtTestPlugin() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static QtTestPlugin getDefault() { + return plugin; + } + +} diff --git a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java index d4b781e9e8b..a8fab56b811 100644 --- a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java +++ b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java @@ -1,3 +1,10 @@ +/* + * 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.ui; import org.eclipse.ui.plugin.AbstractUIPlugin; @@ -13,7 +20,7 @@ public class QtUIPlugin extends AbstractUIPlugin { // The shared instance private static QtUIPlugin plugin; - + /** * The constructor */ @@ -24,6 +31,7 @@ public class QtUIPlugin extends AbstractUIPlugin { * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ + @Override public void start(BundleContext context) throws Exception { super.start(context); plugin = this; @@ -33,6 +41,7 @@ public class QtUIPlugin extends AbstractUIPlugin { * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ + @Override public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context);