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
This commit is contained in:
Zoroark
2021-04-24 00:00:54 +02:00
committed by GitHub
parent 7aaefb96fc
commit 238c3cddbd
6 changed files with 244 additions and 16 deletions

View File

@@ -57,6 +57,8 @@ comments:
active: false
CommentOverPrivateProperty:
active: false
DeprecatedBlockTag:
active: false
EndOfSentenceFormat:
active: false
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'

View File

@@ -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.
*
* <noncompliant>
* /**
* * 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) {
* // ...
* }
* </noncompliant>
*
* <compliant>
* /**
* * This function prints a message followed by a new line.
* */
* &#64;Deprecated("Useless, the Kotlin standard library can already do this.")
* &#64;ReplaceWith("println(what)")
* fun printThenNewline(what: String) {
* // ...
* }
* </compliant>
*/
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))
)
)
}
}
}
}

View File

@@ -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()

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}
})

View File

@@ -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") {