From 238c3cddbdf98599f24cb5f5b8926dca30f43f7c Mon Sep 17 00:00:00 2001 From: Zoroark Date: Sat, 24 Apr 2021 00:00:54 +0200 Subject: [PATCH] Add DeprecatedBlockTag rule (#3660) * Add DeprecatedBlockTag rule * Refactor DeprecatedBlockTagSpec tests to remove Spek complexity * Fix incorrect deprecation test snippets on property set/get * Add link to official docs in DeprecatedBlockTag * Extract KDocStyle class out of EndOfSentenceFormat.kt * Fix EndOfSentenceFormat not visiting declarations on its own * Add DeprecatedBlockTag to KDocStyle * Fix code made useless by DeprecatedBlockTag move --- .../main/resources/default-detekt-config.yml | 2 + .../rules/documentation/DeprecatedBlockTag.kt | 70 +++++++++ .../documentation/EndOfSentenceFormat.kt | 20 +-- .../detekt/rules/documentation/KDocStyle.kt | 22 +++ .../documentation/DeprecatedBlockTagSpec.kt | 144 ++++++++++++++++++ .../documentation/EndOfSentenceFormatSpec.kt | 2 +- 6 files changed, 244 insertions(+), 16 deletions(-) create mode 100644 detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTag.kt create mode 100644 detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/KDocStyle.kt create mode 100644 detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTagSpec.kt diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index edc26e6d6..c1376e944 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -57,6 +57,8 @@ comments: active: false CommentOverPrivateProperty: active: false + DeprecatedBlockTag: + active: false EndOfSentenceFormat: active: false endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTag.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTag.kt new file mode 100644 index 000000000..29e2bc80a --- /dev/null +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTag.kt @@ -0,0 +1,70 @@ +package io.gitlab.arturbosch.detekt.rules.documentation + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import org.jetbrains.kotlin.psi.KtDeclaration + +/** + * This rule reports use of the `@deprecated` block tag in KDoc comments. Deprecation must be specified using a + * `@Deprecated` annotation as adding a `@deprecated` block tag in KDoc comments + * [has no effect and is not supported](https://kotlinlang.org/docs/kotlin-doc.html#suppress). The `@Deprecated` + * annotation constructor has dedicated fields for a message and a type (warning, error, etc.). You can also use the + * `@ReplaceWith` annotation to specify how to solve the deprecation automatically via the IDE. + * + * + * /** + * * This function prints a message followed by a new line. + * * + * * @deprecated Useless, the Kotlin standard library can already do this. Replace with println. + * */ + * fun printThenNewline(what: String) { + * // ... + * } + * + * + * + * /** + * * This function prints a message followed by a new line. + * */ + * @Deprecated("Useless, the Kotlin standard library can already do this.") + * @ReplaceWith("println(what)") + * fun printThenNewline(what: String) { + * // ... + * } + * + */ +class DeprecatedBlockTag(config: Config = Config.empty) : Rule(config) { + override val issue = Issue( + "DeprecatedBlockTag", + Severity.Defect, + "Do not use the @deprecated block tag, which is not supported by KDoc. " + + "Use the @Deprecated annotation instead.", + Debt.FIVE_MINS + ) + + override fun visitDeclaration(dcl: KtDeclaration) { + super.visitDeclaration(dcl) + verify(dcl) + } + + fun verify(dcl: KtDeclaration) { + dcl.docComment?.getAllSections()?.forEach { section -> + section.findTagsByName("deprecated").forEach { tag -> + report( + CodeSmell( + issue, + Entity.from(dcl), + "@deprecated tag block does not properly report deprecation in Kotlin, use @Deprecated " + + "annotation instead", + references = listOf(Entity.from(tag)) + ) + ) + } + } + } +} diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormat.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormat.kt index 1591859d7..f95e66c1e 100644 --- a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormat.kt +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormat.kt @@ -5,26 +5,11 @@ import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Debt import io.gitlab.arturbosch.detekt.api.Entity import io.gitlab.arturbosch.detekt.api.Issue -import io.gitlab.arturbosch.detekt.api.MultiRule import io.gitlab.arturbosch.detekt.api.Rule import io.gitlab.arturbosch.detekt.api.Severity import io.gitlab.arturbosch.detekt.rules.lastArgumentMatchesUrl import org.jetbrains.kotlin.psi.KtDeclaration -class KDocStyle(config: Config = Config.empty) : MultiRule() { - - private val endOfSentenceFormat = EndOfSentenceFormat(config) - - override val rules = listOf( - endOfSentenceFormat - ) - - override fun visitDeclaration(dcl: KtDeclaration) { - super.visitDeclaration(dcl) - endOfSentenceFormat.verify(dcl) - } -} - /** * This rule validates the end of the first sentence of a KDoc comment. * It should end with proper punctuation or with a correct URL. @@ -46,6 +31,11 @@ class EndOfSentenceFormat(config: Config = Config.empty) : Rule(config) { Regex(valueOrDefault(END_OF_SENTENCE_FORMAT, "([.?!][ \\t\\n\\r\\f<])|([.?!:]\$)")) private val htmlTag = Regex("<.+>") + override fun visitDeclaration(dcl: KtDeclaration) { + super.visitDeclaration(dcl) + verify(dcl) + } + fun verify(declaration: KtDeclaration) { declaration.docComment?.let { val text = it.getDefaultSection().getContent() diff --git a/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/KDocStyle.kt b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/KDocStyle.kt new file mode 100644 index 000000000..5ccc61510 --- /dev/null +++ b/detekt-rules-documentation/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/KDocStyle.kt @@ -0,0 +1,22 @@ +package io.gitlab.arturbosch.detekt.rules.documentation + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.MultiRule +import org.jetbrains.kotlin.psi.KtDeclaration + +class KDocStyle(config: Config = Config.empty) : MultiRule() { + + private val deprecatedBlockTag = DeprecatedBlockTag(config) + private val endOfSentenceFormat = EndOfSentenceFormat(config) + + override val rules = listOf( + deprecatedBlockTag, + endOfSentenceFormat + ) + + override fun visitDeclaration(dcl: KtDeclaration) { + super.visitDeclaration(dcl) + deprecatedBlockTag.verify(dcl) + endOfSentenceFormat.verify(dcl) + } +} diff --git a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTagSpec.kt b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTagSpec.kt new file mode 100644 index 000000000..42c2feca7 --- /dev/null +++ b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/DeprecatedBlockTagSpec.kt @@ -0,0 +1,144 @@ +package io.gitlab.arturbosch.detekt.rules.documentation + +import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.specification.describe + +class DeprecatedBlockTagSpec : Spek({ + val subject by memoized { DeprecatedBlockTag() } + describe("DeprecatedBlockTag rule") { + it("does not report regular kdoc block") { + val code = """ + /** + * This is just a regular kdoc block. + * + * Nothing to see here... + */ + val v = 2 + """ + assertThat(subject.compileAndLint(code)).hasSize(0) + } + + describe("reporting deprecation tag on kdoc block") { + val code = """ + /** + * I am a KDoc block + * + * @deprecated oh no, this should not be here + */ + fun ohNo() { } + """ + + it("has found something") { + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("correct message") { + assertThat(subject.compileAndLint(code)[0]).hasMessage( + "@deprecated tag block does not properly report " + + "deprecation in Kotlin, use @Deprecated annotation instead" + ) + } + } + + describe("reporting deprecation tag wherever @Deprecated is available") { + + it("report deprecation tag on class") { + val code = """ + /** + * Hello there + * + * @deprecated This thing is deprecated + */ + class Thing { } + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on property") { + val code = """ + class Thing { + /** + * A thing you should not use + * + * @deprecated Do not use that + */ + val doNotUseMe = 0 + } + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on annotation class") { + val code = """ + /** + * An annotation you should not use + * + * @deprecated Do not use that + */ + annotation class Thing() + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on constructor") { + val code = """ + class Thing { + /** + * A constructor you should not use + * + * @deprecated Do not use that + */ + constructor(something: String) + } + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on property setter") { + val code = """ + class Thing { + var someProperty: Int + get() = 10 + /** + * Do not use this setter + * + * @deprecated Do not use it + */ + set(value) { println(value) } + } + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on property getter") { + val code = """ + class Thing { + var someProperty: Int + /** + * Do not use this getter + * + * @deprecated Do not use it + */ + get() = 10 + set(value) { println(value) } + } + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + + it("report deprecation tag on typealias") { + val code = """ + /** + * This alias is pointless, do not use it + * + * @deprecated Do not use this typealias + */ + typealias VeryString = String + """ + assertThat(subject.compileAndLint(code)).hasSize(1) + } + } + } +}) diff --git a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormatSpec.kt b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormatSpec.kt index 7fc621107..62c1df584 100644 --- a/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormatSpec.kt +++ b/detekt-rules-documentation/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/documentation/EndOfSentenceFormatSpec.kt @@ -6,7 +6,7 @@ import org.spekframework.spek2.Spek import org.spekframework.spek2.style.specification.describe class EndOfSentenceFormatSpec : Spek({ - val subject by memoized { KDocStyle() } + val subject by memoized { EndOfSentenceFormat() } describe("KDocStyle rule") {