From 0f482a80b2cfb9e1d8d44afef2a787ee04181dcd Mon Sep 17 00:00:00 2001 From: Andrew Eidsness Date: Fri, 29 Nov 2013 15:09:55 -0500 Subject: Bug 422841: Add QEnum to QtIndex 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 Reviewed-on: https://git.eclipse.org/r/19147 Reviewed-by: Doug Schaefer IP-Clean: Doug Schaefer Tested-by: Doug Schaefer --- .../src/org/eclipse/cdt/qt/core/index/IQEnum.java | 79 ++++++++++ .../org/eclipse/cdt/qt/core/index/IQObject.java | 8 ++ .../eclipse/cdt/qt/internal/core/index/QEnum.java | 69 +++++++++ .../cdt/qt/internal/core/index/QObject.java | 17 +++ .../qt/internal/core/pdom/QtASTImageLocation.java | 88 ++++++++++++ .../cdt/qt/internal/core/pdom/QtASTVisitor.java | 125 ++++++++++++++-- .../cdt/qt/internal/core/pdom/QtEnumName.java | 135 +++++++++++++++++ .../cdt/qt/internal/core/pdom/QtPDOMLinkage.java | 3 + .../cdt/qt/internal/core/pdom/QtPDOMNodeType.java | 5 +- .../cdt/qt/internal/core/pdom/QtPDOMQEnum.java | 159 +++++++++++++++++++++ .../src/org/eclipse/cdt/qt/tests/QObjectTests.java | 129 +++++++++++++++++ 11 files changed, 805 insertions(+), 12 deletions(-) create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQEnum.java create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QEnum.java create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTImageLocation.java create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java create mode 100644 qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java (limited to 'qt') 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 00000000000..062c47c3cef --- /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. + *
+ * 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 };
+ * }
+ * 
+ * NOTE: http://qt-project.org/doc/qt-4.8/qobject.html#Q_ENUMS + *
+ * 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(). + *
+ * 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. + *

+ * 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 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 3cbe4a0f4cc..8d10aadefef 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 getBases(); + + /** + * Returns an unsorted collection of all Q_ENUMS macro expansions within this QObject's class + * declaration. + * @see IQEnum + */ + public Collection 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 00000000000..0610f6101f8 --- /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 enumerators; + + public QEnum(String name, boolean isFlag, List enumerators) { + this.name = name; + this.isFlag = isFlag; + this.enumerators = new ArrayList(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 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 1ba7951e818..e1c93bad2cb 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 bases; + private final List 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(); for(QtPDOMQObject base : pdomQObject.findBases()) this.bases.add(new QObject(qtIndex, cdtIndex, base)); + + this.enums = new ArrayList(); + 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 getBases() { return bases; } + + @Override + public Collection 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 00000000000..e7b063afc66 --- /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, + * + *

+ * MACRO( expansionParameter )
+ * ^      ^                ^ c: end of reference name
+ * |      +----------------- b: start of reference name
+ * +------------------------ a: start of macro expansion
+ * 
+ * + * 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 c1cda5330b0..6aaca0e4ff4 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 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 enumDecls = new ArrayList(); + Map flagAliases = new HashMap(); + + 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 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 00000000000..5536f110c57 --- /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 ebd0968ddf2..1f1e6c4f75c 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 2048afebb6e..182333c9fc7 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 00000000000..c28139955d7 --- /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 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 a3011bc03fd..c65bc80146b 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 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 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 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 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 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 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 qEnums = qobj.getEnums(); + assertNotNull(qEnums); + assertEquals(2, qEnums.size()); + + for(IQEnum qEnum : qEnums) { + assertNotNull(qEnum); + assertTrue(qEnum.isFlag()); + if ("Flag".equals(qEnum.getName())) { + Collection enumerators = qEnum.getEnumerators(); + assertNotNull(enumerators); + assertEquals(1, enumerators.size()); + assertEquals("e0", enumerators.iterator().next().getName()); + } else if("Flag2".equals(qEnum.getName())) { + Collection enumerators = qEnum.getEnumerators(); + assertNotNull(enumerators); + assertEquals(1, enumerators.size()); + assertEquals("e2", enumerators.iterator().next().getName()); + } else + fail("unexpected Q_FLAGS " + qEnum.getName()); + } + } } -- cgit v1.2.3