mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[ssr] Fix Java SSR after the javadoc lexer changes
GitOrigin-RevId: 87fa31142e1cf0acd9d8c2f3c738fe481cbda2fe
This commit is contained in:
committed by
intellij-monorepo-bot
parent
3b41193c33
commit
a6981a4688
+34
-20
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.structuralsearch.impl.matcher;
|
||||
|
||||
import com.intellij.dupLocator.iterators.ArrayBackedNodeIterator;
|
||||
@@ -9,6 +9,8 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -18,7 +20,6 @@ import com.intellij.structuralsearch.StructuralSearchUtil;
|
||||
import com.intellij.structuralsearch.impl.matcher.handlers.LiteralWithSubstitutionHandler;
|
||||
import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler;
|
||||
import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler;
|
||||
import com.intellij.structuralsearch.impl.matcher.iterators.DocValuesIterator;
|
||||
import com.intellij.structuralsearch.impl.matcher.iterators.HierarchyNodeIterator;
|
||||
import com.intellij.structuralsearch.impl.matcher.predicates.MatchPredicate;
|
||||
import com.intellij.structuralsearch.impl.matcher.predicates.NotPredicate;
|
||||
@@ -192,34 +193,37 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
|
||||
|
||||
if (!isTypedVar && !myMatchingVisitor.setResult(tag.getName().equals(other.getName()))) return;
|
||||
|
||||
PsiElement psiDocTagValue = tag.getValueElement();
|
||||
boolean isTypedValue = false;
|
||||
PsiElement psiDocTagValue = Arrays.stream(tag.getDataElements()).findFirst().orElse(null);
|
||||
|
||||
if (psiDocTagValue != null) {
|
||||
final PsiElement[] children = psiDocTagValue.getChildren();
|
||||
if (children.length == 1) {
|
||||
psiDocTagValue = children[0];
|
||||
}
|
||||
isTypedValue = pattern.isTypedVar(psiDocTagValue);
|
||||
|
||||
if (isTypedValue) {
|
||||
if (other.getValueElement() != null) {
|
||||
if (!myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(psiDocTagValue, other.getValueElement()))) return;
|
||||
}
|
||||
else {
|
||||
if (!myMatchingVisitor.setResult(myMatchingVisitor.allowsAbsenceOfMatch(psiDocTagValue))) return;
|
||||
if (psiDocTagValue instanceof PsiDocTagValue) {
|
||||
final PsiElement[] children = psiDocTagValue.getChildren();
|
||||
if (children.length == 1) {
|
||||
psiDocTagValue = children[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTypedValue && !myMatchingVisitor.setResult(myMatchingVisitor.matchInAnyOrder(new DocValuesIterator(tag.getFirstChild()),
|
||||
new DocValuesIterator(other.getFirstChild())))) {
|
||||
return;
|
||||
if (pattern.isTypedVar(psiDocTagValue) && other.getDataElements().length == 0) {
|
||||
myMatchingVisitor.setResult(myMatchingVisitor.allowsAbsenceOfMatch(psiDocTagValue));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTypedVar) {
|
||||
myMatchingVisitor.setResult(myMatchingVisitor.handleTypedElement(tag.getNameElement(), other.getNameElement()));
|
||||
}
|
||||
|
||||
myMatchingVisitor.setResult(myMatchingVisitor.matchOptionally(tag.getDataElements(), other.getDataElements()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDocTagValue(@NotNull PsiDocTagValue value) {
|
||||
myMatchingVisitor.setResult(substituteOrMatchText(value, myMatchingVisitor.getMatchContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDocToken(@NotNull PsiDocToken token) {
|
||||
myMatchingVisitor.setResult(substituteOrMatchText(token, myMatchingVisitor.getMatchContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -680,6 +684,16 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
|
||||
: element;
|
||||
}
|
||||
|
||||
private boolean substituteOrMatchText(@NotNull PsiElement element, @NotNull MatchContext context) {
|
||||
final var handler = context.getPattern().getHandler(element);
|
||||
final PsiElement other = myMatchingVisitor.getElement();
|
||||
if (handler instanceof SubstitutionHandler substitutionHandler) {
|
||||
return substitutionHandler.handle(other, context);
|
||||
} else {
|
||||
return myMatchingVisitor.matchText(element, other);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getArrayDimensions(PsiElement element) {
|
||||
if (element == null) {
|
||||
return 0;
|
||||
|
||||
+6
-7
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.structuralsearch.impl.matcher.compiler;
|
||||
|
||||
import com.intellij.dupLocator.iterators.NodeIterator;
|
||||
import com.intellij.java.syntax.parser.JavaKeywords;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -10,6 +9,7 @@ import com.intellij.openapi.util.text.Strings;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.javadoc.PsiDocTag;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.intellij.psi.search.PsiShortNamesCache;
|
||||
@@ -24,7 +24,6 @@ import com.intellij.structuralsearch.impl.matcher.JavaCompiledPattern;
|
||||
import com.intellij.structuralsearch.impl.matcher.JavaMatchUtil;
|
||||
import com.intellij.structuralsearch.impl.matcher.filters.*;
|
||||
import com.intellij.structuralsearch.impl.matcher.handlers.*;
|
||||
import com.intellij.structuralsearch.impl.matcher.iterators.DocValuesIterator;
|
||||
import com.intellij.structuralsearch.impl.matcher.predicates.ExprTypePredicate;
|
||||
import com.intellij.structuralsearch.impl.matcher.predicates.RegExpPredicate;
|
||||
import com.intellij.util.SmartList;
|
||||
@@ -197,12 +196,12 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
|
||||
@Override
|
||||
public void visitDocTag(@NotNull PsiDocTag psiDocTag) {
|
||||
super.visitDocTag(psiDocTag);
|
||||
}
|
||||
|
||||
final NodeIterator nodes = new DocValuesIterator(psiDocTag.getFirstChild());
|
||||
while (nodes.hasNext()) {
|
||||
myCompilingVisitor.setHandler(nodes.current(), new DocDataHandler());
|
||||
nodes.advance();
|
||||
}
|
||||
@Override
|
||||
public void visitDocTagValue(@NotNull PsiDocTagValue value) {
|
||||
super.visitDocTagValue(value);
|
||||
myCompilingVisitor.setFilterSimple(value, JavaDocTagDataFilter.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.structuralsearch.impl.matcher.filters;
|
||||
|
||||
import com.intellij.dupLocator.util.NodeFilter;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
|
||||
public final class JavaDocTagDataFilter implements NodeFilter {
|
||||
|
||||
private static final NodeFilter INSTANCE = new JavaDocTagDataFilter();
|
||||
|
||||
private JavaDocTagDataFilter() {}
|
||||
|
||||
@Override
|
||||
public boolean accepts(PsiElement element) {
|
||||
return element instanceof PsiDocTagValue || element instanceof PsiDocToken;
|
||||
}
|
||||
|
||||
public static NodeFilter getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
// 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.structuralsearch.impl.matcher.handlers;
|
||||
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
import com.intellij.structuralsearch.impl.matcher.MatchContext;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Handler for doc nodes
|
||||
*/
|
||||
public class DocDataHandler extends MatchingHandler {
|
||||
private static final @NonNls String P_STR = "^\\s*((?:\\w|_|-|\\$)+)\\s*(?:=\\s*\"(.*)\"\\s*)?$";
|
||||
private static final Pattern p = Pattern.compile(
|
||||
P_STR,
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
@Override
|
||||
public boolean match(PsiElement node, PsiElement match, @NotNull MatchContext context) {
|
||||
String text1 = node.getText();
|
||||
|
||||
text1 = getTextFromNode(node, text1);
|
||||
|
||||
Matcher m1 = p.matcher(text1);
|
||||
|
||||
String text2 = match.getText();
|
||||
text2 = getTextFromNode(match, text2);
|
||||
|
||||
Matcher m2 = p.matcher(text2);
|
||||
|
||||
if (m1.matches() && m2.matches()) {
|
||||
String name = m1.group(1);
|
||||
String name2 = m2.group(1);
|
||||
boolean isTypedName = context.getPattern().isTypedVar(name);
|
||||
|
||||
if (name.equals(name2) || isTypedName) {
|
||||
String value = m1.group(2);
|
||||
String value2 = m2.group(2);
|
||||
|
||||
if (value!=null) {
|
||||
if (value2 == null || !value2.matches(value)) return false;
|
||||
}
|
||||
if (isTypedName) {
|
||||
SubstitutionHandler handler = (SubstitutionHandler) context.getPattern().getHandler(name);
|
||||
return handler.handle(match,context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return text1.equals(text2);
|
||||
}
|
||||
|
||||
// since doctag value may be inside doc comment we specially build text including skipped nodes
|
||||
private static String getTextFromNode(final PsiElement node, String text1) {
|
||||
PsiElement nextSibling = node.getNextSibling();
|
||||
if (nextSibling instanceof PsiDocTagValue) {
|
||||
text1 += nextSibling.getText();
|
||||
|
||||
nextSibling = nextSibling.getNextSibling();
|
||||
if (PsiDocToken.isDocToken(nextSibling, JavaDocTokenType.DOC_COMMENT_DATA)) {
|
||||
text1 += nextSibling.getText();
|
||||
}
|
||||
}
|
||||
return text1;
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.structuralsearch.impl.matcher.iterators;
|
||||
|
||||
import com.intellij.dupLocator.iterators.NodeIterator;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.javadoc.PsiDocTagValue;
|
||||
import com.intellij.psi.javadoc.PsiDocToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Iterates over java doc values tag
|
||||
*/
|
||||
public class DocValuesIterator extends NodeIterator {
|
||||
private int index;
|
||||
private final ArrayList<PsiElement> tokens = new ArrayList<>(2);
|
||||
|
||||
public DocValuesIterator(PsiElement start) {
|
||||
for (PsiElement e = start; e != null; e = e.getNextSibling()) {
|
||||
if (e instanceof PsiDocTagValue) tokens.add(e);
|
||||
else if (PsiDocToken.isDocToken(e, JavaDocTokenType.DOC_COMMENT_DATA)) {
|
||||
tokens.add(e);
|
||||
e = advanceToNext(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// since doctag value may be inside doc comment we specially skip that nodes from list
|
||||
static PsiElement advanceToNext(PsiElement e) {
|
||||
PsiElement nextSibling = e.getNextSibling();
|
||||
if (nextSibling instanceof PsiDocTagValue) e = nextSibling;
|
||||
|
||||
nextSibling = e.getNextSibling();
|
||||
if (PsiDocToken.isDocToken(nextSibling, JavaDocTokenType.DOC_COMMENT_DATA)) {
|
||||
e = nextSibling;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index >=0 && index < tokens.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement current() {
|
||||
return hasNext() ? tokens.get(index) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance() {
|
||||
if (index < tokens.size()) ++ index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewind() {
|
||||
if (index >= 0) --index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
+1
-66
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.structuralsearch;
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
@@ -1286,71 +1286,6 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
|
||||
assertEquals("optional tag value match", 6, findMatchesCount(s57, "/** @'T '_T2? */"));
|
||||
assertEquals("no infinite loop on javadoc matching", 1, findMatchesCount(s57, "/** 'Text */ class '_ { }"));
|
||||
|
||||
final String s83 = """
|
||||
/**
|
||||
* @hibernate.class
|
||||
* table="CATS"
|
||||
*/
|
||||
public class Cat {
|
||||
private Long id; // identifier
|
||||
private Date birthdate;
|
||||
/**
|
||||
* @hibernate.id
|
||||
* generator-class="native"
|
||||
* column="CAT_ID"
|
||||
*/
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
private void setId(Long id) {
|
||||
this.id=id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hibernate.property
|
||||
* column="BIRTH_DATE"
|
||||
*/
|
||||
public Date getBirthdate() {
|
||||
return birthdate;
|
||||
}
|
||||
void setBirthdate(Date date) {
|
||||
birthdate = date;
|
||||
}
|
||||
/**
|
||||
* @hibernate.property
|
||||
* column="SEX"
|
||||
* not-null="true"
|
||||
* update="false"
|
||||
*/
|
||||
public char getSex() {
|
||||
return sex;
|
||||
}
|
||||
void setSex(char sex) {
|
||||
this.sex=sex;
|
||||
}
|
||||
}""";
|
||||
assertEquals("XDoclet metadata", 2, findMatchesCount(s83, """
|
||||
/**
|
||||
* @hibernate.property
|
||||
* 'Property
|
||||
*/
|
||||
"""));
|
||||
assertEquals("XDoclet metadata 2", 1, findMatchesCount(s83, """
|
||||
/**
|
||||
* @hibernate.property
|
||||
* update="fa.se"
|
||||
*/
|
||||
"""));
|
||||
|
||||
final String s75 = """
|
||||
/** @class aClass
|
||||
@author the author */ class A {}
|
||||
/** */ class B {}
|
||||
/** @class aClass */ class C {}""";
|
||||
assertEquals("multiple tags match +", 2, findMatchesCount(s75, " /** @'_tag+ '_value+ */"));
|
||||
assertEquals("multiple tags match *", 3, findMatchesCount(s75, " /** @'_tag* '_value* */"));
|
||||
assertEquals("multiple tags match ?", 3, findMatchesCount(s75, " /** @'_tag? '_value? */ class 't {}"));
|
||||
|
||||
final String source = """
|
||||
class outer {
|
||||
/** bla */
|
||||
|
||||
Reference in New Issue
Block a user