1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 17:26:01 +02:00

Bug 422841 QtIndex API for qmlRegisterType function calls

The Qt spec includes a special function that is used to introduce C++
types to a namespace that is accessible from QML.  E.g.,

	qmlRegisterType<Q>( "uri", 1, 0, "QMLType" );

This will create a QML type called QMLType.  The type will include the
signals, slots, and invokable that are defined in the C++ class Q.  The
type is accessible in QML using the given URI and the version is 1.0.

More information is available at:

	http://qt-project.org/doc/qt-4.8/qdeclarativeengine.html

This patch adds IQmlRegisteredType, a collection of which can be
accessed from a new method in QtIndex.

This also includes new test cases for this feature.

Change-Id: I70c44d1d8d3a0594de44e692a16f7b26396e8464
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/20347
Tested-by: Hudson CI
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2014-01-07 12:45:00 -05:00 committed by Doug Schaefer
parent 0e196ca5f2
commit 78fc903d16
18 changed files with 877 additions and 36 deletions

View file

@ -12,6 +12,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
@ -27,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.model.ICProject;
@ -34,6 +36,7 @@ import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.qt.core.index.IQMethod;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.core.resources.IProject;
@ -76,6 +79,13 @@ public class ASTUtil {
public static String getFullyQualifiedName(IBinding binding) {
if (binding == null)
return null;
if (binding instanceof ICPPBinding)
try {
return getFullyQualifiedName(((ICPPBinding) binding).getQualifiedName());
} catch(DOMException e) {
QtPlugin.log(e);
return null;
}
String ownerName = getFullyQualifiedName(binding.getOwner());
return (ownerName == null ? "" : ownerName) + "::" + binding.getName();
@ -86,11 +96,16 @@ public class ASTUtil {
* input array's elements.
*/
public static String getFullyQualifiedName(String[] qualName) {
String fullyQualifiedName = "";
for(int i = 0; i < qualName.length; ++i) {
fullyQualifiedName += "::" + qualName[i];
boolean first = true;
StringBuilder str = new StringBuilder();
for(String name : qualName) {
if (first)
first = false;
else
str.append("::");
str.append(name);
}
return fullyQualifiedName;
return str.toString();
}
// NOTE: This expression allows embedded line terminators (?s) for cases where the code looks like:

View file

@ -11,7 +11,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.internal.qt.core.pdom.QtPDOMProperty;
@ -36,15 +35,8 @@ public class QObject implements IQObject {
private final List<IQEnum> enums;
private final Map<String, String> classInfos;
/**
* QObjects are stored in the QtLinkage using their fully qualified name. The API
* for IQObject does not expect the leading ::, so they are stripped when the
* object is read from the PDOM.
*/
private static Pattern StripLeadingQual_Regex = Pattern.compile("^::(.*)$");
public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
this.name = StripLeadingQual_Regex.matcher(pdomQObject.getName()).replaceAll("$1");
this.name = pdomQObject.getName();
this.pdomQObject = pdomQObject;
List<IQMethod> baseSlots = new ArrayList<IQMethod>();

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2014 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.qt.core.index;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.internal.qt.core.pdom.QtPDOMQmlRegistration;
import org.eclipse.cdt.internal.qt.core.pdom.QtPDOMQmlUncreatableRegistration;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQmlRegistered;
import org.eclipse.core.runtime.CoreException;
public class QmlRegistered implements IQmlRegistered {
private final QtIndexImpl qtIndex;
private final IQmlRegistered.Kind kind;
private final String[] ownerName;
private final Long version;
private final String uri;
private final Long major;
private final Long minor;
private final String qmlName;
private final String reason;
private IQObject qObject;
public static QmlRegistered create(QtIndexImpl qtIndex, IBinding pdom) throws CoreException {
if (pdom instanceof QtPDOMQmlUncreatableRegistration)
return new QmlRegistered(qtIndex, (QtPDOMQmlUncreatableRegistration) pdom);
if (pdom instanceof QtPDOMQmlRegistration)
return new QmlRegistered(qtIndex, (QtPDOMQmlRegistration) pdom);
return null;
}
private QmlRegistered(QtIndexImpl qtIndex, QtPDOMQmlRegistration pdom) throws CoreException {
this.qtIndex = qtIndex;
this.kind = IQmlRegistered.Kind.Type;
String qobjName = pdom.getQObjectName();
this.ownerName = qobjName == null ? null : qobjName.split("::");
this.version = pdom.getVersion();
this.uri = pdom.getUri();
this.major = pdom.getMajor();
this.minor = pdom.getMinor();
this.qmlName = pdom.getQmlName();
this.reason = null;
}
private QmlRegistered(QtIndexImpl qtIndex, QtPDOMQmlUncreatableRegistration pdom) throws CoreException {
this.qtIndex = qtIndex;
this.kind = IQmlRegistered.Kind.Uncreatable;
String qobjName = pdom.getQObjectName();
this.ownerName = qobjName == null ? null : qobjName.split("::");
this.version = pdom.getVersion();
this.uri = pdom.getUri();
this.major = pdom.getMajor();
this.minor = pdom.getMinor();
this.qmlName = pdom.getQmlName();
this.reason = pdom.getReason();
}
@Override
public IQmlRegistered.Kind getKind() {
return kind;
}
@Override
public IQObject getQObject() {
if (qObject == null
&& ownerName != null)
qObject = qtIndex.findQObject(ownerName);
return qObject;
}
@Override
public Long getVersion() {
return version;
}
@Override
public String getURI() {
return uri;
}
@Override
public Long getMajor() {
return major;
}
@Override
public Long getMinor() {
return minor;
}
@Override
public String getQmlName() {
return qmlName;
}
@Override
public String getReason() {
return reason;
}
}

View file

@ -7,6 +7,11 @@
*/
package org.eclipse.cdt.internal.qt.core.index;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.index.IIndex;
@ -15,8 +20,10 @@ import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.internal.qt.core.ASTUtil;
import org.eclipse.cdt.internal.qt.core.pdom.AbstractQtPDOMClass;
import org.eclipse.cdt.internal.qt.core.pdom.QtPDOMQObject;
import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.index.IQGadget;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQmlRegistered;
import org.eclipse.cdt.qt.core.index.QtIndex;
import org.eclipse.core.runtime.CoreException;
@ -24,6 +31,11 @@ public class QtIndexImpl extends QtIndex {
private final CDTIndex cdtIndex;
private static final Pattern QmlTypeNameRegex
= Pattern.compile("^(?:"
+ QtKeywords.QML_REGISTER_TYPE + '|' + QtKeywords.QML_REGISTER_UNCREATABLE_TYPE
+ ")<.*>\0(.*)$");
private static final IndexFilter QtLinkageFilter = new IndexFilter() {
@Override
public boolean acceptLinkage(ILinkage linkage) {
@ -50,6 +62,11 @@ public class QtIndexImpl extends QtIndex {
return name == null ? null : cdtIndex.get(new QGadgetImplAccessor(name));
}
@Override
public Collection<IQmlRegistered> getQmlRegistered() {
return cdtIndex.get(new QMLRegisteredAccessor());
}
private class QObjectImplAccessor implements CDTIndex.Accessor<IQObject> {
private final char[] name;
@ -92,4 +109,22 @@ public class QtIndexImpl extends QtIndex {
return null;
}
}
private class QMLRegisteredAccessor implements CDTIndex.Accessor<Collection<IQmlRegistered>> {
@Override
public Collection<IQmlRegistered> access(IIndex index) throws CoreException {
Collection<IQmlRegistered> types = null;
for(IIndexBinding binding : index.findBindings(QmlTypeNameRegex, false, QtLinkageFilter, null)) {
IQmlRegistered qml = QmlRegistered.create(QtIndexImpl.this, binding);
if (qml != null) {
if (types == null)
types = new ArrayList<IQmlRegistered>();
types.add(qml);
}
}
return types == null ? Collections.<IQmlRegistered>emptyList() : types;
}
}
}

View file

@ -29,7 +29,9 @@ public class QObjectName extends AbstractQClassName {
public QObjectName(ICPPASTCompositeTypeSpecifier spec) {
super(spec);
fullyQualifiedName = ASTUtil.getFullyQualifiedName(spec.getName()).toCharArray();
String fqn = ASTUtil.getFullyQualifiedName(spec.getName());
fullyQualifiedName = fqn == null ? new char[0] : fqn.toCharArray();
}
@Override

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2014 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.qt.core.pdom;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.internal.qt.core.ASTUtil;
import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.index.IQmlRegistered;
import org.eclipse.core.runtime.CoreException;
public class QmlTypeRegistration extends ASTDelegatedName implements IQtASTName {
private final ICPPTemplateInstance functionInstanceBinding;
private final IASTFunctionCallExpression fnCall;
private final IQmlRegistered.Kind kind;
private char[] simpleID;
public QmlTypeRegistration(IASTName ast, ICPPTemplateInstance functionInstanceBinding, IASTFunctionCallExpression fnCall) {
super(ast);
this.functionInstanceBinding = functionInstanceBinding;
this.fnCall = fnCall;
if (QtKeywords.QML_REGISTER_UNCREATABLE_TYPE.equals(functionInstanceBinding.getName()))
this.kind = IQmlRegistered.Kind.Uncreatable;
else
this.kind = IQmlRegistered.Kind.Type;
}
@Override
public char[] getSimpleID() {
if (simpleID == null) {
IASTInitializerClause[] args = fnCall.getArguments();
simpleID = (functionInstanceBinding.getName()
+ ASTTypeUtil.getArgumentListString(functionInstanceBinding.getTemplateArguments(), true)
+ "\0("
+ asStringForName(args, 0) + ','
+ asStringForName(args, 1) + ','
+ asStringForName(args, 2) + ','
+ asStringForName(args, 3) + ')'
).toCharArray();
}
return simpleID;
}
@Override
public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
switch(kind) {
case Type:
return new QtPDOMQmlRegistration(linkage, this, delegate);
case Uncreatable:
return new QtPDOMQmlUncreatableRegistration(linkage, this, delegate);
}
return null;
}
public String getQObjectName() {
ICPPTemplateArgument[] args = functionInstanceBinding.getTemplateArguments();
if (args.length < 1)
return null;
IType type = args[0].getTypeValue();
return type instanceof ICPPBinding ? ASTUtil.getFullyQualifiedName((ICPPBinding) type) : null;
}
public Long getVersion() {
ICPPTemplateArgument[] args = functionInstanceBinding.getTemplateArguments();
if (args.length < 2)
return null;
IValue val = args[1].getNonTypeValue();
return val == null ? null : val.numericalValue();
}
public String getUri() {
return getArgAsStringOrNull(0);
}
public Long getMajor() {
return getArgAsLongOrNull(1);
}
public Long getMinor() {
return getArgAsLongOrNull(2);
}
public String getQmlName() {
return getArgAsStringOrNull(3);
}
public String getReason() {
return getArgAsStringOrNull(4);
}
private String asStringForName(IASTInitializerClause[] args, int index) {
String arg = args.length <= index ? null : asString(args[index]);
return arg == null ? "" : arg;
}
private String getArgAsStringOrNull(int index) {
IASTInitializerClause[] args = fnCall.getArguments();
if (args.length <= index)
return null;
return asString(args[index]);
}
private Long getArgAsLongOrNull(int index) {
IASTInitializerClause[] args = fnCall.getArguments();
if (args.length <= index)
return null;
String str = asString(args[index]);
if (str != null)
try {
return Long.parseLong(str);
} catch(NumberFormatException e) {
// This is caused by invalid user code, do not log it
}
return null;
}
private static String asString(IASTInitializerClause init) {
if (init instanceof IASTLiteralExpression) {
IASTLiteralExpression literal = (IASTLiteralExpression) init;
switch(literal.getKind()) {
case IASTLiteralExpression.lk_integer_constant:
return new String(literal.getValue());
case IASTLiteralExpression.lk_string_literal:
char[] value = literal.getValue();
return new String(value, 1, value.length - 2);
}
}
return null;
}
}

View file

@ -23,6 +23,7 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
@ -33,10 +34,12 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
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.internal.qt.core.ASTUtil;
import org.eclipse.cdt.internal.qt.core.QtFunctionCall;
import org.eclipse.cdt.internal.qt.core.QtMethodReference;
import org.eclipse.cdt.internal.qt.core.QtMethodUtil;
@ -124,7 +127,10 @@ public class QtASTVisitor extends ASTVisitor {
@Override
public int visit(IASTExpression expr) {
if (expr instanceof IASTFunctionCallExpression) {
Collection<QtMethodReference> refs = QtFunctionCall.getReferences((IASTFunctionCallExpression) expr);
IASTFunctionCallExpression call = (IASTFunctionCallExpression) expr;
// See if this is a QObject::connect or disconnect function call.
Collection<QtMethodReference> refs = QtFunctionCall.getReferences(call);
if (refs != null)
for (IASTName ref : refs) {
IASTFileLocation nameLoc = ref.getFileLocation();
@ -133,6 +139,27 @@ public class QtASTVisitor extends ASTVisitor {
symbols.add(owner, ref, null);
}
}
// See if this is a qmlRegisterType or qmlRegisterUncreatableType function call.
ICPPTemplateInstance templateFn = ASTUtil.resolveFunctionBinding(ICPPTemplateInstance.class, call);
if (QtKeywords.is_QmlType(templateFn)) {
IASTName fnName = null;
IASTExpression fnNameExpr = call.getFunctionNameExpression();
if (fnNameExpr instanceof IASTIdExpression) {
fnName = ((IASTIdExpression) fnNameExpr).getName();
}
IASTFileLocation nameLoc = call.getFileLocation();
if (nameLoc != null) {
QmlTypeRegistration qmlTypeReg = new QmlTypeRegistration(fnName, templateFn, call);
IASTPreprocessorIncludeStatement owner = nameLoc.getContextInclusionStatement();
symbols.add(owner, qmlTypeReg, null);
// the Qt data references the C++ function template instance specialization
if (fnName != null)
symbols.add(owner, new ASTNameReference(fnName), qmlTypeReg);
}
}
}
return super.visit(expr);

View file

@ -30,10 +30,6 @@ public abstract class QtPDOMBinding extends PDOMBinding {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
protected QtPDOMBinding(QtPDOMLinkage linkage, long record) {
@ -78,4 +74,49 @@ public abstract class QtPDOMBinding extends PDOMBinding {
return super.getAdapter(adapter);
}
/**
* Returns a Long from the given offset within this node's record. The permitted range of the Long
* is [Long.MIN_VALUE, Long.MAX_VALUE). Notice that Long.MAX_VALUE is excluded from the valid range.
*/
protected Long getLongOrNull(long offset) throws CoreException {
long val = getDB().getLong(record + offset);
return val == Long.MAX_VALUE ? null : Long.valueOf(val);
}
/**
* Puts the given Long into the database at the specified offset within this node's record. The permitted
* range for val is [Long.MIN_VALUE, Long.MAX_VALUE). Notice that Long.MAX_VALUE is excluded from
* the valid range.
* <p>
* The val parameter is allowed to be null. A value will be stored to the database so that later calls to
* {@link #getLongOrNull(long)} will return null;
*/
protected void putLongOrNull(long offset, Long val) throws CoreException {
getDB().putLong(record + offset, val == null ? Long.MAX_VALUE : val.longValue());
}
/**
* Returns a String from the given offset within this node's record. This method will return null if the
* database does not contain an IString at the specified location.
*/
protected String getStringOrNull(long offset) throws CoreException {
long rec = getDB().getRecPtr(record + offset);
return rec == 0 ? null : getDB().getString(rec).getString();
}
/**
* Puts the given String into the database at the specified offset within this node's record. Any IString
* that happens to already exist at the specified location will be deleted before the new value is stored.
* <p>
* The val parameter is allowed to be null. A value will be stored to the database so that later calls to
* {@link #getStringOrNull(long)} will return null;
*/
protected void putStringOrNull(long offset, String val) throws CoreException {
long rec = getDB().getRecPtr(record + offset);
if (rec != 0)
getDB().getString(rec).delete();
getDB().putRecPtr(record + offset, val == null ? 0 : getDB().newString(val).getRecord());
}
}

View file

@ -91,7 +91,7 @@ public class QtPDOMLinkage extends PDOMLinkage {
}
// IBinding#getAdapter cannot create an instance of PDOMBinding because the Linkage is required. This
// utility method uses #getAdapter to see if an instance has already been create. If not then a new
// utility method uses #getAdapter to see if an instance has already been created. If not then a new
// is created and stored in the AST binding.
@Override
public PDOMBinding adaptBinding(IBinding binding, boolean includeLocal) throws CoreException {

View file

@ -18,7 +18,9 @@ public enum QtPDOMNodeType {
QEnum,
QProperty,
QMethod,
QGadget;
QGadget,
QmlTypeRegistration,
QmlUncreatableRegistration;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
@ -32,7 +34,7 @@ public enum QtPDOMNodeType {
* This version can be reset when the PDOM's version changes because older Qt linkages will
* be dropped (along with everything else in that PDOM).
*/
public static final int VERSION = 1;
public static final int VERSION = 2;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
@ -60,6 +62,10 @@ public enum QtPDOMNodeType {
return new QtPDOMQMethod(linkage, record);
case QGadget:
return new QtPDOMQGadget(linkage, record);
case QmlTypeRegistration:
return new QtPDOMQmlRegistration(linkage, record);
case QmlUncreatableRegistration:
return new QtPDOMQmlUncreatableRegistration(linkage, record);
}
return null;

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.qt.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPDOMQmlRegistration extends QtPDOMBinding {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
CppRecord(Database.PTR_SIZE),
QObjectName(Database.PTR_SIZE),
Version(8), // Database doesn't have a LONG_SIZE
Uri(Database.PTR_SIZE),
Major(8),
Minor(8),
QmlName(Database.PTR_SIZE),
Last(0);
public final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
}
public QtPDOMQmlRegistration(QtPDOMLinkage linkage, long record) {
super(linkage, record);
}
public QtPDOMQmlRegistration(QtPDOMLinkage linkage, QmlTypeRegistration qmlTypeReg, IASTName cppName) throws CoreException {
super(linkage, null, qmlTypeReg);
putStringOrNull(Field.QObjectName.offset, qmlTypeReg.getQObjectName());
putLongOrNull(Field.Version.offset, qmlTypeReg.getVersion());
putStringOrNull(Field.Uri.offset, qmlTypeReg.getUri());
putLongOrNull(Field.Major.offset, qmlTypeReg.getMajor());
putLongOrNull(Field.Minor.offset, qmlTypeReg.getMinor());
putStringOrNull(Field.QmlName.offset, qmlTypeReg.getQmlName());
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
@Override
public int getNodeType() {
return QtPDOMNodeType.QmlTypeRegistration.Type;
}
public String getQObjectName() throws CoreException {
return getStringOrNull(Field.QObjectName.offset);
}
public Long getVersion() throws CoreException {
return getLongOrNull(Field.Version.offset);
}
public Long getMajor() throws CoreException {
return getLongOrNull(Field.Major.offset);
}
public Long getMinor() throws CoreException {
return getLongOrNull(Field.Minor.offset);
}
public String getUri() throws CoreException {
return getStringOrNull(Field.Uri.offset);
}
public String getQmlName() throws CoreException {
return getStringOrNull(Field.QmlName.offset);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2014 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.internal.qt.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPDOMQmlUncreatableRegistration extends QtPDOMQmlRegistration {
private static int offsetInitializer = QtPDOMQmlRegistration.Field.Last.offset;
protected static enum Field {
Reason(Database.PTR_SIZE),
Last(0);
public final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
}
public QtPDOMQmlUncreatableRegistration(QtPDOMLinkage linkage, long record) {
super(linkage, record);
}
public QtPDOMQmlUncreatableRegistration(QtPDOMLinkage linkage, QmlTypeRegistration qmlTypeReg, IASTName cppName) throws CoreException {
super(linkage, qmlTypeReg, cppName);
putStringOrNull(Field.Reason.offset, qmlTypeReg.getReason());
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
@Override
public int getNodeType() {
return QtPDOMNodeType.QmlUncreatableRegistration.Type;
}
public String getReason() throws CoreException {
return getStringOrNull(Field.Reason.offset);
}
}

View file

@ -89,6 +89,18 @@ public class QtKeywords {
&& DISCONNECT.equals(qualName[1]);
}
/**
* Returns true if the given binding will register a type with the QML type system and false
* otherwise.
*/
public static boolean is_QmlType(IBinding binding) {
String[] qualName = getFunctionQualifiedName(binding);
return qualName != null
&& qualName.length == 1
&& (QML_REGISTER_TYPE.equals(qualName[0])
|| QML_REGISTER_UNCREATABLE_TYPE.equals(qualName[0]));
}
private static String[] getFunctionQualifiedName(IBinding binding) {
// IBinding#getAdapter returns null when binding is an instance of
// PDOMCPPMethod.

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2014 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;
/**
* Represents a specific QML type registration.
* <p>
* Qt allows types to be registered with QML by calling the qmlRegisterType function,
* e.g.,
* <pre>
* class Q : public QObject { Q_OBJECT };
* qmlRegisterType&lt;Q&gt;( "uri", 1, 0, "Q" );
* </pre>
* Registers Q in the QML system with the name "Q", in the library imported from "uri"
* having the version number 1.0.
*/
public interface IQmlRegistered {
/**
* Identifies the kind of qmlRegister* function that was used to register the
* type. Qt 4.8 only defines two kinds, but in 5.0 there several more.
* <p>
* If a type has been registered more than once, then there will be several
* entries for it in the collection returned by {@link QtIndex#getQmlRegistered()}.
*/
public enum Kind {
/**
* Indicates that the type has been registered with a function call to
* qmlRegisterType.
*/
Type,
/**
* Indicates that the type has been registered with a function call to
* qmlRegisterUncreatableType.
*/
Uncreatable
}
/**
* Returns the kind of function that was used for this registration. In Qt 4.8,
* there are two variations of the qmlRegister* function; qmlRegisterType and
* qmlRegisterUncreatableType. In Qt 5.0 there are several more.
* <p>
* It is possible for the same type to be registered in different ways, although
* this generally indicates a problem in the client code.
*/
public IQmlRegistered.Kind getKind();
/**
* Returns QObject to which this registration applies. In the sample at {@link IQmlRegistered}
* this would return the IQObject for Q.
*/
public IQObject getQObject();
/**
* Returns the specific revision of the IQObject that was registered. Returns null if no
* revision was specified.
* <p>
* E.g.,
* <code>
* class Q : public QObject
* {
* Q_OBJECT
* signals:
* Q_REVISION(2) void sig();
* };
*
* qmlRegisterType<Q>( "uri", 1, 0, "Q1" );
* qmlRegisterType<Q, 2>( "uri", 1, 0, "Q2" );
* </code>
*
* The QML type "Q2" would have access to the "sig" signal, while "Q1" would not.
*
* @see IQMethod#getRevision()
* @see IQProperty#getRevision()
*/
public Long getVersion();
/**
* Returns the literal value of the first argument to the function if it can be
* resolved and null otherwise.
*/
public String getURI();
/**
* Returns the literal value of the second argument to the function if it can be
* resolved and null otherwise.
*/
public Long getMajor();
/**
* Returns the literal value of the third argument to the function if it can be
* resolved and null otherwise.
*/
public Long getMinor();
/**
* Returns the literal value of the fourth argument to the function if it can be
* resolved and null otherwise.
*/
public String getQmlName();
/**
* Returns the literal value of the fifth argument to qmlRegisterUncreatableType if it
* can be resolved and null otherwise.
*/
public String getReason();
}

View file

@ -7,6 +7,8 @@
*/
package org.eclipse.cdt.qt.core.index;
import java.util.Collection;
import org.eclipse.cdt.internal.qt.core.index.QtFactory;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.resources.IProject;
@ -70,4 +72,13 @@ public abstract class QtIndex {
* the given name.
*/
public abstract IQGadget findQGadget(String[] qualifiedName);
/**
* Find and return the types that have been registered with the Qt meta type system. This
* is the result of the function calls like:
* <pre>
* qmlRegisterType<Q>( "uri", 1, 2, "Qv1.2" );
* </pre>
*/
public abstract Collection<IQmlRegistered> getQmlRegistered();
}

View file

@ -20,6 +20,7 @@ public class AllQtTests extends TestSuite {
QObjectTests.class,
QtContentAssistantTests.class,
QtIndexTests.class,
QtRegressionTests.class);
QtRegressionTests.class,
QmlRegisteredTests.class);
}
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2014 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.tests;
import java.util.Collection;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQmlRegistered;
import org.eclipse.cdt.qt.core.index.QtIndex;
public class QmlRegisteredTests extends BaseQtTestCase {
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// };
//
// class D : public B
// {
// Q_OBJECT
// };
//
// static void func()
// {
// qmlRegisterType<B>( "b-uri", 1, 2, "B" );
// qmlRegisterType<B>( "b-uri.34", 3, 4, "B34" );
//
// const char * uri = "d-uri";
// int maj = 2, min = 3;
// const char * qmlName = "D1";
// qmlRegisterType<D, 1>( uri, maj, min, qmlName );
// }
public void testQMLRegisterType() throws Exception {
loadComment("qmlregistertype.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject b_qobj = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", b_qobj))
return;
assertNotNull(b_qobj);
Collection<IQmlRegistered> qmlRegistereds = qtIndex.getQmlRegistered();
assertNotNull(qmlRegistereds);
assertEquals(3, qmlRegistereds.size());
for(IQmlRegistered qmlRegistered : qmlRegistereds) {
IQObject qobj = qmlRegistered.getQObject();
assertNotNull(qobj);
// all values of B should be fully resolved, except for Revision, which was not provided
if (qobj.getName().equals("B")) {
assertNull(qmlRegistered.getVersion());
String qmlName = qmlRegistered.getQmlName();
assertNotNull(qmlName);
if ("B".equals(qmlName)) {
assertEquals(IQmlRegistered.Kind.Type, qmlRegistered.getKind());
assertEquals("b-uri", qmlRegistered.getURI());
assertEquals(Long.valueOf(1), qmlRegistered.getMajor());
assertEquals(Long.valueOf(2), qmlRegistered.getMinor());
assertNull(qmlRegistered.getReason());
} else if ("B34".equals(qmlName)) {
assertEquals(IQmlRegistered.Kind.Type, qmlRegistered.getKind());
assertEquals("b-uri.34", qmlRegistered.getURI());
assertEquals(Long.valueOf(3), qmlRegistered.getMajor());
assertEquals(Long.valueOf(4), qmlRegistered.getMinor());
assertNull(qmlRegistered.getReason());
} else {
fail("unexpected uri for B " + qmlName);
}
// the values for D are not expected to be resolved (yet), but it does have a Revision
} else if (qobj.getName().equals("D")) {
assertEquals(IQmlRegistered.Kind.Type, qmlRegistered.getKind());
assertEquals(Long.valueOf(1), qmlRegistered.getVersion());
assertNull(qmlRegistered.getURI());
assertNull(qmlRegistered.getMajor());
assertNull(qmlRegistered.getMinor());
assertNull(qmlRegistered.getQmlName());
assertNull(qmlRegistered.getReason());
} else {
fail("unexpected qmlRegistered " + qobj.getName());
}
}
}
// class T;
//
// static void func()
// {
// qmlRegisterType<T>( "t-uri", 3, 4, "qml-T" );
// }
public void testQMLRegisterFwdDecl() throws Exception {
loadComment("qmlregistertype.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
Collection<IQmlRegistered> qmlRegistereds = qtIndex.getQmlRegistered();
assertNotNull(qmlRegistereds);
assertEquals(1, qmlRegistereds.size());
IQmlRegistered qml = qmlRegistereds.iterator().next();
assertNotNull(qml);
assertEquals(IQmlRegistered.Kind.Type, qml.getKind());
assertEquals("t-uri", qml.getURI());
assertEquals(Long.valueOf(3), qml.getMajor());
assertEquals(Long.valueOf(4), qml.getMinor());
assertEquals("qml-T", qml.getQmlName());
assertNull(qml.getReason());
// The QObject has not been defined, so it cannot be found.
assertNull(qml.getQObject());
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// };
//
// class D : public B
// {
// Q_OBJECT
// };
//
// static void func()
// {
// qmlRegisterUncreatableType<B>( "b-uri", 1, 2, "B", QString( "msg1" ) );
// qmlRegisterUncreatableType<B>( "b-uri.34", 3, 4, "B34", QString( "msg2" ) );
//
// const char * uri = "d-uri";
// int maj = 2, min = 3;
// const char * qmlName = "D1";
// const QString msg( "msg3" );
// qmlRegisterUncreatableType<D>( uri, maj, min, qmlName, msg );
// }
public void testQMLRegisterUncreatableType() throws Exception {
loadComment("qmlregistereduncreatabletype.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject b_qobj = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", b_qobj))
return;
assertNotNull(b_qobj);
Collection<IQmlRegistered> qmlRegistereds = qtIndex.getQmlRegistered();
assertNotNull(qmlRegistereds);
assertEquals(3, qmlRegistereds.size());
for(IQmlRegistered qmlRegistered : qmlRegistereds) {
IQObject qobj = qmlRegistered.getQObject();
assertNotNull(qobj);
// all values of B should be fully resolved, except for Revision, which was not provided
if (qobj.getName().equals("B")) {
assertNull(qmlRegistered.getVersion());
String qmlName = qmlRegistered.getQmlName();
assertNotNull(qmlName);
if ("B".equals(qmlName)) {
assertEquals(IQmlRegistered.Kind.Uncreatable, qmlRegistered.getKind());
assertEquals("b-uri", qmlRegistered.getURI());
assertEquals(Long.valueOf(1), qmlRegistered.getMajor());
assertEquals(Long.valueOf(2), qmlRegistered.getMinor());
assertEquals(null/*"msg1"*/, qmlRegistered.getReason());
} else if ("B34".equals(qmlName)) {
assertEquals(IQmlRegistered.Kind.Uncreatable, qmlRegistered.getKind());
assertEquals("b-uri.34", qmlRegistered.getURI());
assertEquals(Long.valueOf(3), qmlRegistered.getMajor());
assertEquals(Long.valueOf(4), qmlRegistered.getMinor());
assertEquals(null/*"msg2"*/, qmlRegistered.getReason());
} else {
fail("unexpected uri for B " + qmlName);
}
// the values for D are not expected to be resolved (yet), but it does have a Revision
} else if (qobj.getName().equals("D")) {
assertEquals(IQmlRegistered.Kind.Uncreatable, qmlRegistered.getKind());
assertNull(qmlRegistered.getVersion());
assertNull(qmlRegistered.getURI());
assertNull(qmlRegistered.getMajor());
assertNull(qmlRegistered.getMinor());
assertNull(qmlRegistered.getQmlName());
assertNull(qmlRegistered.getReason());
} else {
fail("unexpected qmlRegistered " + qobj.getName());
}
}
}
}

View file

@ -21,7 +21,6 @@ import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
@ -133,18 +132,6 @@ public class QObjectConnectCompletion {
}
}
private static boolean is_QObject_connect(ICEditorContentAssistInvocationContext context, IASTCompletionContext astContext, IASTName name) {
// Bug332201: Qt content assist should always be applied to the most specific part of
// the target name.
IBinding[] funcBindings = astContext.findBindings(name.getLastName(), !context.isContextInformationStyle());
for (IBinding funcBinding : funcBindings)
if (QtKeywords.is_QObject_connect(funcBinding))
return true;
return false;
}
// Copied from org.eclipse.cdt.internal.ui.text.CParameterListValidator
private static int indexOfClosingPeer(String code, char left, char right, int pos) {
int level = 0;