mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
Create signature based on extends, implements, types and parameters of classes/methods
This commit is contained in:
@@ -4,6 +4,7 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getTextWithLocation
|
||||
@@ -96,31 +97,77 @@ data class Entity(val name: String,
|
||||
val location: Location) : Compactable {
|
||||
|
||||
override fun compact(): String {
|
||||
return "$name/$className - ${location.compact()}"
|
||||
return "$name/$className/$signature - ${location.compact()}"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(startElement: PsiElement, endElementExclusively: PsiElement?): Entity {
|
||||
val name = startElement.namedUnwrappedElement?.name ?: "Not found: ${startElement.text}"
|
||||
val signature = startElement.text
|
||||
val name = searchName(startElement)
|
||||
val signature = searchSignature(startElement)
|
||||
val clazz = searchClass(startElement)
|
||||
return Entity(name, clazz, signature, Location.from(startElement, endElementExclusively))
|
||||
}
|
||||
|
||||
fun from(element: PsiElement): Entity {
|
||||
val name = searchName(element)
|
||||
val signature = element.text
|
||||
val signature = searchSignature(element)
|
||||
val clazz = searchClass(element)
|
||||
return Entity(name, clazz, signature, Location.from(element))
|
||||
}
|
||||
|
||||
private fun searchSignature(element: PsiElement): String {
|
||||
return when (element) {
|
||||
is KtNamedFunction -> buildFunctionSignature(element)
|
||||
is KtClassOrObject -> buildClassSignature(element)
|
||||
else -> element.text
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildClassSignature(classOrObject: KtClassOrObject): String {
|
||||
var baseName = classOrObject.nameAsSafeName.asString()
|
||||
val typeParameters = classOrObject.typeParameters
|
||||
if (typeParameters.size > 0) {
|
||||
baseName += "<"
|
||||
baseName += typeParameters.joinToString(", ") { it.text }
|
||||
baseName += ">"
|
||||
}
|
||||
val extendedEntries = classOrObject.getSuperTypeListEntries()
|
||||
if (extendedEntries.size > 0) baseName += " : "
|
||||
extendedEntries.forEach { baseName += it.typeAsUserType?.referencedName ?: "" }
|
||||
return baseName
|
||||
}
|
||||
|
||||
private fun buildFunctionSignature(element: KtNamedFunction): String {
|
||||
val methodStart = 0
|
||||
var methodEnd = element.endOffset - element.startOffset
|
||||
val typeReference = element.typeReference
|
||||
if (typeReference != null) {
|
||||
methodEnd = typeReference.endOffset - element.startOffset
|
||||
} else {
|
||||
element.valueParameterList?.let {
|
||||
methodEnd = it.endOffset - element.startOffset
|
||||
}
|
||||
}
|
||||
require(methodStart < methodEnd) {
|
||||
"Error building function signature with range $methodStart - $methodEnd for element: ${element.text}"
|
||||
}
|
||||
return element.text.substring(methodStart, methodEnd)
|
||||
}
|
||||
|
||||
private fun searchName(element: PsiElement): String {
|
||||
return element.namedUnwrappedElement?.name ?: "Not found: ${element.text}"
|
||||
return element.namedUnwrappedElement?.name ?: "<NoNameFound>"
|
||||
}
|
||||
|
||||
private fun searchClass(element: PsiElement): String {
|
||||
return element.getNonStrictParentOfType(KtClassOrObject::class.java)?.name ?: element.containingFile.name
|
||||
val classElement = element.getNonStrictParentOfType(KtClassOrObject::class.java)
|
||||
var className = classElement?.name
|
||||
if (className != null && className == "Companion") {
|
||||
classElement?.parent?.getNonStrictParentOfType(KtClassOrObject::class.java)?.name?.let {
|
||||
className = it + ".$className"
|
||||
}
|
||||
}
|
||||
return className ?: element.containingFile.name
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,18 +4,32 @@ import io.gitlab.arturbosch.detekt.api.CodeSmell
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.api.Entity
|
||||
import io.gitlab.arturbosch.detekt.api.Rule
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
|
||||
/**
|
||||
* @author Artur Bosch
|
||||
*/
|
||||
class NoDocOverPublicClass(config: Config = Config.EMPTY) : Rule("NoDocOverPublicClass", Severity.Maintainability, config) {
|
||||
|
||||
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
|
||||
if (classOrObject.isPublicNotOverriden()) {
|
||||
addFindings(CodeSmell(id, Entity.from(classOrObject, classOrObject.getBody())))
|
||||
override fun visitClass(klass: KtClass) {
|
||||
if (klass.isPublicNotOverriden()) {
|
||||
addFindings(CodeSmell(id, Entity.from(klass)))
|
||||
}
|
||||
super.visitClassOrObject(classOrObject)
|
||||
super.visitClass(klass)
|
||||
}
|
||||
|
||||
override fun visitObjectDeclaration(declaration: KtObjectDeclaration) {
|
||||
if (declaration.isCompanionWithoutName())
|
||||
return
|
||||
|
||||
if (declaration.isPublicNotOverriden()) {
|
||||
addFindings(CodeSmell(id, Entity.from(declaration)))
|
||||
}
|
||||
super.visitObjectDeclaration(declaration)
|
||||
}
|
||||
|
||||
private fun KtObjectDeclaration.isCompanionWithoutName() =
|
||||
this.isCompanion() && this.nameAsSafeName.asString() == "Companion"
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ class NoDocOverPublicMethod(config: Config = Config.EMPTY) : Rule("NoDocOverPubl
|
||||
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
if (function.funKeyword == null && function.isLocal) return
|
||||
|
||||
val modifierList = function.modifierList
|
||||
if (function.docComment == null) {
|
||||
if (modifierList == null) {
|
||||
@@ -26,6 +27,6 @@ class NoDocOverPublicMethod(config: Config = Config.EMPTY) : Rule("NoDocOverPubl
|
||||
}
|
||||
}
|
||||
|
||||
private fun methodHeaderLocation(function: KtNamedFunction) = Entity.from(function, function.colon)
|
||||
private fun methodHeaderLocation(function: KtNamedFunction) = Entity.from(function)
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.spek.api.dsl.it
|
||||
class NoDocOverPublicClassSpec : SubjectSpek<NoDocOverPublicClass>({
|
||||
subject { NoDocOverPublicClass() }
|
||||
|
||||
it("finds undocumented class") {
|
||||
it("finds two undocumented classes") {
|
||||
val root = load(Case.Comments)
|
||||
subject.visit(root)
|
||||
assertThat(subject.findings, hasSize(equalTo(2)))
|
||||
|
||||
@@ -12,9 +12,9 @@ import org.jetbrains.spek.api.dsl.it
|
||||
class NoDocOverPublicMethodSpec : SubjectSpek<NoDocOverPublicMethod>({
|
||||
subject { NoDocOverPublicMethod() }
|
||||
|
||||
it("finds two undocumented functions") {
|
||||
it("finds three undocumented functions") {
|
||||
val root = load(Case.Comments)
|
||||
subject.visit(root)
|
||||
assertThat(subject.findings, hasSize(equalTo(2)))
|
||||
assertThat(subject.findings, hasSize(equalTo(3)))
|
||||
}
|
||||
})
|
||||
@@ -1,10 +1,16 @@
|
||||
package cases
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* @author Artur Bosch
|
||||
*/
|
||||
@Suppress("unused")
|
||||
open class Comments {
|
||||
open class Comments<T : K, out K> : Serializable {
|
||||
|
||||
companion object {
|
||||
fun withinCompanionNeedComment(){}
|
||||
}
|
||||
|
||||
public class INeedComment
|
||||
private class NoNeedForComments{}
|
||||
|
||||
Reference in New Issue
Block a user