fix(JavaDoc): illegal character on escaped array types

GitOrigin-RevId: dd8098159715bd42510d807077d2679b365c546f
This commit is contained in:
Mathias Boulay
2024-08-27 17:29:56 +00:00
committed by intellij-monorepo-bot
parent afa40cc6b8
commit d69bdfe6a4
10 changed files with 77 additions and 15 deletions
@@ -0,0 +1,44 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.java.lexer;
import com.intellij.lexer.Lexer;
import com.intellij.lexer.MergeFunction;
import com.intellij.lexer.MergingLexerAdapterBase;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
/**
* Lexer to wrap around the Java Lexer when parsing Java types for the markdown JavaDoc (JEP-467)
* In reference links, array types are escaped: "char\[\]" which the JavaLexer doesn't like
* <p>
* It does this by <em>covering up</em> a <i>BAD_CHARACTER</i> token if followed by <i>[</i> or <i>]</i>
*/
public class JavaTypeEscapeLexer extends MergingLexerAdapterBase {
private final MergeFunction myMergeFunction = new EscapeMarkdownFunction();
public JavaTypeEscapeLexer(BasicJavaLexer original) {
super(original);
}
@Override
public MergeFunction getMergeFunction() {
return myMergeFunction;
}
private static class EscapeMarkdownFunction implements MergeFunction {
@Override
public IElementType merge(IElementType type, Lexer originalLexer) {
if(type != TokenType.BAD_CHARACTER) return type;
CharSequence tokenText = originalLexer.getTokenSequence();
if (tokenText.length() != 1 || (tokenText.charAt(0) != '[' && tokenText.charAt(0) != ']')) {
return type;
}
type = originalLexer.getTokenType();
originalLexer.advance();
return type;
}
}
}
@@ -193,7 +193,7 @@ public final class BasicJavaParserUtil {
final boolean eatAll,
final LanguageLevel level,
Function<LanguageLevel, JavaDocLexer> javaDocLexer,
Function<LanguageLevel, BasicJavaLexer> javaLexer) {
Function<LanguageLevel, ? extends Lexer> javaLexer) {
final PsiElement psi = chameleon.getTreeParent() != null ? chameleon.getTreeParent().getPsi() : chameleon.getPsi();
assert psi != null : chameleon;
final Project project = psi.getProject();
@@ -5,6 +5,7 @@ import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.java.lexer.BasicJavaLexer;
import com.intellij.lang.java.lexer.JavaDocLexer;
import com.intellij.lang.java.lexer.JavaTypeEscapeLexer;
import com.intellij.lang.java.parser.BasicJavaDocParser;
import com.intellij.lang.java.parser.BasicJavaParser;
import com.intellij.lang.java.parser.BasicJavaParserUtil;
@@ -125,11 +126,11 @@ public interface BasicJavaDocElementType {
private final @NotNull Supplier<? extends BasicJavaParser> myJavaThinParser;
private final Function<LanguageLevel, JavaDocLexer> javaDocLexer;
private final Function<LanguageLevel, BasicJavaLexer> javaLexer;
private final Function<LanguageLevel, JavaTypeEscapeLexer> javaLexer;
public DocTypeHolderElementType(@NotNull Supplier<? extends BasicJavaParser> parser,
@NotNull Function<LanguageLevel, JavaDocLexer> docLexerFunction,
@NotNull Function<LanguageLevel, BasicJavaLexer> javaLexer) {
@NotNull Function<LanguageLevel, JavaTypeEscapeLexer> javaLexer) {
super("DOC_TYPE_HOLDER", BASIC_DOC_TYPE_HOLDER);
this.myJavaThinParser = parser;
this.javaDocLexer = docLexerFunction;
@@ -251,10 +251,10 @@ public interface BasicJavaElementType {
final class JavaDummyElementType extends ILazyParseableElementType implements ICompositeElementType, ParentProviderElementType {
private static final Set<IElementType> PARENT_ELEMENT_TYPES = Collections.singleton(BASIC_DUMMY_ELEMENT);
private final Function<LanguageLevel, JavaDocLexer> javaDocLexer;
private final Function<LanguageLevel, BasicJavaLexer> javaLexer;
private final Function<LanguageLevel, ? extends Lexer> javaLexer;
public JavaDummyElementType(@NotNull Function<LanguageLevel, JavaDocLexer> lexer,
@NotNull Function<LanguageLevel, BasicJavaLexer> javaLexer) {
@NotNull Function<LanguageLevel, Lexer> javaLexer) {
super("DUMMY_ELEMENT", JavaLanguage.INSTANCE);
javaDocLexer = lexer;
this.javaLexer = javaLexer;
@@ -7,6 +7,7 @@ import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiParser;
import com.intellij.lang.java.lexer.JavaDocLexer;
import com.intellij.lang.java.lexer.JavaLexer;
import com.intellij.lang.java.lexer.JavaTypeEscapeLexer;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
@@ -37,6 +38,11 @@ public class JavaParserDefinition implements ParserDefinition {
return new JavaLexer(level);
}
/** @return A lexer which handles JEP-467 bracket escapes when parsing Java types */
public static @NotNull Lexer createLexerWithMarkdownEscape(@NotNull LanguageLevel level) {
return new JavaTypeEscapeLexer(new JavaLexer(level));
}
public static @NotNull Lexer createDocLexer(@NotNull LanguageLevel level) {
return new JavaDocLexer(level);
}
@@ -5,6 +5,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaParserDefinition;
import com.intellij.lang.java.lexer.BasicJavaLexer;
import com.intellij.lang.java.lexer.JavaDocLexer;
import com.intellij.lang.java.lexer.JavaTypeEscapeLexer;
import com.intellij.lang.java.parser.JavaParser;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.pom.java.LanguageLevel;
@@ -68,7 +69,7 @@ public interface JavaDocElementType {
ILazyParseableElementType DOC_TYPE_HOLDER = new BasicJavaDocElementType.DocTypeHolderElementType(
() -> JavaParser.INSTANCE,
(level) -> (JavaDocLexer)JavaParserDefinition.createDocLexer(level),
(level) -> (BasicJavaLexer)JavaParserDefinition.createLexer(level)
(level) -> (JavaTypeEscapeLexer)JavaParserDefinition.createLexerWithMarkdownEscape(level)
);
ILazyParseableElementType DOC_COMMENT = new BasicJavaDocElementType.DocCommentElementType(
@@ -275,6 +275,6 @@ public interface JavaElementType {
BASIC_TYPE_WITH_CONJUNCTIONS_TEXT);
IElementType DUMMY_ELEMENT = new JavaDummyElementType(level -> (JavaDocLexer)JavaParserDefinition.createDocLexer(level),
level -> (BasicJavaLexer)JavaParserDefinition.createLexer(level)
level -> JavaParserDefinition.createLexerWithMarkdownEscape(level)
);
}
@@ -1,2 +1,2 @@
/// [java.util.HashMap#toString()]
/// [java.lang.String#copyValueOf(char\[\])]
class C{}
@@ -9,25 +9,30 @@ PsiJavaFile:ReferenceLinkMarkdown11.java
PsiDocToken:DOC_LBRACKET('[')
PsiElement(DOC_METHOD_OR_FIELD_REF)
PsiElement(DOC_REFERENCE_HOLDER)
PsiJavaCodeReferenceElement:java.util.HashMap
PsiJavaCodeReferenceElement:java.util
PsiJavaCodeReferenceElement:java.lang.String
PsiJavaCodeReferenceElement:java.lang
PsiJavaCodeReferenceElement:java
PsiIdentifier:java('java')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:util('util')
PsiIdentifier:lang('lang')
PsiReferenceParameterList
<empty list>
PsiJavaToken:DOT('.')
PsiIdentifier:HashMap('HashMap')
PsiIdentifier:String('String')
PsiReferenceParameterList
<empty list>
PsiDocToken:DOC_TAG_VALUE_SHARP_TOKEN('#')
PsiDocToken:DOC_TAG_VALUE_TOKEN('toString')
PsiDocToken:DOC_TAG_VALUE_TOKEN('copyValueOf')
PsiDocToken:DOC_LPAREN('(')
PsiElement(DOC_TAG_VALUE_ELEMENT)
<empty list>
PsiElement(DOC_TYPE_HOLDER)
PsiTypeElement:char\[\]
PsiTypeElement:char
PsiKeyword:char('char')
PsiJavaToken:LBRACKET('\[')
PsiJavaToken:RBRACKET('\]')
PsiDocToken:DOC_RPAREN(')')
PsiDocToken:DOC_RBRACKET(']')
PsiWhiteSpace('\n')
@@ -27,7 +27,12 @@ java.FILE
DOC_TAG_VALUE_TOKEN
DOC_LPAREN
DOC_TAG_VALUE_ELEMENT
<empty list>
DOC_TYPE_HOLDER
TYPE
TYPE
CHAR_KEYWORD
LBRACKET
RBRACKET
DOC_RPAREN
DOC_RBRACKET
WHITE_SPACE