Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong semantic highlighting due to out-of-date AST being used. #2709

Merged
merged 1 commit into from Jul 20, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -18,10 +18,14 @@
import java.util.stream.Collectors;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.JobHelpers;
import org.eclipse.jdt.ls.core.internal.handlers.BaseDocumentLifeCycleHandler.DocumentMonitor;
import org.eclipse.jdt.ls.core.internal.semantictokens.SemanticTokensVisitor;
Expand All @@ -43,7 +47,7 @@ public static SemanticTokens full(IProgressMonitor monitor, SemanticTokensParams
JobHelpers.waitForJobs(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
documentMonitor.checkChanged();

CompilationUnit root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
CompilationUnit root = getAst(typeRoot, monitor);
documentMonitor.checkChanged();
if (root == null || monitor.isCanceled()) {
return new SemanticTokens(Collections.emptyList());
Expand All @@ -61,4 +65,33 @@ public static SemanticTokensLegend legend() {
);
}

/**
* Get the AST from CoreASTProvider. After getting the AST, it will check if the buffer size is equal to
* the AST's length. If it's not - indicating that the AST is out-of-date. The AST will be disposed and
* request CoreASTProvider to get a new one.
*
* <p>
* Such inconsistency will happen when a thread is calling getAST(), at the meantime, the
* document has been changed. Though the disposeAST() will be called when document change event
* comes, there is a chance when disposeAST() finishes before getAST(). In that case, an out-of-date
* AST will be cached and be used by other threads.
* </p>
*
* TODO: Consider to extract it to a utility and used by other handlers that need AST.
*/
private static CompilationUnit getAst(ITypeRoot typeRoot, IProgressMonitor monitor) {
CompilationUnit root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
IJavaElement element = root.getJavaElement();
if (element instanceof ICompilationUnit cu) {
try {
if (cu.getBuffer().getLength() != root.getLength()) {
CoreASTProvider.getInstance().disposeAST();
root = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.log(e);
}
}
return root;
}
}