Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 1289e87087b06a4701d6ba4a308c22d19335e5a7 (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
/*******************************************************************************
 * Copyright (c) 2006, 2015 Wind River Systems, Inc. and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Markus Schorn - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.callhierarchy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.model.ext.ICElementHandle;
import org.eclipse.cdt.internal.ui.viewsupport.IndexUI;
import org.eclipse.cdt.ui.extensions.ICallHierarchyProvider;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;

/**
 * Access to high level queries in the index.
 * @since 4.0
 */
public class CHQueries {
	private static final CHNode[] EMPTY_NODES = {};

	private CHQueries() {
	}

	/**
	 * Searches for functions and methods that call a given element.
	 */
	public static CHNode[] findCalledBy(CHContentProvider cp, CHNode node, IIndex index, IProgressMonitor pm)
			throws CoreException {
		CalledByResult result = new CalledByResult();
		ICElement callee = node.getRepresentedDeclaration();
		if (!(callee instanceof ISourceReference)) {
			return EMPTY_NODES;
		}
		boolean done = false;
		int linkageID = node.getLinkageID();
		if (linkageID == -1) {
			final ITranslationUnit tu = ((ISourceReference) callee).getTranslationUnit();
			if (tu == null)
				return EMPTY_NODES;

			final String ct = tu.getContentTypeId();
			if (ct.equals(CCorePlugin.CONTENT_TYPE_CXXHEADER) || ct.equals(CCorePlugin.CONTENT_TYPE_CHEADER)) {
				// Bug 260262: in a header file we need to consider C and C++.
				findCalledBy(callee, ILinkage.C_LINKAGE_ID, index, result);
				findCalledBy(callee, ILinkage.CPP_LINKAGE_ID, index, result);
				done = true;
			}
		}
		if (!done) {
			findCalledBy(callee, linkageID, index, result);
		}
		for (ICallHierarchyProvider provider : CHProviderManager.INSTANCE.getCallHierarchyProviders()) {
			provider.findCalledBy(callee, linkageID, index, result);
		}
		return cp.createNodes(node, result);
	}

	/**
	 * @return {@code true} if the element is owned by an external call hierarchy provider.
	 */
	public static boolean isExternal(ICElement element) {
		for (ICallHierarchyProvider provider : CHProviderManager.INSTANCE.getCallHierarchyProviders()) {
			if (provider.ownsElement(element))
				return true;
		}
		return false;
	}

	private static void findCalledBy(ICElement callee, int linkageID, IIndex index, CalledByResult result)
			throws CoreException {
		final ICProject project = callee.getCProject();
		IIndexBinding calleeBinding = IndexUI.elementToBinding(index, callee, linkageID);
		if (calleeBinding != null) {
			findCalledBy1(index, calleeBinding, true, project, result);
			if (calleeBinding instanceof ICPPMethod) {
				IBinding[] overriddenBindings = ClassTypeHelper.findOverridden((ICPPMethod) calleeBinding);
				for (IBinding overriddenBinding : overriddenBindings) {
					findCalledBy1(index, overriddenBinding, false, project, result);
				}
			}
		}
	}

	private static void findCalledBy1(IIndex index, IBinding callee, boolean includeOrdinaryCalls, ICProject project,
			CalledByResult result) throws CoreException {
		findCalledBy2(index, callee, includeOrdinaryCalls, project, result);
		List<? extends IBinding> specializations = IndexUI.findSpecializations(index, callee);
		for (IBinding spec : specializations) {
			findCalledBy2(index, spec, includeOrdinaryCalls, project, result);
		}
	}

	private static void findCalledBy2(IIndex index, IBinding callee, boolean includeOrdinaryCalls, ICProject project,
			CalledByResult result) throws CoreException {
		IIndexName[] names = index.findNames(callee, IIndex.FIND_REFERENCES | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES);
		for (IIndexName rname : names) {
			if (includeOrdinaryCalls || rname.couldBePolymorphicMethodCall()) {
				IIndexName caller = rname.getEnclosingDefinition();
				if (caller != null) {
					ICElement elem = IndexUI.getCElementForName(project, index, caller);
					if (elem != null) {
						result.add(elem, rname);
					}
				}
			}
		}
	}

	/**
	 * Searches for all calls that are made within a given range.
	 */
	public static CHNode[] findCalls(CHContentProvider cp, CHNode node, IIndex index, IProgressMonitor pm)
			throws CoreException {
		ICElement caller = node.getRepresentedDeclaration();
		CallsToResult result = new CallsToResult();
		IIndexName callerName = IndexUI.elementToName(index, caller);
		if (callerName != null) {
			IIndexName[] refs = callerName.getEnclosedNames();
			for (IIndexName name : refs) {
				IBinding binding = index.findBinding(name);
				if (CallHierarchyUI.isRelevantForCallHierarchy(binding)) {
					while (true) {
						ICElement[] defs = null;
						if (binding instanceof ICPPMethod && name.couldBePolymorphicMethodCall()) {
							defs = findOverriders(index, (ICPPMethod) binding);
						}
						if (defs == null) {
							defs = IndexUI.findRepresentative(index, binding);
						}
						if (defs != null && defs.length > 0) {
							result.add(defs, name);
						} else if (binding instanceof ICPPSpecialization) {
							binding = ((ICPPSpecialization) binding).getSpecializedBinding();
							if (binding != null)
								continue;
						}
						break;
					}
				}
			}
		}
		for (ICallHierarchyProvider provider : CHProviderManager.INSTANCE.getCallHierarchyProviders()) {
			provider.findCalls(caller, index, result);
		}
		return cp.createNodes(node, result);
	}

	/**
	 * Searches for overriders of method and converts them to ICElement, returns null,
	 * if there are none.
	 */
	static ICElement[] findOverriders(IIndex index, ICPPMethod binding) throws CoreException {
		IBinding[] virtualOverriders = ClassTypeHelper.findOverriders(index, binding);
		if (virtualOverriders.length > 0) {
			ArrayList<ICElementHandle> list = new ArrayList<ICElementHandle>();
			list.addAll(Arrays.asList(IndexUI.findRepresentative(index, binding)));
			for (IBinding overrider : virtualOverriders) {
				list.addAll(Arrays.asList(IndexUI.findRepresentative(index, overrider)));
			}
			return list.toArray(new ICElement[list.size()]);
		}
		return null;
	}
}

Back to the top