Skip to main content
summaryrefslogtreecommitdiffstats
blob: 92e9829b44535be11aa6ae3b4e360c0d6197cbcf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
 * Copyright (c) 2013, 2015 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;

import java.util.Collection;
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;
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.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
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.ICPPClassScope;
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;
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.ICPPEvaluationOwner;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.qt.core.index.IQMethod;
import org.eclipse.cdt.internal.qt.core.index.IQObject;
import org.eclipse.core.resources.IProject;

@SuppressWarnings("restriction")
public class ASTUtil {

	/**
	 * A convenience method to find the project that contains the given node.  Returns null if
	 * the project cannot be found.
	 */
	public static IProject getProject(IASTNode node) {
		IASTTranslationUnit astTU = node.getTranslationUnit();
		if (astTU == null)
			return null;

		ITranslationUnit tu = astTU.getOriginatingTranslationUnit();
		if (tu == null)
			return null;

		ICProject cProject = tu.getCProject();
		if (cProject == null)
			return null;

		return cProject.getProject();
	}

	/**
	 * Return the fully qualified name of the binding for the given name.  Returns null
	 * if the name has no binding.  Tries to resolve the binding if needed.
	 */
	public static String getFullyQualifiedName(IASTName name) {
		return getFullyQualifiedName(name.resolveBinding());
	}

	/**
	 * Return the fully qualified name of the given binding.  Returns null if there
	 * is no binding.
	 */
	public static String getFullyQualifiedName(IBinding binding) {
		if (binding == null)
			return null;
		if (binding instanceof ICPPBinding)
			try {
				return getFullyQualifiedName(((ICPPBinding) binding).getQualifiedName());
			} catch(DOMException e) {
				Activator.log(e);
				return null;
			}

		String ownerName = getFullyQualifiedName(binding.getOwner());
		return (ownerName == null ? "" : ownerName) + "::" + binding.getName();
	}

	/**
	 * Create and return a string representation of the fully qualified name in the
	 * input array's elements.
	 */
	public static String getFullyQualifiedName(String[] qualName) {
		boolean first = true;
		StringBuilder str = new StringBuilder();
		for(String name : qualName) {
			if (first)
				first = false;
			else
				str.append("::");
			str.append(name);
		}
		return str.toString();
	}

	// NOTE: This expression allows embedded line terminators (?s) for cases where the code looks like:
	// QObject::connect( &a, SIGNAL(
	//					sig1(
	//						int
	//					), ...
	// The regex trims leading and trailing whitespace within the expansion parameter.  This is needed
	// so that the start of the capture group provides the proper offset into the expansion.
	public static final Pattern Regex_MacroExpansion = Pattern.compile("(?s)([_a-zA-Z]\\w*)\\s*\\(\\s*(.*?)\\s*\\)\\s*");

	public static IType getBaseType(IType type) {
		while (type instanceof ITypeContainer)
			type = ((ITypeContainer) type).getType();
		return type;
	}

	public static IType getBaseType(IASTNode node) {
		if (node instanceof IASTIdExpression)
			return getBaseType((IASTIdExpression) node);
		if (node instanceof IASTFunctionCallExpression)
			return getBaseType((IASTFunctionCallExpression) node);
		if (node instanceof IASTExpression)
			return getBaseType(((IASTExpression) node).getExpressionType());

		return null;
	}

	public static IType getBaseType(IASTInitializerClause init) {
		if (!(init instanceof ICPPASTInitializerClause))
			return null;

		ICPPASTInitializerClause cppInit = (ICPPASTInitializerClause) init;
		ICPPEvaluation eval = ((ICPPEvaluationOwner)cppInit).getEvaluation();
		return eval == null ? null : getBaseType(eval.getType(cppInit));
	}

	public static ICPPClassType getReceiverType(IASTFunctionCallExpression fncall) {
		// If the expression is calling a member function then find the type of the
		// receiver.
		IASTExpression fnName = fncall.getFunctionNameExpression();
		if (fnName instanceof ICPPASTFieldReference) {
			ICPPASTFieldReference fieldRef = (ICPPASTFieldReference) fnName;
			ICPPASTExpression receiver = fieldRef.getFieldOwner();

			IType recvType = getBaseType(receiver);
			if (recvType instanceof ICPPClassType)
				return (ICPPClassType) recvType;
		}

		// Otherwise check for a call to implicit 'this'.  See details in the thread that
		// starts at http://dev.eclipse.org/mhonarc/lists/cdt-dev/msg26972.html
		try {
			for(IScope scope = CPPVisitor.getContainingScope(fncall); scope != null; scope = scope.getParent())
				if (scope instanceof ICPPClassScope)
					return ((ICPPClassScope) scope).getClassType();
		} catch (DOMException e) {
			Activator.log(e);
		}

		return null;
	}

