Skip to content

Commit 63805fd

Browse files
authored
Fix wrong semantic highlighting due to out-of-date AST being used. (eclipse-jdtls#2709)
- There are chances that the AST used for semantic highlighting is out-of-date, which leads to wrong highlighting. This fix simply compare the length of the document with the length of the AST to check if the AST is out-of-date. In most of the case, checking the length is enough. - For root cause analysis, see: eclipse-jdt/eclipse.jdt.core#1151 Signed-off-by: Sheng Chen <[email protected]>
1 parent c22b229 commit 63805fd

File tree

1 file changed

+34
-1
lines changed

1 file changed

+34
-1
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/SemanticTokensHandler.java

+34-1
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818
import java.util.stream.Collectors;
1919

2020
import org.eclipse.core.runtime.IProgressMonitor;
21+
import org.eclipse.jdt.core.ICompilationUnit;
22+
import org.eclipse.jdt.core.IJavaElement;
2123
import org.eclipse.jdt.core.ITypeRoot;
24+
import org.eclipse.jdt.core.JavaModelException;
2225
import org.eclipse.jdt.core.dom.CompilationUnit;
2326
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
2427
import org.eclipse.jdt.ls.core.internal.JDTUtils;
28+
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
2529
import org.eclipse.jdt.ls.core.internal.JobHelpers;
2630
import org.eclipse.jdt.ls.core.internal.handlers.BaseDocumentLifeCycleHandler.DocumentMonitor;
2731
import org.eclipse.jdt.ls.core.internal.semantictokens.SemanticTokensVisitor;
@@ -43,7 +47,7 @@ public static SemanticTokens full(IProgressMonitor monitor, SemanticTokensParams
4347
JobHelpers.waitForJobs(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
4448
documentMonitor.checkChanged();
4549

46-
CompilationUnit root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
50+
CompilationUnit root = getAst(typeRoot, monitor);
4751
documentMonitor.checkChanged();
4852
if (root == null || monitor.isCanceled()) {
4953
return new SemanticTokens(Collections.emptyList());
@@ -61,4 +65,33 @@ public static SemanticTokensLegend legend() {
6165
);
6266
}
6367

68+
/**
69+
* Get the AST from CoreASTProvider. After getting the AST, it will check if the buffer size is equal to
70+
* the AST's length. If it's not - indicating that the AST is out-of-date. The AST will be disposed and
71+
* request CoreASTProvider to get a new one.
72+
*
73+
* <p>
74+
* Such inconsistency will happen when a thread is calling getAST(), at the meantime, the
75+
* document has been changed. Though the disposeAST() will be called when document change event
76+
* comes, there is a chance when disposeAST() finishes before getAST(). In that case, an out-of-date
77+
* AST will be cached and be used by other threads.
78+
* </p>
79+
*
80+
* TODO: Consider to extract it to a utility and used by other handlers that need AST.
81+
*/
82+
private static CompilationUnit getAst(ITypeRoot typeRoot, IProgressMonitor monitor) {
83+
CompilationUnit root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
84+
IJavaElement element = root.getJavaElement();
85+
if (element instanceof ICompilationUnit cu) {
86+
try {
87+
if (cu.getBuffer().getLength() != root.getLength()) {
88+
CoreASTProvider.getInstance().disposeAST();
89+
root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
90+
}
91+
} catch (JavaModelException e) {
92+
JavaLanguageServerPlugin.log(e);
93+
}
94+
}
95+
return root;
96+
}
6497
}

0 commit comments

Comments
 (0)