Skip to content

Commit 51cbfa4

Browse files
Fix NPE in Navigate to declaration + Support for lambda
Variable Navigation throwing NPE and support for variable navigation in lambda for non-java projects Fix: #712
1 parent d8fb6ab commit 51cbfa4

File tree

1 file changed

+264
-22
lines changed

1 file changed

+264
-22
lines changed

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/NavigateToVarDeclAction.java

Lines changed: 264 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,24 @@
1919

2020
import org.eclipse.core.resources.IProject;
2121
import org.eclipse.core.resources.ResourcesPlugin;
22+
import org.eclipse.core.runtime.NullProgressMonitor;
2223
import org.eclipse.debug.core.model.IStackFrame;
2324
import org.eclipse.debug.internal.ui.DebugUIPlugin;
2425
import org.eclipse.debug.ui.DebugUITools;
26+
import org.eclipse.jdt.core.IClassFile;
2527
import org.eclipse.jdt.core.ICompilationUnit;
28+
import org.eclipse.jdt.core.IJavaElement;
2629
import org.eclipse.jdt.core.IJavaProject;
30+
import org.eclipse.jdt.core.IOrdinaryClassFile;
31+
import org.eclipse.jdt.core.IPackageFragment;
2732
import org.eclipse.jdt.core.IType;
2833
import org.eclipse.jdt.core.JavaCore;
34+
import org.eclipse.jdt.core.JavaModelException;
2935
import org.eclipse.jdt.core.dom.AST;
3036
import org.eclipse.jdt.core.dom.ASTParser;
3137
import org.eclipse.jdt.core.dom.ASTVisitor;
3238
import org.eclipse.jdt.core.dom.CompilationUnit;
39+
import org.eclipse.jdt.core.dom.LambdaExpression;
3340
import org.eclipse.jdt.core.dom.MethodDeclaration;
3441
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
3542
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
@@ -42,10 +49,15 @@
4249
import org.eclipse.jface.text.IRegion;
4350
import org.eclipse.jface.viewers.IStructuredSelection;
4451
import org.eclipse.ui.IEditorPart;
52+
import org.eclipse.ui.IWorkbenchWindow;
53+
import org.eclipse.ui.PartInitException;
54+
import org.eclipse.ui.PlatformUI;
4555
import org.eclipse.ui.texteditor.IDocumentProvider;
4656
import org.eclipse.ui.texteditor.ITextEditor;
4757