	/**
	 * Does not return null.
	 */
	public static Collection<IQMethod> findMethods(IQObject qobj, QtMethodReference ref) {
		Set<IQMethod> bindings = new LinkedHashSet<IQMethod>();

		Iterable<IQMethod> methods = null;
		switch(ref.getType())
		{
		case Signal:
			methods = qobj.getSignals().withoutOverrides();
			break;
		case Slot:
			methods = qobj.getSlots().withoutOverrides();
			break;
		}

		if (methods != null) {
			String qtNormalizedSig = QtMethodUtil.getQtNormalizedMethodSignature(ref.getRawSignature());
			if (qtNormalizedSig == null)
				return bindings;

			for (IQMethod method : methods)
				for(String signature : method.getSignatures())
					if (signature.equals(qtNormalizedSig))
						bindings.add(method);
		}
		return bindings;
	}

	public static <T extends IBinding> T resolveFunctionBinding(Class<T> cls, IASTFunctionCallExpression fnCall) {
		IASTName fnName = null;
		IASTExpression fnNameExpr = fnCall.getFunctionNameExpression();
		if (fnNameExpr instanceof IASTIdExpression)
			fnName = ((IASTIdExpression) fnNameExpr).getName();
		else if (fnNameExpr instanceof ICPPASTFieldReference)
			fnName = ((ICPPASTFieldReference) fnNameExpr).getFieldName();

		IBinding binding = fnName == null ? null : fnName.resolveBinding();
		if (binding == null)
			return null;

		return cls.isAssignableFrom(binding.getClass()) ? cls.cast(binding) : null;
	}

	public static ICPPASTVisibilityLabel findVisibilityLabel(ICPPMethod method, IASTNode ast) {
		// the visibility cannot be found without an ast
		if (ast == null)
			return null;

		// We need to get the CompTypeSpec in order to see the token that created the method's
		// visibility specifier.  The ast parameter will be either the method definition or a
		// declaration.  If it happens to be a declaration, then the CompTypeSpec is a parent of
		// the AST and it can be accessed through public API.  However, if the ast parameter happens
		// to be a definition, then there isn't any public API (that I've found) to get to the
		// CompTypeSpec.  Instead, we cheat and use the InternalBinding.

		MethodSpec methodSpec = new MethodSpec(ast);
		if (methodSpec.clsSpec == null
		 && method instanceof ICPPInternalBinding)
		{
			ICPPInternalBinding internalBinding = (ICPPInternalBinding) method;
			IASTNode[] decls = internalBinding.getDeclarations();
			for (int i = 0; methodSpec.clsSpec == null && i < decls.length; ++i)
				methodSpec = new MethodSpec(decls[i]);
		}

		if(methodSpec.clsSpec == null)
			return null;

		ICPPASTVisibilityLabel lastLabel = null;
		for (IASTDeclaration decl : methodSpec.clsSpec.getMembers()) {
			if (decl instanceof ICPPASTVisibilityLabel)
				lastLabel = (ICPPASTVisibilityLabel) decl;
			else if (decl == methodSpec.methodDecl)
				return lastLabel;
		}

		return null;
	}

	private static class MethodSpec
	{
		public final ICPPASTCompositeTypeSpecifier clsSpec;
		public final IASTNode methodDecl;

		public MethodSpec( IASTNode node )
		{
			ICPPASTCompositeTypeSpecifier cls = null;
			IASTNode mth = node;
			while( mth != null && cls == null )
			{
				IASTNode parent = mth.getParent();
				if (parent instanceof ICPPASTCompositeTypeSpecifier)
					cls = (ICPPASTCompositeTypeSpecifier) parent;
				else
					mth = parent;
			}

			clsSpec = cls;
			methodDecl = mth;
		}
	}
}

Back to the top