summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Eidsness2013-11-29 15:09:55 (EST)
committer Doug Schaefer2013-12-03 09:02:31 (EST)
commit0f482a80b2cfb9e1d8d44afef2a787ee04181dcd (patch)
tree45159e985baff66b4059f3c72daa15c8f1ef90d8
parentdc746352a32ab1b18732db3922d406bcb5170336 (diff)
downloadorg.eclipse.cdt-0f482a80b2cfb9e1d8d44afef2a787ee04181dcd.zip
org.eclipse.cdt-0f482a80b2cfb9e1d8d44afef2a787ee04181dcd.tar.gz
org.eclipse.cdt-0f482a80b2cfb9e1d8d44afef2a787ee04181dcd.tar.bz2
Bug 422841: Add QEnum to QtIndexrefs/changes/47/19147/2
The Qt meta-object system allows C++ enums to be added as simple enums and as flags. There is more detail at: http://qt-project.org/doc/qt-4.8/qobject.html#Q_ENUMS and http://qt-project.org/doc/qt-4.8/qflags.html This patch adds IQEnum to the QtIndex. IQEnums are contained in IQObjects and therefore are accessed with the IQObject.getEnums. An IQEnum holds its name, enumerators, and a boolean indicating whether the instance represents a Qt flag. A Qt flag is an enum where the enumerators are intended to be used with bitwise operations. The Q_DECLARE_FLAGS macro is used to introduce a type-safe container for the flags. This patch also adds unit tests for this new functionality. Change-Id: If51524e93533bae82a3263f3c7973a31793a8a83 Signed-off-by: Andrew Eidsness <eclipse@jfront.com> Reviewed-on: https://git.eclipse.org/r/19147 Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com> Tested-by: Doug Schaefer <dschaefer@qnx.com>
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQEnum.java79
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java8
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QEnum.java69
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java17
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTImageLocation.java88
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java125
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java135
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java3
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java5
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java159
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java129
11 files changed, 805 insertions, 12 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQEnum.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQEnum.java
new file mode 100644
index 0000000..062c47c
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQEnum.java
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+/**
+ * Represents expansions of the Q_ENUMS macro within a class declaration.
+ * <pre>
+ * class B : public QObject
+ * {
+ * Q_OBJECT
+ * enum E { enumerator };
+ * Q_ENUMS( E )
+ * };
+ *
+ * class Q : public QObject
+ * {
+ * Q_OBJECT
+ * Q_ENUMS( B::E E0 )
+ * Q_ENUMS( E1 )
+ * enum E0 { e0a, e0b = 2 };
+ * enum E1 { e1 };
+ * }
+ * </pre>
+ * NOTE: http://qt-project.org/doc/qt-4.8/qobject.html#Q_ENUMS
+ * <blockquote>
+ * If you want to register an enum that is declared in another class, the enum must be fully qualified
+ * with the name of the class defining it. In addition, the class defining the enum has to inherit
+ * QObject as well as declare the enum using Q_ENUMS().
+ * </blockquote>
+ * So, the lookup for the C++ enum only needs to look in the same class spec when the name is not
+ * qualified. When it is qualified, then it needs to find the QObject and then look at its Q_ENUMS.
+ */
+public interface IQEnum {
+ /**
+ * Returns the name of the enumerator as referenced in parameter in the Q_ENUMS
+ * macro expansion. In the sample code in the class declaration, this would return
+ * "B::E", "E0", or "E1".
+ */
+ public String getName();
+
+ /**
+ * Returns true if this enumeration was introduced to the Qt meta-object system with
+ * a Q_FLAGS expansion and false if it was introduced with Q_ENUMS.
+ */
+ public boolean isFlag();
+
+ /**
+ * Returns an unsorted collection of the enumerators contained in the enum references
+ * in the Q_ENUMS macro expansion.
+ * <p>
+ * NOTE: It would be nice if the textual order of the enumerators was preserved by the
+ * underlying CDT index, but it is not. The {@link Enumerator#getOrdinal()} method can
+ * be used to recover some ordering information.
+ */
+ public Collection<Enumerator> getEnumerators();
+
+ /**
+ * A small wrapper class for the enumerators that are declared within the enum that is
+ * referenced by the parameter of the Q_ENUMS macro expansion.
+ */
+ public interface Enumerator {
+ /**
+ * Returns the name of the enumerator.
+ */
+ public String getName();
+
+ /**
+ * Returns the ordinal (either explicitly or implicitly) assigned to the enumerator.
+ */
+ public Long getOrdinal();
+ }
+}
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 3cbe4a0..8d10aad 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
@@ -7,6 +7,7 @@
*/
package org.eclipse.cdt.qt.core.index;
+import java.util.Collection;
import java.util.List;
/**
@@ -36,4 +37,11 @@ public interface IQObject extends IQElement {
* base classes.
*/
public List<IQObject> getBases();
+
+ /**
+ * Returns an unsorted collection of all Q_ENUMS macro expansions within this QObject's class
+ * declaration.
+ * @see IQEnum
+ */
+ public Collection<IQEnum> getEnums();
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QEnum.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QEnum.java
new file mode 100644
index 0000000..0610f61
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QEnum.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.internal.core.index;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.dom.ast.IEnumerator;
+import org.eclipse.cdt.core.dom.ast.IValue;
+import org.eclipse.cdt.qt.core.index.IQEnum;
+
+public class QEnum implements IQEnum {
+
+ private final String name;
+ private final boolean isFlag;
+ private final List<IQEnum.Enumerator> enumerators;
+
+ public QEnum(String name, boolean isFlag, List<IEnumerator> enumerators) {
+ this.name = name;
+ this.isFlag = isFlag;
+ this.enumerators = new ArrayList<IQEnum.Enumerator>(enumerators.size());
+ for (IEnumerator enumerator : enumerators)
+ this.enumerators.add(new Enumerator(enumerator));
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isFlag() {
+ return isFlag;
+ }
+
+ @Override
+ public Collection<IQEnum.Enumerator> getEnumerators() {
+ return enumerators;
+ }
+
+ private static class Enumerator implements IQEnum.Enumerator {
+
+ private final String name;
+ private final Long ordinal;
+
+ public Enumerator(IEnumerator enumerator) {
+ this.name = enumerator.getName();
+
+ IValue val = enumerator.getValue();
+ this.ordinal = val == null ? null : val.numericalValue();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Long getOrdinal() {
+ return ordinal;
+ }
+ }
+}
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 1ba7951..e1c93ba 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
@@ -8,10 +8,14 @@
package org.eclipse.cdt.qt.internal.core.index;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.dom.ast.IField;
+import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQEnum;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
@@ -20,6 +24,7 @@ public class QObject implements IQObject {
private final String name;
private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases;
+ private final List<IQEnum> enums;
public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
this.name = pdomQObject.getName();
@@ -28,6 +33,13 @@ public class QObject implements IQObject {
this.bases = new ArrayList<IQObject>();
for(QtPDOMQObject base : pdomQObject.findBases())
this.bases.add(new QObject(qtIndex, cdtIndex, base));
+
+ this.enums = new ArrayList<IQEnum>();
+ for(IField field : pdomQObject.getFields())
+ if (field instanceof QtPDOMQEnum) {
+ QtPDOMQEnum qEnum = (QtPDOMQEnum) field;
+ this.enums.add(new QEnum(field.getName(), qEnum.isFlag(), qEnum.getEnumerators()));
+ }
}
@Override
@@ -44,4 +56,9 @@ public class QObject implements IQObject {
public List<IQObject> getBases() {
return bases;
}
+
+ @Override
+ public Collection<IQEnum> getEnums() {
+ return enums;
+ }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTImageLocation.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTImageLocation.java
new file mode 100644
index 0000000..e7b063a
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTImageLocation.java
@@ -0,0 +1,88 @@
+/*
+ * 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.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
+import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
+
+/**
+ * The location of the signal/slot reference is stored as the location of the parent
+ * macro expansion + an offset, which is the number of characters between the start
+ * of the expansion and the start of the argument (including whitespace). E.g. in,
+ *
+ * <pre>
+ * MACRO( expansionParameter )
+ * ^ ^ ^ c: end of reference name
+ * | +----------------- b: start of reference name
+ * +------------------------ a: start of macro expansion
+ * </pre>
+ *
+ * The offset is b - a and length is c - b. This means that the result of 'Find
+ * References' will highlight just "parameter".
+ */
+public class QtASTImageLocation implements IASTImageLocation {
+
+ private final IASTFileLocation refLocation;
+ private final int offset;
+ private final int length;
+
+ public QtASTImageLocation(IASTFileLocation refLocation, int offset, int length) {
+ this.refLocation = refLocation;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ @Override
+ public IASTFileLocation asFileLocation() {
+ return this;
+ }
+
+ @Override
+ public String getFileName() {
+ return refLocation.getFileName();
+ }
+
+ @Override
+ public int getNodeOffset() {
+ return refLocation.getNodeOffset() + offset;
+ }
+
+ @Override
+ public int getNodeLength() {
+ return length;
+ }
+
+ @Override
+ public int getStartingLineNumber() {
+ return refLocation.getStartingLineNumber();
+ }
+
+ @Override
+ public int getEndingLineNumber() {
+ return refLocation.getEndingLineNumber();
+ }
+
+ @Override
+ public IASTPreprocessorIncludeStatement getContextInclusionStatement() {
+ return refLocation.getContextInclusionStatement();
+ }
+
+ @Override
+ public int getLocationKind() {
+ return REGULAR_CODE;
+ }
+}
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 c1cda53..6aaca0e 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
@@ -7,14 +7,26 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
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.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.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
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;
@@ -24,6 +36,11 @@ public class QtASTVisitor extends ASTVisitor {
private final IIndexSymbols symbols;
private final LocationMap locationMap;
+ private static final Pattern expansionParamRegex = Pattern.compile("^" + "(?:Q_ENUMS|Q_FLAGS)" + "\\s*\\((.*)\\)$");
+ private static final Pattern qualNameRegex = Pattern.compile("\\s*((?:[^\\s:]+\\s*::\\s*)*[^\\s:]+).*");
+
+ private static final Pattern declareFlagsRegex = Pattern.compile("^Q_DECLARE_FLAGS\\s*\\(\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\)$");
+
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
@@ -31,6 +48,23 @@ public class QtASTVisitor extends ASTVisitor {
this.locationMap = locationMap;
}
+ @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);
+ }
+
private boolean isQObject(ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) {
// The class definition must contain a Q_OBJECT expansion.
@@ -43,26 +77,95 @@ public class QtASTVisitor extends ASTVisitor {
return false;
}
+ private class EnumDecl {
+ private final String name;
+ private final boolean isFlag;
+ private final IASTName refName;
+ private final QtASTImageLocation location;
+
+ public EnumDecl(String name, boolean isFlag, IASTName refName, int offset, int length) {
+ this.name = name;
+ this.isFlag = isFlag;
+ this.refName = refName;
+ this.location = new QtASTImageLocation(refName.getFileLocation(), offset, length);
+ }
+
+ public void handle(IASTPreprocessorIncludeStatement owner, ICPPASTCompositeTypeSpecifier spec, QObjectName qobjName, Map<String, String> aliases) {
+
+ String alias = aliases.get(name);
+
+ 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;
+ }
+
+ QtEnumName astName = new QtEnumName(qobjName, refName, name, cppName, location, isFlag);
+ symbols.add(owner, astName, qobjName);
+
+ if (cppName != null)
+ symbols.add(owner, new ASTDelegatedName.Reference(cppName, location), astName);
+ }
+ }
+ }
+
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);
+
+ // There are three macros that are significant to QEnums, Q_ENUMS, Q_FLAGS, and Q_DECLARE_FLAGS.
+ // All macro expansions in the QObject class definition are examined to find instances of these
+ // three. Two lists are created during this processing. Then the those lists are uses to create
+ // the QEnum instances.
+
+ List<EnumDecl> enumDecls = new ArrayList<QtASTVisitor.EnumDecl>();
+ Map<String, String> flagAliases = new HashMap<String, String>();
+
+ for (IASTPreprocessorMacroExpansion expansion : expansions) {
+ String macroName = String.valueOf(expansion.getMacroReference());
+ if (QtKeywords.Q_OBJECT.equals(macroName))
+ continue;
+ if (QtKeywords.Q_ENUMS.equals(macroName))
+ extractEnumDecls(expansion, false, enumDecls);
+ else if (QtKeywords.Q_FLAGS.equals(macroName))
+ extractEnumDecls(expansion, true, enumDecls);
+ else if (QtKeywords.Q_DECLARE_FLAGS.equals(macroName)) {
+ Matcher m = declareFlagsRegex.matcher(expansion.getRawSignature());
+ if (m.matches()) {
+ String flagName = m.group(1);
+ String enumName = m.group(2);
+ flagAliases.put(flagName, enumName);
+ }
+ }
+ }
+
+ for(EnumDecl decl : enumDecls)
+ decl.handle(owner, spec, qobjName, flagAliases);
}
- @Override
- public int visit(IASTDeclSpecifier declSpec) {
- if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
- ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) declSpec;
+ private void extractEnumDecls(IASTPreprocessorMacroExpansion expansion, boolean isFlag, List<EnumDecl> decls) {
+ String signature = expansion.getRawSignature();
+ Matcher m = expansionParamRegex.matcher(signature);
+ if (!m.matches())
+ return;
- IASTFileLocation loc = spec.getFileLocation();
- IASTPreprocessorIncludeStatement owner = loc == null ? null : loc.getContextInclusionStatement();
+ IASTName refName = expansion.getMacroReference();
+ String param = m.group(1);
+ for(int offset = m.start(1), end = param.length(); !param.isEmpty(); offset += end, param = param.substring(end)) {
+ m = qualNameRegex.matcher(param);
+ if (!m.matches())
+ break;
- IASTPreprocessorMacroExpansion[] expansions = locationMap.getMacroExpansions(loc);
+ int start = m.start(1);
+ end = m.end(1);
- if (isQObject(spec, expansions))
- handleQObject(owner, spec, expansions);
+ String enumName = m.group(1);
+ decls.add(new EnumDecl(enumName, isFlag, refName, offset + start, end - start));
}
-
- return super.visit(declSpec);
}
}
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
new file mode 100644
index 0000000..5536f11
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
@@ -0,0 +1,135 @@
+/*
+ * 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.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.IBinding;
+import org.eclipse.cdt.internal.core.dom.Linkage;
+
+@SuppressWarnings("restriction")
+public class QtEnumName extends ASTDelegatedName {
+
+ private final QObjectName qobjName;
+ private final String qtEnumName;
+ private final IASTName cppEnumName;
+ private final QtASTImageLocation location;
+ private final boolean isFlag;
+
+ private ASTNodeProperty propertyInParent;
+
+ public QtEnumName(QObjectName qobjName, IASTName ast, String qtEnumName, IASTName cppEnumName, QtASTImageLocation location, boolean isFlag) {
+ super(ast);
+ this.qobjName = qobjName;
+ this.qtEnumName = qtEnumName;
+ this.cppEnumName = cppEnumName;
+ this.location = location;
+ this.isFlag = isFlag;
+ }
+
+ public boolean isFlag() {
+ return isFlag;
+ }
+
+ @Override
+ protected IBinding createBinding() {
+ IBinding owner = qobjName.getBinding();
+ QtBinding qobj = owner == null ? null : (QtBinding) owner.getAdapter(QtBinding.class);
+ return new QtBinding(QtPDOMNodeType.QEnum, qobj, this, cppEnumName);
+ }
+
+ @Override
+ public IASTFileLocation getFileLocation() {
+ return location;
+ }
+
+ @Override
+ public IASTNode getParent() {
+ return qobjName;
+ }
+
+ @Override
+ public IASTNode[] getChildren() {
+ return IASTNode.EMPTY_NODE_ARRAY;
+ }
+
+ @Override
+ public void setParent(IASTNode node) {
+ throw new IllegalStateException("attempt to modify parent of Qt Enum"); //$NON-NLS-1$
+ }
+
+ @Override
+ public ASTNodeProperty getPropertyInParent() {
+ return propertyInParent;
+ }
+
+ @Override
+ public void setPropertyInParent(ASTNodeProperty property) {
+ propertyInParent = property;
+ }
+
+ @Override
+ public char[] getSimpleID() {
+ return qtEnumName.toCharArray();
+ }
+
+ @Override
+ public String getRawSignature() {
+ return qtEnumName;
+ }
+
+ @Override
+ public IASTNode getOriginalNode() {
+ return this;
+ }
+
+ @Override
+ public boolean isDeclaration() {
+ return false;
+ }
+
+ @Override
+ public boolean isReference() {
+ return false;
+ }
+
+ @Override
+ public boolean isDefinition() {
+ return true;
+ }
+
+ @Override
+ public int getRoleOfName(boolean allowResolution) {
+ return IASTNameOwner.r_definition;
+ }
+
+ @Override
+ public ILinkage getLinkage() {
+ return Linkage.QT_LINKAGE;
+ }
+
+ @Override
+ public IASTImageLocation getImageLocation() {
+ return location;
+ }
+
+ @Override
+ public IASTName copy() {
+ return copy(CopyStyle.withoutLocations);
+ }
+
+ @Override
+ public IASTName copy(CopyStyle style) {
+ return new QtEnumName(qobjName, delegate, qtEnumName, cppEnumName, location, isFlag);
+ }
+}
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
index ebd0968..1f1e6c4 100644
--- 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
@@ -130,6 +130,9 @@ public class QtPDOMLinkage extends PDOMLinkage {
case QObject:
pdomBinding = new QtPDOMQObject(this, qtBinding);
break;
+ case QEnum:
+ pdomBinding = new QtPDOMQEnum(this, adaptBinding(qtBinding.getOwner()), qtBinding);
+ break;
}
// If a PDOMBinding was created, then add it to the linkage before returning it.
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 2048afe..182333c 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
@@ -14,7 +14,8 @@ import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public enum QtPDOMNodeType {
- QObject;
+ QObject,
+ QEnum;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
@@ -45,6 +46,8 @@ public enum QtPDOMNodeType {
switch(node) {
case QObject:
return new QtPDOMQObject(linkage, record);
+ case QEnum:
+ return new QtPDOMQEnum(linkage, record);
}
return null;
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
new file mode 100644
index 0000000..c281399
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
@@ -0,0 +1,159 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+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.dom.PDOMBinding;
+import org.eclipse.cdt.qt.core.QtPlugin;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public class QtPDOMQEnum extends QtPDOMBinding implements IField {
+
+ private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
+ protected static enum Field {
+ 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 IS_FLAG_MASK = 1;
+
+ private QtPDOMQObject qobj;
+
+ protected QtPDOMQEnum(QtPDOMLinkage linkage, long record) throws CoreException {
+ super(linkage, record);
+ }
+
+ public QtPDOMQEnum(QtPDOMLinkage linkage, PDOMBinding parent, QtBinding binding) throws CoreException {
+ super(linkage, parent, binding);
+
+ // The flags are set in several sections, and then written to the Database in one operation.
+ byte flags = 0;
+
+ IASTName qtName = binding.getQtName();
+ if (qtName instanceof QtEnumName
+ && ((QtEnumName) qtName).isFlag())
+ flags |= IS_FLAG_MASK;
+
+ // Write the flags to the database.
+ getDB().putByte(Field.Flags.getRecord(record), flags);
+
+ if (!(parent instanceof QtPDOMQObject))
+ this.qobj = null;
+ else {
+ this.qobj = (QtPDOMQObject) parent;
+ this.qobj.addChild(this);
+ }
+ }
+
+ @Override
+ public int getRecordSize() {
+ return Field.Last.offset;
+ }
+
+ public boolean isFlag() throws CoreException {
+ byte flags = getDB().getByte(Field.Flags.getRecord(record));
+ return (flags & IS_FLAG_MASK) == IS_FLAG_MASK;
+ }
+
+ @Override
+ public int getNodeType() {
+ 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<IEnumerator> getEnumerators() throws CoreException {
+ IBinding cppBinding = getCppBinding();
+ if (!(cppBinding instanceof IEnumeration))
+ return Collections.emptyList();
+
+ IEnumeration cppEnum = (IEnumeration) cppBinding;
+ return 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.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 a3011bc..c65bc80 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
@@ -10,6 +10,7 @@ package org.eclipse.cdt.qt.tests;
import java.util.Collection;
import java.util.Iterator;
+import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.QtIndex;
@@ -46,4 +47,132 @@ public class QObjectTests extends BaseQtTestCase {
IQObject qobj_D2 = qtIndex.findQObject(new String[]{ "D2" });
assertNull(qobj_D2);
}
+
+ // #include "junit-QObject.hh"
+ // template <typename T> class QList {};
+ // class QString {};
+ // class Q0 : public QObject
+ // {
+ // Q_OBJECT
+ // public:
+ // enum EB { eb0 = 0xff };
+ // };
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ //
+ // enum E0 { e0a, e0b };
+ //
+ // Q_ENUMS( E0 Q0::EB )
+ // Q_ENUMS( E1 )
+ //
+ // enum E1 { e1a, e1b = 2 };
+ // };
+ public void testEnums() throws Exception {
+ loadComment("qenums.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ Collection<IQEnum> qEnums = qobj.getEnums();
+ assertNotNull(qEnums);
+ assertEquals(3, qEnums.size());
+ for(IQEnum qEnum : qEnums) {
+ String name = qEnum.getName();
+ assertFalse(qEnum.isFlag());
+ if ("E0".equals(name)) {
+ Collection<IQEnum.Enumerator> enumerators = qEnum.getEnumerators();
+ assertNotNull(enumerators);
+ assertEquals(2, enumerators.size());
+ for (IQEnum.Enumerator enumerator : enumerators) {
+ Long ordinal = enumerator.getOrdinal();
+ if (Long.valueOf(0).equals(ordinal))
+ assertEquals("e0a", enumerator.getName());
+ else if (Long.valueOf(1).equals(ordinal))
+ assertEquals("e0b", enumerator.getName());
+ else
+ fail("unexpected " + name + "::" + enumerator.getName() + " = " + String.valueOf(ordinal));
+ }
+ } else if("E1".equals(name)) {
+ Collection<IQEnum.Enumerator> enumerators = qEnum.getEnumerators();
+ assertNotNull(enumerators);
+ assertEquals(2, enumerators.size());
+ for (IQEnum.Enumerator enumerator : enumerators) {
+ Long ordinal = enumerator.getOrdinal();
+ if (Long.valueOf(0).equals(ordinal))
+ assertEquals("e1a", enumerator.getName());
+ else if (Long.valueOf(2).equals(ordinal))
+ assertEquals("e1b", enumerator.getName());
+ else
+ fail("unexpected " + name + "::" + enumerator.getName() + " = " + String.valueOf(ordinal));
+ }
+ } else if("Q0::EB".equals(name)) {
+ Collection<IQEnum.Enumerator> enumerators = qEnum.getEnumerators();
+ assertNotNull(enumerators);
+ assertEquals(1, enumerators.size());
+ for (IQEnum.Enumerator enumerator : enumerators) {
+ Long ordinal = enumerator.getOrdinal();
+ if (Long.valueOf(255).equals(ordinal))
+ assertEquals("eb0", enumerator.getName());
+ else
+ fail("unexpected " + name + "::" + enumerator.getName() + " = " + String.valueOf(ordinal));
+ }
+ } else {
+ fail("unexpected Q_ENUM " + name);
+ }
+ }
+ }
+
+ // #include "junit-QObject.hh"
+ // template <typename T> class QList {};
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ // enum Enum { e0 };
+ // Q_DECLARE_FLAGS(Flag, Enum)
+ // Q_FLAGS(Flag);
+ // enum Enum2 { e2 };
+ // Q_FLAGS(Flag2);
+ // Q_DECLARE_FLAGS(Flag2, Enum2)
+ // Q_DECLARE_FLAGS(Flag2b, Enum2)
+ // enum Enum3 { e3 };
+ // Q_DECLARE_FLAGS(Flag3, Enum3)
+ // };
+ public void testFlags() throws Exception {
+ loadComment("qflags.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ Collection<IQEnum> qEnums = qobj.getEnums();
+ assertNotNull(qEnums);
+ assertEquals(2, qEnums.size());
+
+ for(IQEnum qEnum : qEnums) {
+ assertNotNull(qEnum);
+ assertTrue(qEnum.isFlag());
+ if ("Flag".equals(qEnum.getName())) {
+ Collection<IQEnum.Enumerator> enumerators = qEnum.getEnumerators();
+ assertNotNull(enumerators);
+ assertEquals(1, enumerators.size());
+ assertEquals("e0", enumerators.iterator().next().getName());
+ } else if("Flag2".equals(qEnum.getName())) {
+ Collection<IQEnum.Enumerator> enumerators = qEnum.getEnumerators();
+ assertNotNull(enumerators);
+ assertEquals(1, enumerators.size());
+ assertEquals("e2", enumerators.iterator().next().getName());
+ } else
+ fail("unexpected Q_FLAGS " + qEnum.getName());
+ }
+ }
}