4858
public class NavigateToVarDeclAction extends ObjectActionDelegate {
59+
60+
private IType iTypeGlobal;
4961
@Override
5062
public void run(IAction action) {
5163
IStructuredSelection selection = getCurrentSelection();
@@ -58,10 +70,22 @@ public void run(IAction action) {
5870
Object frame = DebugUITools.getDebugContext();
5971
if (frame instanceof IStackFrame jFrame) {
6072
if (jFrame instanceof IJavaStackFrame javaStackFrame) {
73+
IJavaProject iJavaProject = null;
6174
String type = javaStackFrame.getLaunch().getLaunchConfiguration().getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
62-
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(type);
63-
IJavaProject iJavaProject = JavaCore.create(project);
75+
if (type == null) {
76+
for (IJavaProject proj : JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects()) {
77+
IType type2 = proj.findType(javaStackFrame.getDeclaringTypeName());
78+
if (type2 != null && type2.exists()) {
79+
iJavaProject = proj;
80+
}
81+
}
82+
}
83+
if (iJavaProject == null && type != null) {
84+
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(type);
85+
iJavaProject = JavaCore.create(project);
86+
}
6487
IType iType = iJavaProject.findType(javaStackFrame.getReceivingTypeName());
88+
int currentLine = javaStackFrame.getLineNumber();
6589
String currentMethod = javaStackFrame.getMethodName();
6690
List<String> frameParams = javaStackFrame.getArgumentTypeNames();
6791
List<String> ref = frameParams.stream().map(e -> {
@@ -71,48 +95,130 @@ public void run(IAction action) {
7195
}
7296
return e;
7397
}).collect(Collectors.toList());
74-
ICompilationUnit cu = iType.getCompilationUnit();
75-
ASTParser parse = ASTParser.newParser(AST.getJLSLatest());
76-
parse.setSource(cu);
77-
parse.setKind(ASTParser.K_COMPILATION_UNIT);
78-
parse.setResolveBindings(true);
79-
CompilationUnit ast = (CompilationUnit) parse.createAST(null);
98+
final ICompilationUnit[] cu = { null };
99+
if (iType == null) {
100+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
101+
IEditorPart editor = window.getActivePage().getActiveEditor();
102+
IJavaElement element = JavaUI.getEditorInputJavaElement(editor.getEditorInput());
103+
cu[0] = (element instanceof ICompilationUnit cuElement) ? cuElement : null;
104+
} else {
105+
cu[0] = iType.getCompilationUnit();
106+
}
107+
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
108+
if (cu[0] == null && iType != null) {
109+
IOrdinaryClassFile classFile = iType.getClassFile();
110+
if (classFile != null && classFile.getSource() != null) {
111+
String source = classFile.getSource();
112+
parser.setSource(source.toCharArray());
113+
parser.setKind(ASTParser.K_COMPILATION_UNIT);
114+
iTypeGlobal = iType;
115+
}
116+
} else if (cu[0] == null && iType == null) {
117+
final IJavaElement javaElement = getJavaElement(javaStackFrame);
118+
cu[0] = getCompilationUnit(javaElement);
119+
if (javaElement != null) {
120+
if (javaElement instanceof ICompilationUnit) {
121+
parser.setSource((ICompilationUnit) javaElement);
122+
} else if (javaElement instanceof IClassFile) {
123+
parser.setSource((IClassFile) javaElement);
124+
} else {
125+
String source = getSourceContent(javaElement);
126+
if (source != null) {
127+
parser.setSource(source.toCharArray());
128+
} else {
129+
return; // No source
130+
}
131+
}
132+
parser.setResolveBindings(true);
133+
}
134+
} else {
135+
parser.setSource(cu[0]);
136+
parser.setKind(ASTParser.K_COMPILATION_UNIT);
137+
parser.setResolveBindings(true);
138+
}
139+
CompilationUnit ast = (CompilationUnit) parser.createAST(null);
80140
ast.accept(new ASTVisitor() {
81141
boolean meth = false;
82142
boolean found = false;
143+
boolean inTargetContext = false;
83144
@Override
84145
public boolean visit(MethodDeclaration node) {
85146
if (node.getName().getIdentifier().equals(currentMethod)) {
86147
List<Object> parameters = node.parameters();
87148
List<String> methodParams = parameters.stream().map(p -> ((SingleVariableDeclaration) p).getType().toString()).toList();
88-
if (methodParams.equals(ref)) {
89-
meth = true;
90-
for (Object op : node.parameters()) {
91-
SingleVariableDeclaration parm = (SingleVariableDeclaration) op;
92-
if (parm.getName().getIdentifier().equals(name)) {
93-
highlightLine(ast, cu, node.getStartPosition());
94-
found = true;
95-
return false;
149+
int start = node.getStartPosition();
150+
int end = start + node.getLength();
151+
int startLine = ast.getLineNumber(start);
152+
int endLine = ast.getLineNumber(end);
153+
if (currentLine >= startLine && currentLine <= endLine) {
154+
inTargetContext = true;
155+
if (methodParams.equals(ref)) {
156+
meth = true;
157+
for (Object op : node.parameters()) {
158+
SingleVariableDeclaration parm = (SingleVariableDeclaration) op;
159+
if (parm.getName().getIdentifier().equals(name)) {
160+
final ICompilationUnit finalCu = cu[0];
161+
highlightLine(ast, finalCu, parm.getStartPosition());
162+
found = true;
163+
return false;
164+
}
96165
}
166+
return true;
97167
}
98-
return true;
99168
}
100169
}
101170
return true;
102171
}
103172

173+
@Override
174+
public void endVisit(MethodDeclaration node) {
175+
inTargetContext = false;
176+
}
177+
104178
@Override
105179
public boolean visit(VariableDeclarationFragment node) {
106180
if (found) {
107181
return false;
108182
}
109-
if (meth && node.getName().getIdentifier().equals(name)) {
183+
if ((meth || inTargetContext) && node.getName().getIdentifier().equals(name)) {
110184
found = true;
111-
highlightLine(ast, cu, node.getStartPosition());
185+
final ICompilationUnit finalCu = cu[0];
186+
highlightLine(ast, finalCu, node.getStartPosition());
112187
return false;
113188
}
114189
return true;
115190
}
191+
192+
@Override
193+
public boolean visit(LambdaExpression node) {
194+
if (found) {
195+
return false;
196+
}
197+
List<Object> parameters = node.parameters();
198+
int start = node.getStartPosition();
199+
int end = start + node.getLength();
200+
int startLine = ast.getLineNumber(start);
201+
int endLine = ast.getLineNumber(end);
202+
if (currentLine >= startLine && currentLine <= endLine) {
203+
inTargetContext = true;
204+
for (Object param : parameters) {
205+
if (param instanceof SingleVariableDeclaration) {
206+
SingleVariableDeclaration svd = (SingleVariableDeclaration) param;
207+
if (svd.getName().getIdentifier().equals(name)) {
208+
highlightLine(ast, cu[0], svd.getStartPosition());
209+
found = true;
210+
return false;
211+
}
212+
}
213+
}
214+
}
215+
return true;
216+
}
217+
218+
@Override
219+
public void endVisit(LambdaExpression node) {
220+
inTargetContext = false;
221+
}
116222
});
117223
}
118224
}
@@ -135,15 +241,151 @@ public boolean visit(VariableDeclarationFragment node) {
135241
private void highlightLine(CompilationUnit ast, ICompilationUnit cu, int startPos) {
136242
int line = ast.getLineNumber(startPos);
137243
try {
138-
IEditorPart editor = JavaUI.openInEditor(cu);
244+
IEditorPart editor = null;
245+
if (cu != null && cu.exists()) {
246+
try {
247+
editor = JavaUI.openInEditor(cu);
248+
} catch (PartInitException e) { // We can ignore the PartInitException
249+
DebugUIPlugin.log(e);
250+
}
251+
}
252+
if (editor == null && iTypeGlobal != null && iTypeGlobal.getClassFile() != null) {
253+
try {
254+
editor = JavaUI.openInEditor(iTypeGlobal.getClassFile());
255+
} catch (PartInitException e) { // We can ignore the PartInitException
256+
DebugUIPlugin.log(e);
257+
}
258+
}
259+
if (editor == null) {
260+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
261+
if (window != null && window.getActivePage() != null) {
262+
editor = window.getActivePage().getActiveEditor();
263+
}
264+
}
139265
if (editor instanceof ITextEditor txtEd) {
140266
IDocumentProvider prov = txtEd.getDocumentProvider();
141267
IDocument doc = prov.getDocument(txtEd.getEditorInput());
142-
IRegion lineReg = doc.getLineInformation(line - 1);
268+
int adjustedLine = Math.max(0, line - 1);
269+
IRegion lineReg = doc.getLineInformation(adjustedLine);
143270
txtEd.selectAndReveal(lineReg.getOffset(), lineReg.getLength());
144271
}
145272
} catch (Exception e) {
146273
DebugUIPlugin.log(e);
147274
}
148275
}
149-
}
276+
277+
/**
278+
* Gets the Java element for the current stack frame
279+
*
280+
* @param frame
281+
* IJavaStackFrame of the element
282+
*/
283+
private IJavaElement getJavaElement(IJavaStackFrame frame) {
284+
try {
285+
String projectName = frame.getLaunch().getLaunchConfiguration().getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
286+
if (projectName != null) {
287+
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
288+
if (project.exists()) {
289+
IJavaProject javaProject = JavaCore.create(project);
290+
IType type = javaProject.findType(frame.getReceivingTypeName());
291+
if (type != null) {
292+
return type.getCompilationUnit();
293+
}
294+
}
295+
}
296+
IType globalType = findTypeInWorkspace(frame.getDeclaringTypeName());
297+
if (globalType != null) {
298+
return globalType;
299+
}
300+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
301+
if (window != null && window.getActivePage() != null) {
302+
IEditorPart editor = window.getActivePage().getActiveEditor();
303+
if (editor != null) {
304+
return JavaUI.getEditorInputJavaElement(editor.getEditorInput());
305+
}
306+
}
307+
} catch (Exception e) {
308+
DebugUIPlugin.log(e);
309+
}
310+
return null;
311+
}
312+
313+
/**
314+
* Finds a type in the entire workspace
315+
*
316+
* @param fullyQualifiedName
317+
*/
318+
private IType findTypeInWorkspace(String fullyQualifiedName) {
319+
try {
320+
for (IJavaProject project : JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects()) {
321+
IType type = project.findType(fullyQualifiedName);
322+
if (type != null) {
323+
return type;
324+
}
325+
}
326+
} catch (JavaModelException e) {
327+
DebugUIPlugin.log(e);
328+
}
329+
return null;
330+
}
331+
332+
/**
333+
* Gets source content for a Java element
334+
*
335+
* @param element
336+
* IJavaElement of the element
337+
*/
338+
private String getSourceContent(IJavaElement element) {
339+
try {
340+
if (element instanceof ICompilationUnit) {
341+
return ((ICompilationUnit) element).getSource();
342+
} else if (element instanceof IClassFile) {
343+
return ((IClassFile) element).getSource();
344+
} else if (element instanceof IType) {
345+
return ((IType) element).getSource();
346+
}
347+
} catch (JavaModelException e) {
348+
DebugUIPlugin.log(e);
349+
}
350+
return null;
351+
}
352+
353+
/**
354+
* Gets the Compilation unit for the JavaElement
355+
*
356+
* @param element
357+
* IJavaElement of the java element
358+
*/
359+
public ICompilationUnit getCompilationUnit(IJavaElement element) {
360+
if (element == null) {
361+
return null;
362+
}
363+
if (element instanceof ICompilationUnit) {
364+
return (ICompilationUnit) element;
365+
}
366+
if (element instanceof IType) {
367+
return ((IType) element).getCompilationUnit();
368+
}
369+
if (element instanceof IClassFile) {
370+
try {
371+
return ((IClassFile) element).getWorkingCopy(null, new NullProgressMonitor());
372+
} catch (JavaModelException e) {
373+
DebugUIPlugin.log(e);
374+
return null;
375+
}
376+
}
377+
IJavaElement ancestor = element.getAncestor(IJavaElement.COMPILATION_UNIT);
378+
if (ancestor instanceof ICompilationUnit) {
379+
return (ICompilationUnit) ancestor;
380+
}
381+
if (element instanceof IPackageFragment) {
382+
try {
383+
ICompilationUnit[] units = ((IPackageFragment) element).getCompilationUnits();
384+
return units.length > 0 ? units[0] : null;
385+
} catch (JavaModelException e) {
386+
DebugUIPlugin.log(e);
387+
}
388+
}
389+
return null;
390+
}
391+
}

0 commit comments

Comments
 (0)