Skip to content

Commit 99259bd

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 99259bd

1 file changed

Lines changed: 243 additions & 22 deletions

File tree

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

Lines changed: 243 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,132 @@ 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 if (javaElement instanceof IType) {
125+
char[] source = ((IType) javaElement).getSource().toCharArray();
126+
if (source != null) {
127+
parser.setSource(source);
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+
if (op instanceof SingleVariableDeclaration param) {
159+
if (param.getName().getIdentifier().equals(name)) {
160+
final ICompilationUnit finalCu = cu[0];
161+
highlightLine(ast, finalCu, param.getStartPosition());
162+
found = true;
163+
return false;
164+
}
165+
}
96166
}
167+
return true;
97168
}
98-
return true;
99169
}
100170
}
101171
return true;
102172
}
103173

174+
@Override
175+
public void endVisit(MethodDeclaration node) {
176+
inTargetContext = false;
177+
}
178+
104179
@Override
105180
public boolean visit(VariableDeclarationFragment node) {
106181
if (found) {
107182
return false;
108183
}
109-
if (meth && node.getName().getIdentifier().equals(name)) {
184+
if ((meth || inTargetContext) && node.getName().getIdentifier().equals(name)) {
110185
found = true;
111-
highlightLine(ast, cu, node.getStartPosition());
186+
final ICompilationUnit finalCu = cu[0];
187+
highlightLine(ast, finalCu, node.getStartPosition());
188+
return false;
189+
}
190+
return true;
191+
}
192+
193+
@Override
194+
public boolean visit(LambdaExpression node) {
195+
if (found) {
112196
return false;
113197
}
198+
List<Object> parameters = node.parameters();
199+
int start = node.getStartPosition();
200+
int end = start + node.getLength();
201+
int startLine = ast.getLineNumber(start);
202+
int endLine = ast.getLineNumber(end);
203+
if (currentLine >= startLine && currentLine <= endLine) {
204+
inTargetContext = true;
205+
for (Object param : parameters) {
206+
if (param instanceof SingleVariableDeclaration) {
207+
if (param instanceof SingleVariableDeclaration svd) {
208+
if (svd.getName().getIdentifier().equals(name)) {
209+
highlightLine(ast, cu[0], svd.getStartPosition());
210+
found = true;
211+
return false;
212+
}
213+
}
214+
}
215+
}
216+
}
114217
return true;
115218
}
219+
220+
@Override
221+
public void endVisit(LambdaExpression node) {
222+
inTargetContext = false;
223+
}
116224
});
117225
}
118226
}
@@ -135,15 +243,128 @@ public boolean visit(VariableDeclarationFragment node) {
135243
private void highlightLine(CompilationUnit ast, ICompilationUnit cu, int startPos) {
136244
int line = ast.getLineNumber(startPos);
137245
try {
138-
IEditorPart editor = JavaUI.openInEditor(cu);
246+
IEditorPart editor = null;
247+
if (cu != null && cu.exists()) {
248+
try {
249+
editor = JavaUI.openInEditor(cu);
250+
} catch (PartInitException e) { // We can ignore the PartInitException
251+
DebugUIPlugin.log(e);
252+
}
253+
}
254+
if (editor == null && iTypeGlobal != null && iTypeGlobal.getClassFile() != null) {
255+
try {
256+
editor = JavaUI.openInEditor(iTypeGlobal.getClassFile());
257+
} catch (PartInitException e) { // We can ignore the PartInitException
258+
DebugUIPlugin.log(e);
259+
}
260+
}
261+
if (editor == null) {
262+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
263+
if (window != null && window.getActivePage() != null) {
264+
editor = window.getActivePage().getActiveEditor();
265+
}
266+
}
139267
if (editor instanceof ITextEditor txtEd) {
140268
IDocumentProvider prov = txtEd.getDocumentProvider();
141269
IDocument doc = prov.getDocument(txtEd.getEditorInput());
142-
IRegion lineReg = doc.getLineInformation(line - 1);
270+
int adjustedLine = Math.max(0, line - 1);
271+
IRegion lineReg = doc.getLineInformation(adjustedLine);
143272
txtEd.selectAndReveal(lineReg.getOffset(), lineReg.getLength());
144273
}
145274
} catch (Exception e) {
146275
DebugUIPlugin.log(e);
147276
}
148277
}
149-
}
278+
279+
/**
280+
* Gets the Java element for the current stack frame
281+
*
282+
* @param frame
283+
* IJavaStackFrame of the element
284+
*/
285+
private IJavaElement getJavaElement(IJavaStackFrame frame) {
286+
try {
287+
String projectName = frame.getLaunch().getLaunchConfiguration().getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
288+
if (projectName != null) {
289+
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
290+
if (project.exists()) {
291+
IJavaProject javaProject = JavaCore.create(project);
292+
IType type = javaProject.findType(frame.getReceivingTypeName());
293+
if (type != null) {
294+
return type.getCompilationUnit();
295+
}
296+
}
297+
}
298+
IType globalType = findTypeInWorkspace(frame.getDeclaringTypeName());
299+
if (globalType != null) {
300+
return globalType;
301+
}
302+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
303+
if (window != null && window.getActivePage() != null) {
304+
IEditorPart editor = window.getActivePage().getActiveEditor();
305+
if (editor != null) {
306+
return JavaUI.getEditorInputJavaElement(editor.getEditorInput());
307+
}
308+
}
309+
} catch (Exception e) {
310+
DebugUIPlugin.log(e);
311+
}
312+
return null;
313+
}
314+
315+
/**
316+
* Finds a type in the entire workspace
317+
*
318+
* @param fullyQualifiedName
319+
*/
320+
private IType findTypeInWorkspace(String fullyQualifiedName) {
321+
try {
322+
for (IJavaProject project : JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects()) {
323+
IType type = project.findType(fullyQualifiedName);
324+
if (type != null) {
325+
return type;
326+
}
327+
}
328+
} catch (JavaModelException e) {
329+
DebugUIPlugin.log(e);
330+
}
331+
return null;
332+
}
333+
334+
/**
335+
* Gets the Compilation unit for the JavaElement
336+
*
337+
* @param element
338+
* IJavaElement of the java element
339+
*/
340+
public ICompilationUnit getCompilationUnit(IJavaElement element) {
341+
if (element == null) {
342+
return null;
343+
}
344+
345+
if (element instanceof IClassFile classFile) {
346+
try {
347+
return classFile.getWorkingCopy(null, new NullProgressMonitor());
348+
} catch (JavaModelException e) {
349+
DebugUIPlugin.log(e);
350+
return null;
351+
}
352+
}
353+
IJavaElement ancestor = element.getAncestor(IJavaElement.COMPILATION_UNIT);
354+
if (ancestor instanceof ICompilationUnit iCompilationUnit) {
355+
return iCompilationUnit;
356+
}
357+
if (element instanceof IPackageFragment) {
358+
try {
359+
ICompilationUnit[] units = null;
360+
if (element instanceof IPackageFragment fragment) {
361+
units = fragment.getCompilationUnits();
362+
}
363+
return units.length > 0 ? units[0] : null;
364+
} catch (JavaModelException e) {
365+
DebugUIPlugin.log(e);
366+
}
367+
}
368+
return null;
369+
}
370+
}

0 commit comments

Comments
 (0)