From f5f4b819d16f3b1e8a9d2ddec2177693ea321abf Mon Sep 17 00:00:00 2001 From: ArtiSmarti Date: Sat, 22 Oct 2016 14:52:40 +0200 Subject: [PATCH] Create signature based on extends, implements, types and parameters of classes/methods --- .../gitlab/arturbosch/detekt/api/Findings.kt | 59 +++++++++++++++++-- .../detekt/rules/NoDocOverPublicClass.kt | 24 ++++++-- .../detekt/rules/NoDocOverPublicMethod.kt | 3 +- .../detekt/rules/NoDocOverPublicClassSpec.kt | 2 +- .../detekt/rules/NoDocOverPublicMethodSpec.kt | 4 +- .../src/test/resources/cases/Comments.kt | 8 ++- 6 files changed, 84 insertions(+), 16 deletions(-) diff --git a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt index f80d54ef9..56d5a9587 100644 --- a/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt +++ b/detekt-api/src/main/kotlin/io/gitlab/arturbosch/detekt/api/Findings.kt @@ -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 ?: "" } 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 } } diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt index 1f5194349..86d1a21ae 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClass.kt @@ -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" + } \ No newline at end of file diff --git a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt index 6d520cafe..38e1c9165 100644 --- a/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt +++ b/detekt-rules/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethod.kt @@ -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) } \ No newline at end of file diff --git a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClassSpec.kt b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClassSpec.kt index 47e00d5ef..4443b601a 100644 --- a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClassSpec.kt +++ b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicClassSpec.kt @@ -12,7 +12,7 @@ import org.jetbrains.spek.api.dsl.it class NoDocOverPublicClassSpec : SubjectSpek({ 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))) diff --git a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethodSpec.kt b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethodSpec.kt index b9391cfd3..99faad701 100644 --- a/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethodSpec.kt +++ b/detekt-rules/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/NoDocOverPublicMethodSpec.kt @@ -12,9 +12,9 @@ import org.jetbrains.spek.api.dsl.it class NoDocOverPublicMethodSpec : SubjectSpek({ 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))) } }) \ No newline at end of file diff --git a/detekt-rules/src/test/resources/cases/Comments.kt b/detekt-rules/src/test/resources/cases/Comments.kt index d3df9b7d3..21c5641cf 100644 --- a/detekt-rules/src/test/resources/cases/Comments.kt +++ b/detekt-rules/src/test/resources/cases/Comments.kt @@ -1,10 +1,16 @@ package cases +import java.io.Serializable + /** * @author Artur Bosch */ @Suppress("unused") -open class Comments { +open class Comments : Serializable { + + companion object { + fun withinCompanionNeedComment(){} + } public class INeedComment private class NoNeedForComments{}