diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java index df450cd06f7..49d9ba58ef2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java @@ -35,11 +35,11 @@ public interface KDocTokens { ILazyParseableElementType KDOC = new ILazyParseableElementType("KDoc", JetLanguage.INSTANCE) { @Override public ASTNode parseContents(ASTNode chameleon) { - PsiElement parentElement = chameleon.getTreeParent().getPsi(); - Project project = parentElement.getProject(); - PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, new KDocLexer(), getLanguage(), - chameleon.getText()); - PsiParser parser = new KDocParser(); + PsiElement parentElement = chameleon.getTreeParent().getPsi(); + Project project = parentElement.getProject(); + PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, new KDocLexer(), getLanguage(), + chameleon.getText()); + PsiParser parser = new KDocParser(); return parser.parse(this, builder).getFirstChildNode(); } @@ -67,11 +67,11 @@ public interface KDocTokens { ILazyParseableElementType MARKDOWN_LINK = new ILazyParseableElementType("KDOC_MARKDOWN_LINK", JetLanguage.INSTANCE) { @Override public ASTNode parseContents(ASTNode chameleon) { - PsiElement parentElement = chameleon.getTreeParent().getPsi(); - Project project = parentElement.getProject(); - PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, new JetLexer(), getLanguage(), - chameleon.getText()); - PsiParser parser = new KDocLinkParser(); + PsiElement parentElement = chameleon.getTreeParent().getPsi(); + Project project = parentElement.getProject(); + PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, new JetLexer(), getLanguage(), + chameleon.getText()); + PsiParser parser = new KDocLinkParser(); return parser.parse(this, builder).getFirstChildNode(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocLinkParser.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocLinkParser.kt index 35c151b0aad..ee10333d226 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocLinkParser.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/parser/KDocLinkParser.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.lexer.JetTokens */ class KDocLinkParser(): PsiParser { override fun parse(root: IElementType, builder: PsiBuilder): ASTNode { - builder.setDebugMode(true) val rootMarker = builder.mark() if (builder.getTokenType() == JetTokens.LBRACKET) { builder.advanceLexer() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java index b18c57c2a06..79a1bfa3139 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java @@ -17,11 +17,15 @@ package org.jetbrains.kotlin.kdoc.psi.api; import com.intellij.psi.PsiComment; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection; import org.jetbrains.kotlin.psi.JetDeclaration; // Don't implement JetElement (or it will be treated as statement) public interface KDoc extends PsiComment { + @Nullable JetDeclaration getOwner(); + @NotNull KDocSection getDefaultSection(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.kt index 1d55fe3be3f..1d3e93226fb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.kt @@ -35,7 +35,7 @@ public class KDocImpl(buffer: CharSequence?) : LazyParseablePsiElement(KDocToken override fun getTokenType(): IElementType = JetTokens.DOC_COMMENT - override fun getOwner(): JetDeclaration = getParentOfType(true)!! + override fun getOwner(): JetDeclaration? = getParentOfType(true) override fun getDefaultSection(): KDocSection = getChildOfType()!! } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocLink.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocLink.kt index 27dc011ca4d..f936c51275c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocLink.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocLink.kt @@ -30,7 +30,7 @@ public class KDocLink(node: ASTNode) : JetElementImpl(node) { public fun getLinkTextRange(): TextRange { val text = getText() if (text.startsWith('[') && text.endsWith(']')) { - return TextRange(1, text.length()-1) + return TextRange(1, text.length() - 1) } return TextRange(0, text.length()) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocName.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocName.kt index 7493c54b8a9..eeeac3d2219 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocName.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocName.kt @@ -38,6 +38,10 @@ public class KDocName(node: ASTNode): JetElementImpl(node) { public fun getQualifier(): KDocName? = getChildOfType() + /** + * Returns the range within the element containing the name (in other words, + * the range of the element excluding the qualifier and dot, if present). + */ public fun getNameTextRange(): TextRange { val dot = getNode().findChildByType(JetTokens.DOT) val textRange = getTextRange() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.kt b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.kt index 996b9b4a119..5d3b897f9b5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.kt @@ -54,9 +54,13 @@ public open class KDocTag(node: ASTNode) : KDocElementImpl(node) { return null } + public val knownTag: KDocKnownTag? + get() { + val name = getName() + return if (name != null) KDocKnownTag.findByTagName(name) else null + } + private fun hasSubject(contentChildren: List): Boolean { - val name = getName() - val knownTag = if (name != null) KDocKnownTag.findByTagName(name) else null if (knownTag?.isReferenceRequired() ?: false) { return contentChildren.firstOrNull()?.getElementType() == KDocElementTypes.KDOC_LINK; } @@ -69,7 +73,7 @@ public open class KDocTag(node: ASTNode) : KDocElementImpl(node) { .dropWhile { it.getElementType() == TokenType.WHITE_SPACE } /** - * Returns the contents of this tag (all text following the tag name and the subject if present, + * Returns the content of this tag (all text following the tag name and the subject if present, * with leading asterisks removed). */ public fun getContent(): String { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorUtil.java index bffba11979b..82b1ed6c62a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorUtil.java @@ -78,7 +78,7 @@ public class FunctionDescriptorUtil { public static JetScope getFunctionInnerScope( @NotNull JetScope outerScope, @NotNull FunctionDescriptor descriptor, - RedeclarationHandler redeclarationHandler + @NotNull RedeclarationHandler redeclarationHandler ) { WritableScope parameterScope = new WritableScopeImpl(outerScope, descriptor, redeclarationHandler, "Function inner scope"); ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/JetScopeUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/JetScopeUtils.java index a44dbfc96cf..e63b3f5a6cc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/JetScopeUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/scopes/JetScopeUtils.java @@ -70,7 +70,7 @@ public final class JetScopeUtils { public static JetScope getPropertyDeclarationInnerScope( @NotNull PropertyDescriptor propertyDescriptor, @NotNull JetScope outerScope, - RedeclarationHandler redeclarationHandler + @NotNull RedeclarationHandler redeclarationHandler ) { return getPropertyDeclarationInnerScope(propertyDescriptor, outerScope, @@ -119,7 +119,7 @@ public final class JetScopeUtils { @NotNull JetScope outerScope, @NotNull List typeParameters, @Nullable ReceiverParameterDescriptor receiver, - RedeclarationHandler redeclarationHandler, + @NotNull RedeclarationHandler redeclarationHandler, boolean addLabelForProperty ) { WritableScopeImpl result = new WritableScopeImpl( diff --git a/compiler/testData/psi/kdoc/MarkdownLinkWithError.kt b/compiler/testData/psi/kdoc/MarkdownLinkWithError.kt new file mode 100644 index 00000000000..3a5d2d06b0b --- /dev/null +++ b/compiler/testData/psi/kdoc/MarkdownLinkWithError.kt @@ -0,0 +1,6 @@ +/** + * [A.B.C...] + * [....] + * [A..B..C] + * [A.] + */ diff --git a/compiler/testData/psi/kdoc/MarkdownLinkWithError.txt b/compiler/testData/psi/kdoc/MarkdownLinkWithError.txt new file mode 100644 index 00000000000..d4814179f69 --- /dev/null +++ b/compiler/testData/psi/kdoc/MarkdownLinkWithError.txt @@ -0,0 +1,57 @@ +JetFile: MarkdownLinkWithError.kt + PACKAGE_DIRECTIVE + + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + KDOC_SECTION + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' ') + KDOC_LINK + KDOC_MARKDOWN_LINK + PsiElement(LBRACKET)('[') + KDOC_NAME + KDOC_NAME + KDOC_NAME + PsiElement(IDENTIFIER)('A') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('B') + PsiElement(DOT)('.') + PsiElement(IDENTIFIER)('C') + PsiErrorElement:Closing bracket expected + + PsiElement(RANGE)('..') + PsiElement(DOT)('.') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' [....]') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' ') + KDOC_LINK + KDOC_MARKDOWN_LINK + PsiElement(LBRACKET)('[') + KDOC_NAME + PsiElement(IDENTIFIER)('A') + PsiErrorElement:Closing bracket expected + + PsiElement(RANGE)('..') + PsiElement(IDENTIFIER)('B') + PsiElement(RANGE)('..') + PsiElement(IDENTIFIER)('C') + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' ') + KDOC_LINK + KDOC_MARKDOWN_LINK + PsiElement(LBRACKET)('[') + KDOC_NAME + PsiElement(IDENTIFIER)('A') + PsiElement(DOT)('.') + PsiErrorElement:Identifier expected + + PsiElement(RBRACKET)(']') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java index 7f458996a41..e56b810ec69 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java @@ -1085,6 +1085,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { doParsingTest(fileName); } + @TestMetadata("MarkdownLinkWithError.kt") + public void testMarkdownLinkWithError() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/kdoc/MarkdownLinkWithError.kt"); + doParsingTest(fileName); + } + @TestMetadata("MarkdownQualifiedName.kt") public void testMarkdownQualifiedName() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/kdoc/MarkdownQualifiedName.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/kdoc/KDocReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/kdoc/KDocReference.kt index c71bb560277..5b29fa98ae8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/kdoc/KDocReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/kdoc/KDocReference.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag public class KDocReference(element: KDocName): JetMultiReference(element) { override fun getTargetDescriptors(context: BindingContext): Collection { @@ -54,7 +55,7 @@ public class KDocReference(element: KDocName): JetMultiReference(eleme if (declaration == null) { return arrayListOf() } - val declarationDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) + val declarationDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] val kdocLink = getElement().getStrictParentOfType()!! return resolveKDocLink(session, declarationDescriptor, kdocLink.getTagIfSubject(), getElement().getQualifiedName()) } @@ -75,7 +76,7 @@ public fun resolveKDocLink(session: KotlinCodeAnalyzer, fromDescriptor: DeclarationDescriptor, fromSubjectOfTag: KDocTag?, qualifiedName: List): Collection { - if (fromSubjectOfTag?.getName() == "param") { + if (fromSubjectOfTag?.knownTag == KDocKnownTag.PARAM) { return resolveParamLink(fromDescriptor, qualifiedName) } @@ -90,19 +91,20 @@ public fun resolveKDocLink(session: KotlinCodeAnalyzer, } private fun resolveParamLink(fromDescriptor: DeclarationDescriptor, qualifiedName: List): List { - if (qualifiedName.size() != 1) { - return listOf() - } - if (fromDescriptor is CallableDescriptor) { - val valueParams = fromDescriptor.getValueParameters().filter { it.getName().asString() == qualifiedName[0] } - if (valueParams.isNotEmpty()) { - return valueParams - } - } - if (fromDescriptor is ClassifierDescriptor) { - val typeParams = fromDescriptor.getTypeConstructor().getParameters().filter { it.getName().asString() == qualifiedName[0] } - if (typeParams.isNotEmpty()) { - return typeParams + // TODO resolve parameters of functions passed as parameters + val name = qualifiedName.singleOrNull() ?: return listOf() + when (fromDescriptor) { + is CallableDescriptor -> + return fromDescriptor.getValueParameters().filter { it.getName().asString() == name } + is ClassifierDescriptor -> { + val typeParams = fromDescriptor.getTypeConstructor().getParameters().filter { it.getName().asString() == name } + if (typeParams.isNotEmpty()) { + return typeParams + } + if (fromDescriptor is ClassDescriptor) { + return resolveParamLink(fromDescriptor.getUnsubstitutedPrimaryConstructor(), qualifiedName) + } + return listOf() } } @@ -126,7 +128,6 @@ private fun getClassInnerScope(outerScope: JetScope, descriptor: ClassDescriptor for (constructor in descriptor.getConstructors()) { headerScope.addFunctionDescriptor(constructor) } - headerScope.addLabeledDeclaration(descriptor) headerScope.changeLockLevel(WritableScope.LockLevel.READING) val classScope = ChainedScope(descriptor, "Class ${descriptor.getName()} scope", descriptor.getDefaultType().getMemberScope(), headerScope) @@ -134,30 +135,30 @@ private fun getClassInnerScope(outerScope: JetScope, descriptor: ClassDescriptor } private fun getResolutionScope(session: KotlinCodeAnalyzer, descriptor: DeclarationDescriptor): JetScope { - when (descriptor) { + return when (descriptor) { is PackageFragmentDescriptor -> - return getPackageInnerScope(descriptor) + getPackageInnerScope(descriptor) is PackageViewDescriptor -> - return descriptor.getMemberScope() + descriptor.getMemberScope() is ClassDescriptor -> - return getClassInnerScope(getOuterScope(descriptor, session), descriptor) + getClassInnerScope(getOuterScope(descriptor, session), descriptor) is FunctionDescriptor -> - return FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, session), - descriptor, RedeclarationHandler.DO_NOTHING) + FunctionDescriptorUtil.getFunctionInnerScope(getOuterScope(descriptor, session), + descriptor, RedeclarationHandler.DO_NOTHING) is PropertyDescriptor -> - return JetScopeUtils.getPropertyDeclarationInnerScope(descriptor, - getOuterScope(descriptor, session), - RedeclarationHandler.DO_NOTHING) + JetScopeUtils.getPropertyDeclarationInnerScope(descriptor, + getOuterScope(descriptor, session), + RedeclarationHandler.DO_NOTHING) + + is DeclarationDescriptorNonRoot -> + getOuterScope(descriptor, session) + + else -> throw IllegalArgumentException("Cannot find resolution scope for root $descriptor") } - - if (descriptor is DeclarationDescriptorNonRoot) - return getOuterScope(descriptor, session) - - throw IllegalArgumentException("Cannot find resolution scope for root $descriptor") } private fun getOuterScope(descriptor: DeclarationDescriptorWithSource, session: KotlinCodeAnalyzer): JetScope { diff --git a/idea/testData/kdoc/resolve/ClassSelfReference.kt b/idea/testData/kdoc/resolve/ClassSelfReference.kt new file mode 100644 index 00000000000..01befa1fb97 --- /dev/null +++ b/idea/testData/kdoc/resolve/ClassSelfReference.kt @@ -0,0 +1,7 @@ +/** + * This is [Foo] + */ +class Foo { +} + +// REF: ().Foo diff --git a/idea/testData/kdoc/resolve/ConstructorParamReference.kt b/idea/testData/kdoc/resolve/ConstructorParamReference.kt new file mode 100644 index 00000000000..528912ec6d0 --- /dev/null +++ b/idea/testData/kdoc/resolve/ConstructorParamReference.kt @@ -0,0 +1,7 @@ +/** + * @param i this is an integer + */ +class C(i: Int) { +} + +// REF: i diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java index 984f0f144b3..4b2a4043a42 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocResolveTestGenerated.java @@ -17,9 +17,10 @@ package org.jetbrains.kotlin.idea.kdoc; import com.intellij.testFramework.TestDataPath; -import org.jetbrains.kotlin.idea.resolve.AbstractReferenceResolveTest; +import org.jetbrains.kotlin.test.InnerTestClasses; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.JetTestUtils; +import org.jetbrains.kotlin.idea.resolve.AbstractReferenceResolveTest; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -36,12 +37,24 @@ public class KdocResolveTestGenerated extends AbstractReferenceResolveTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/kdoc/resolve"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("ClassSelfReference.kt") + public void testClassSelfReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ClassSelfReference.kt"); + doTest(fileName); + } + @TestMetadata("CodeReference.kt") public void testCodeReference() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/resolve/CodeReference.kt"); doTest(fileName); } + @TestMetadata("ConstructorParamReference.kt") + public void testConstructorParamReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ConstructorParamReference.kt"); + doTest(fileName); + } + @TestMetadata("ImportedClassReference.kt") public void testImportedClassReference() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/kdoc/resolve/ImportedClassReference.kt");