Skip to content

Commit 3b8032a

Browse files
subyssurendran666SougandhS
authored andcommitted
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 d4763b2 commit 3b8032a

File tree

1 file changed

+266
-41
lines changed

1 file changed

+266
-41
lines changed

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

Lines changed: 266 additions & 41 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,49 +95,137 @@ 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);
80-
ast.accept(new ASTVisitor() {
81-
boolean meth = false;
82-
boolean found = false;
83-
@Override
84-
public boolean visit(MethodDeclaration node) {
85-
if (node.getName().getIdentifier().equals(currentMethod)) {
86-
List<Object> parameters = node.parameters();
87-
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;
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 iCompilationUnit) {
121+
parser.setSource(iCompilationUnit);
122+
} else if (javaElement instanceof IClassFile iClassFile) {
123+
parser.setSource(iClassFile);
124+
} else if (javaElement instanceof IType typeNew) {
125+
char[] source = typeNew.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+
140+
if (parser.createAST(null) instanceof CompilationUnit ast) {
141+
ast.accept(new ASTVisitor() {
142+
boolean meth = false;
143+
boolean found = false;
144+
boolean inTargetContext = false;
145+
@Override
146+
public boolean visit(MethodDeclaration node) {
147+
if (node.getName().getIdentifier().equals(currentMethod)) {
148+
List<Object> parameters = node.parameters();
149+
List<String> methodParams = parameters.stream().map(p -> ((SingleVariableDeclaration) p).getType().toString()).toList();
150+
int start = node.getStartPosition();
151+
int end = start + node.getLength();
152+
int startLine = ast.getLineNumber(start);
153+
int endLine = ast.getLineNumber(end);
154+
if (currentLine >= startLine && currentLine <= endLine) {
155+
inTargetContext = true;
156+
if (methodParams.equals(ref)) {
157+
meth = true;
158+
for (Object op : node.parameters()) {
159+
if (op instanceof SingleVariableDeclaration param) {
160+
if (param.getName().getIdentifier().equals(name)) {
161+
final ICompilationUnit finalCu = cu[0];
162+
highlightLine(ast, finalCu, param.getStartPosition());
163+
found = true;
164+
return false;
165+
}
166+
}
167+
}
168+
return true;
96169
}
97170
}
98-
return true;
99171
}
172+
return true;
100173
}
101-
return true;
102-
}
103174

104-
@Override
105-
public boolean visit(VariableDeclarationFragment node) {
106-
if (found) {
107-
return false;
175+
@Override
176+
public void endVisit(MethodDeclaration node) {
177+
inTargetContext = false;
178+
108179
}
109-
if (meth && node.getName().getIdentifier().equals(name)) {
110-
found = true;
111-
highlightLine(ast, cu, node.getStartPosition());
112-
return false;
180+
181+
@Override
182+
public boolean visit(VariableDeclarationFragment node) {
183+
if (found) {
184+
return false;
185+
}
186+
if ((meth || inTargetContext) && node.getName().getIdentifier().equals(name)) {
187+
found = true;
188+
final ICompilationUnit finalCu = cu[0];
189+
highlightLine(ast, finalCu, node.getStartPosition());
190+
return false;
191+
}
192+
return true;
113193
}
114-
return true;
115-
}
116-
});
194+
195+
@Override
196+
public boolean visit(LambdaExpression node) {
197+
if (found) {
198+
return false;
199+
}
200+
List<Object> parameters = node.parameters();
201+
int start = node.getStartPosition();
202+
int end = start + node.getLength();
203+
int startLine = ast.getLineNumber(start);
204+
int endLine = ast.getLineNumber(end);
205+
if (currentLine >= startLine && currentLine <= endLine) {
206+
inTargetContext = true;
207+
for (Object param : parameters) {
208+
if (param instanceof SingleVariableDeclaration) {
209+
if (param instanceof SingleVariableDeclaration svd) {
210+
if (svd.getName().getIdentifier().equals(name)) {
211+
highlightLine(ast, cu[0], svd.getStartPosition());
212+
found = true;
213+
return false;
214+
}
215+
}
216+
}
217+
}
218+
}
219+
return true;
220+
}
221+
222+
@Override
223+
public void endVisit(LambdaExpression node) {
224+
inTargetContext = false;
225+
}
226+
});
227+
228+
}
117229
}
118230
}
119231
}
@@ -135,15 +247,128 @@ public boolean visit(VariableDeclarationFragment node) {
135247
private void highlightLine(CompilationUnit ast, ICompilationUnit cu, int startPos) {
136248
int line = ast.getLineNumber(startPos);
137249
try {
138-
IEditorPart editor = JavaUI.openInEditor(cu);
250+
IEditorPart editor = null;
251+
if (cu != null && cu.exists()) {
252+
try {
253+
editor = JavaUI.openInEditor(cu);
254+
} catch (PartInitException e) { // We can ignore the PartInitException
255+
DebugUIPlugin.log(e);
256+
}
257+
}
258+
if (editor == null && iTypeGlobal != null && iTypeGlobal.getClassFile() != null) {
259+
try {
260+
editor = JavaUI.openInEditor(iTypeGlobal.getClassFile());
261+
} catch (PartInitException e) { // We can ignore the PartInitException
262+
DebugUIPlugin.log(e);
263+
}
264+
}
265+
if (editor == null) {
266+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
267+
if (window != null && window.getActivePage() != null) {
268+
editor = window.getActivePage().getActiveEditor();
269+
}
270+
}
139271
if (editor instanceof ITextEditor txtEd) {
140272
IDocumentProvider prov = txtEd.getDocumentProvider();
141273
IDocument doc = prov.getDocument(txtEd.getEditorInput());
142-
IRegion lineReg = doc.getLineInformation(line - 1);
274+
int adjustedLine = Math.max(0, line - 1);
275+
IRegion lineReg = doc.getLineInformation(adjustedLine);
143276
txtEd.selectAndReveal(lineReg.getOffset(), lineReg.getLength());
144277
}
145278
} catch (Exception e) {
146279
DebugUIPlugin.log(e);
147280
}
148281
}
149-
}
282+
283+
/**
284+
* Gets the Java element for the current stack frame
285+
*
286+
* @param frame
287+
* IJavaStackFrame of the element
288+
*/
289+
private IJavaElement getJavaElement(IJavaStackFrame frame) {
290+
try {
291+
String projectName = frame.getLaunch().getLaunchConfiguration().getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
292+
if (projectName != null) {
293+
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
294+
if (project.exists()) {
295+
IJavaProject javaProject = JavaCore.create(project);
296+
IType type = javaProject.findType(frame.getReceivingTypeName());
297+
if (type != null) {
298+
return type.getCompilationUnit();
299+
}
300+
}
301+
}
302+
IType globalType = findTypeInWorkspace(frame.getDeclaringTypeName());
303+
if (globalType != null) {
304+
return globalType;
305+
}
306+
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
307+
if (window != null && window.getActivePage() != null) {
308+
IEditorPart editor = window.getActivePage().getActiveEditor();
309+
if (editor != null) {
310+
return JavaUI.getEditorInputJavaElement(editor.getEditorInput());
311+
}
312+
}
313+
} catch (Exception e) {
314+
DebugUIPlugin.log(e);
315+
}
316+
return null;
317+
}
318+
319+
/**
320+
* Finds a type in the entire workspace
321+
*
322+
* @param fullyQualifiedName
323+
*/
324+
private IType findTypeInWorkspace(String fullyQualifiedName) {
325+
try {
326+
for (IJavaProject project : JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects()) {
327+
IType type = project.findType(fullyQualifiedName);
328+
if (type != null) {
329+
return type;
330+
}
331+
}
332+
} catch (JavaModelException e) {
333+
DebugUIPlugin.log(e);
334+
}
335+
return null;
336+
}
337+
338+
/**
339+
* Gets the Compilation unit for the JavaElement
340+
*
341+
* @param element
342+
* IJavaElement of the java element
343+
*/
344+
public ICompilationUnit getCompilationUnit(IJavaElement element) {
345+
if (element == null) {
346+
return null;
347+
}
348+
349+
if (element instanceof IClassFile classFile) {
350+
try {
351+
return classFile.getWorkingCopy(null, new NullProgressMonitor());
352+
} catch (JavaModelException e) {
353+
DebugUIPlugin.log(e);
354+
return null;
355+
}
356+
}
357+
IJavaElement ancestor = element.getAncestor(IJavaElement.COMPILATION_UNIT);
358+
if (ancestor instanceof ICompilationUnit iCompilationUnit) {
359+
return iCompilationUnit;
360+
}
361+
if (element instanceof IPackageFragment) {
362+
try {
363+
ICompilationUnit[] units = null;
364+
if (element instanceof IPackageFragment fragment) {
365+
units = fragment.getCompilationUnits();
366+
}
367+
return units.length > 0 ? units[0] : null;
368+
} catch (JavaModelException e) {
369+
DebugUIPlugin.log(e);
370+
}
371+
}
372+
return null;
373+
}
374+
}

0 commit comments

Comments
 (0)