Create signature based on extends, implements, types and parameters of classes/methods

This commit is contained in:
ArtiSmarti
2016-10-22 14:52:40 +02:00
parent 8df668350e
commit f5f4b819d1
6 changed files with 84 additions and 16 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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