mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
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 <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>
This commit is contained in:
parent
dc746352a3
commit
0f482a80b2
11 changed files with 812 additions and 19 deletions
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,24 +48,6 @@ public class QtASTVisitor extends ASTVisitor {
|
|||
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) {
|
||||
|
@ -65,4 +64,108 @@ public class QtASTVisitor extends ASTVisitor {
|
|||
|
||||
return super.visit(declSpec);
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
private void extractEnumDecls(IASTPreprocessorMacroExpansion expansion, boolean isFlag, List<EnumDecl> decls) {
|
||||
String signature = expansion.getRawSignature();
|
||||
Matcher m = expansionParamRegex.matcher(signature);
|
||||
if (!m.matches())
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
int start = m.start(1);
|
||||
end = m.end(1);
|
||||
|
||||
String enumName = m.group(1);
|
||||
decls.add(new EnumDecl(enumName, isFlag, refName, offset + start, end - start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue