Skip to content

Commit

Permalink
Merge branch 'master' into bugfix/indentation-inside-string-templates(#…
Browse files Browse the repository at this point in the history
  • Loading branch information
aktsay6 authored Feb 16, 2021
2 parents c92c850 + 505b024 commit f2d3a98
Show file tree
Hide file tree
Showing 94 changed files with 869 additions and 148 deletions.
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Now diKTat was already added to the lists of [static analysis tools](https://git

| | | | | | |
| --- | --- | --- | --- | --- | --- |
|[Codestyle](info/guide/diktat-coding-convention.md)|[Inspections](info/available-rules.md) | [Examples](https://github.com/akuleshov7/diktat-examples) | [Demo](https://ktlint-demo.herokuapp.com) | [White Paper](wp/wp.pdf) | [Groups of Inspections](info/rules-mapping.md) |
|[Codestyle](info/guide/diktat-coding-convention.md)|[Inspections](info/available-rules.md) | [Examples](examples) | [Demo](https://ktlint-demo.herokuapp.com) | [White Paper](wp/wp.pdf) | [Groups of Inspections](info/rules-mapping.md) |

## Why should I use diktat in my CI/CD?

Expand Down Expand Up @@ -163,6 +163,74 @@ diktat {

You can run diktat checks using task `diktatCheck` and automatically fix errors with tasks `diktatFix`.

## Run with Spotless
[Spotless](https://github.com/diffplug/spotless) is a linter aggregator.

### Gradle
Diktat can be run via spotless-gradle-plugin since version 5.10.0

<details>
<summary>Add this plugin to your build.gradle.kts</summary>

```kotlin
plugins {
id("com.diffplug.spotless") version "5.10.0"
}
spotless {
kotlin {
diktat()
}
kotlinGradle {
diktat()
}
}
```
</details>

<details>
<summary>You can provide a version and configuration path manually as configFile.</summary>

```kotlin
spotless {
kotlin {
diktat("0.4.0").configFile("full/path/to/diktat-analysis.yml")
}
}
```
</details>

### Maven
Diktat can be run via spotless-maven-plugin since version 2.8.0

<details>
<summary>Add this plugin to your pom.xml</summary>

```xml
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>${spotless.version}</version>
<configuration>
<kotlin>
<diktat />
</kotlin>
</configuration>
</plugin>
```
</details>

<details>
<summary>You can provide a version and configuration path manually as configFile</summary>

```xml
<diktat>
<version>0.4.0</version> <!-- optional -->
<configFile>full/path/to/diktat-analysis.yml</configFile> <!-- optional, configuration file path -->
</diktat>
```
</details>

## Customizations via `diktat-analysis.yml`

In KTlint, rules can be configured via `.editorconfig`, but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory

import java.io.BufferedReader
import java.io.File
import java.util.concurrent.atomic.AtomicInteger

import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
Expand Down Expand Up @@ -122,7 +123,9 @@ data class CommonConfiguration(private val configuration: Map<String, String>?)
*/
val kotlinVersion: KotlinVersion by lazy {
configuration?.get("kotlinVersion")?.kotlinVersion() ?: run {
log.error("Kotlin version not specified in the configuration file. Will be using ${KotlinVersion.CURRENT} version")
if (visitorCounter.incrementAndGet() == 1) {
log.error("Kotlin version not specified in the configuration file. Will be using ${KotlinVersion.CURRENT} version")
}
KotlinVersion.CURRENT
}
}
Expand All @@ -131,16 +134,21 @@ data class CommonConfiguration(private val configuration: Map<String, String>?)
* False if configuration has been read from config file, true if defaults are used
*/
val isDefault = configuration == null

companion object {
/**
* Counter that helps not to raise multiple warnings about kotlin version
*/
var visitorCounter = AtomicInteger(0)
}
}

// ================== utils for List<RulesConfig> from yml config

/**
* @return common configuration from list of all rules configuration
*/
fun List<RulesConfig>.getCommonConfiguration() = lazy {
CommonConfiguration(getCommonConfig()?.configuration)
}
fun List<RulesConfig>.getCommonConfiguration() = CommonConfiguration(getCommonConfig()?.configuration)

/**
* Get [RulesConfig] for particular [Rule] object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ConfigReaderTest {
.readResource("src/test/resources/test-rules-config.yml")
val kotlinVersionForTest = KotlinVersion(1, 4, 21)
requireNotNull(rulesConfigList)
assert(rulesConfigList.getCommonConfiguration().value.kotlinVersion == kotlinVersionForTest)
assert(rulesConfigList.getCommonConfiguration().kotlinVersion == kotlinVersionForTest)
assert(rulesConfigList.find { it.name == DIKTAT_COMMON }
?.configuration
?.get("kotlinVersion")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ package org.cqfn.diktat.plugin.gradle

import groovy.lang.Closure

@Suppress("MISSING_KDOC_TOP_LEVEL", "MISSING_KDOC_CLASS_ELEMENTS", "KDOC_NO_CONSTRUCTOR_PROPERTY",
"MISSING_KDOC_ON_FUNCTION", "KDOC_WITHOUT_PARAM_TAG", "KDOC_WITHOUT_RETURN_TAG")
@Suppress(
"MISSING_KDOC_TOP_LEVEL",
"MISSING_KDOC_CLASS_ELEMENTS",
"KDOC_NO_CONSTRUCTOR_PROPERTY",
"MISSING_KDOC_ON_FUNCTION",
"KDOC_WITHOUT_PARAM_TAG",
"KDOC_WITHOUT_RETURN_TAG"
)
class KotlinClosure1<in T : Any?, V : Any>(
val function: T.() -> V?,
owner: Any? = null,
Expand All @@ -21,6 +27,11 @@ class KotlinClosure1<in T : Any?, V : Any>(

// These two are copy-pasted from `kotlin-dsl` plugin's groovy interop.
// Because `kotlin-dsl` depends on kotlin 1.3.x.
@Suppress("MISSING_KDOC_TOP_LEVEL", "MISSING_KDOC_ON_FUNCTION", "KDOC_WITHOUT_PARAM_TAG", "KDOC_WITHOUT_RETURN_TAG")
@Suppress(
"MISSING_KDOC_TOP_LEVEL",
"MISSING_KDOC_ON_FUNCTION",
"KDOC_WITHOUT_PARAM_TAG",
"KDOC_WITHOUT_RETURN_TAG"
)
fun <T> Any.closureOf(action: T.() -> Unit): Closure<Any?> =
KotlinClosure1(action, this, this)
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum class Chapters(val number: String, val title: String) {
*/
fun Warnings.isRuleFromActiveChapter(configRules: List<RulesConfig>): Boolean {
val chapterFromRule = getChapterByWarning()
val configuration by configRules.getCommonConfiguration()
val configuration = configRules.getCommonConfiguration()
val disabledChapters = configuration.disabledChapters
?.takeIf { it.isNotBlank() }
?.split(",")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ typealias ListOfPairs = MutableList<Pair<ASTNode, String>>
* @property canBeAutoCorrected whether this inspection can automatically fix the code
* @property ruleId number of the inspection according to []diktat code style](https://www.cqfn.org/diKTat/info/guide/diktat-coding-convention.html)
*/
@Suppress("ForbiddenComment", "MagicNumber", "WRONG_DECLARATIONS_ORDER", "MaxLineLength")
@Suppress(
"ForbiddenComment",
"MagicNumber",
"WRONG_DECLARATIONS_ORDER",
"MaxLineLength",
"WRONG_NEWLINES"
)
enum class Warnings(
val canBeAutoCorrected: Boolean,
val ruleId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import java.io.File
* Aggressive: In case file contains only one class on upper level - it should be named with the same name
*/
@Suppress("ForbiddenComment")
class FileNaming(configRules: List<RulesConfig>) : DiktatRule("file-naming", configRules, listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)) {
class FileNaming(configRules: List<RulesConfig>) : DiktatRule(
"file-naming",
configRules,
listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)) {
private lateinit var filePath: String

override fun logic(node: ASTNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
* // FixMe: because it fixes only declaration without the usages
*/
@Suppress("ForbiddenComment", "MISSING_KDOC_CLASS_ELEMENTS")
class IdentifierNaming(configRules: List<RulesConfig>) : DiktatRule("identifier-naming", configRules,
class IdentifierNaming(configRules: List<RulesConfig>) : DiktatRule(
"identifier-naming",
configRules,
listOf(BACKTICKS_PROHIBITED, VARIABLE_NAME_INCORRECT, VARIABLE_NAME_INCORRECT_FORMAT, CONSTANT_UPPERCASE,
VARIABLE_HAS_PREFIX, CONFUSING_IDENTIFIER_NAMING, GENERIC_NAME, CLASS_NAME_INCORRECT,
ENUM_VALUE, EXCEPTION_SUFFIX, FUNCTION_BOOLEAN_PREFIX, FUNCTION_NAME_INCORRECT_CASE,
Expand Down Expand Up @@ -148,7 +150,11 @@ class IdentifierNaming(configRules: List<RulesConfig>) : DiktatRule("identifier-
/**
* all checks for case and naming for vals/vars/constants
*/
@Suppress("SAY_NO_TO_VAR", "TOO_LONG_FUNCTION", "ComplexMethod")
@Suppress(
"SAY_NO_TO_VAR",
"TOO_LONG_FUNCTION",
"ComplexMethod"
)
private fun checkVariableName(node: ASTNode): List<ASTNode> {
// special case for Destructuring declarations that can be treated as parameters in lambda:
var namesOfVariables = extractVariableIdentifiers(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ import java.util.concurrent.atomic.AtomicInteger
* package a.b.c.D -> then class D should be placed in a/b/c/ directories
*/
@Suppress("ForbiddenComment", "TOO_MANY_LINES_IN_LAMBDA")
class PackageNaming(configRules: List<RulesConfig>) : DiktatRule("package-naming", configRules,
class PackageNaming(configRules: List<RulesConfig>) : DiktatRule(
"package-naming",
configRules,
listOf(INCORRECT_PACKAGE_SEPARATOR, PACKAGE_NAME_INCORRECT_CASE, PACKAGE_NAME_MISSING,
PACKAGE_NAME_INCORRECT_PATH, PACKAGE_NAME_INCORRECT_PREFIX, PACKAGE_NAME_INCORRECT_SYMBOLS)) {
private lateinit var domainName: String

override fun logic(node: ASTNode) {
val configuration by configRules.getCommonConfiguration()
val configuration = configRules.getCommonConfiguration()
configuration.domainName?.let {
domainName = it
if (node.elementType == PACKAGE_DIRECTIVE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import org.jetbrains.kotlin.resolve.ImportPath
* No commented out code is allowed, including imports.
*/
@Suppress("ForbiddenComment")
class CommentsRule(configRules: List<RulesConfig>) : DiktatRule("comments", configRules, listOf(COMMENTED_OUT_CODE)) {
class CommentsRule(configRules: List<RulesConfig>) : DiktatRule(
"comments",
configRules,
listOf(COMMENTED_OUT_CODE)) {
private lateinit var ktPsiFactory: KtPsiFactory

override fun logic(node: ASTNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ import java.time.LocalDate
* 4) Ensure files with many or zero classes have proper description
*/
@Suppress("ForbiddenComment")
class HeaderCommentRule(configRules: List<RulesConfig>) : DiktatRule("header-comment", configRules,
class HeaderCommentRule(configRules: List<RulesConfig>) : DiktatRule(
"header-comment",
configRules,
listOf(HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE, HEADER_MISSING_OR_WRONG_COPYRIGHT, HEADER_NOT_BEFORE_PACKAGE,
HEADER_NOT_BEFORE_PACKAGE, HEADER_WRONG_FORMAT, WRONG_COPYRIGHT_YEAR)) {
override fun logic(node: ASTNode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
* * Leave one single space between the comment on the right side of the code and the code.
* * Comments in if else should be inside code blocks. Exception: General if comment
*/
class CommentsFormatting(configRules: List<RulesConfig>) : DiktatRule("kdoc-comments-codeblocks-formatting", configRules,
class CommentsFormatting(configRules: List<RulesConfig>) : DiktatRule(
"kdoc-comments-codeblocks-formatting",
configRules,
listOf(COMMENT_WHITE_SPACE, FIRST_COMMENT_NO_BLANK_LINE,
IF_ELSE_COMMENTS, WRONG_NEWLINES_AROUND_KDOC)) {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
* 2) All internal elements in class like class, property or function should be documented with KDoc
* 3) All properties declared in the primary constructor are documented using `@property` tag in class KDoc
*/
class KdocComments(configRules: List<RulesConfig>) : DiktatRule("kdoc-comments", configRules,
class KdocComments(configRules: List<RulesConfig>) : DiktatRule(
"kdoc-comments",
configRules,
listOf(KDOC_EXTRA_PROPERTY, KDOC_NO_CONSTRUCTOR_PROPERTY,
KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT, MISSING_KDOC_CLASS_ELEMENTS, MISSING_KDOC_TOP_LEVEL)) {
/**
Expand All @@ -53,7 +55,7 @@ class KdocComments(configRules: List<RulesConfig>) : DiktatRule("kdoc-comments",
* @param emit
*/
override fun logic(node: ASTNode) {
val config = configRules.getCommonConfiguration().value
val config = configRules.getCommonConfiguration()
val filePath = node.getRootNode().getFilePath()
if (!(node.hasTestAnnotation() || isLocatedInTest(filePath.splitPathToDirs(), config.testAnchors))) {
when (node.elementType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ import java.time.temporal.ChronoField
* 7) ensuring @since tag contains only versions and not dates
*/
@Suppress("ForbiddenComment")
class KdocFormatting(configRules: List<RulesConfig>) : DiktatRule("kdoc-formatting", configRules,
class KdocFormatting(configRules: List<RulesConfig>) : DiktatRule(
"kdoc-formatting",
configRules,
listOf(KDOC_CONTAINS_DATE_OR_AUTHOR, KDOC_EMPTY_KDOC, KDOC_NEWLINES_BEFORE_BASIC_TAGS, KDOC_NO_DEPRECATED_TAG,
KDOC_NO_EMPTY_TAGS, KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS, KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS,
KDOC_WRONG_SPACES_AFTER_TAG, KDOC_WRONG_TAGS_ORDER)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
* Currently only `throw` keyword from this methods body is supported for `@throws` check.
*/
@Suppress("ForbiddenComment")
class KdocMethods(configRules: List<RulesConfig>) : DiktatRule("kdoc-methods", configRules,
class KdocMethods(configRules: List<RulesConfig>) : DiktatRule(
"kdoc-methods",
configRules,
listOf(KDOC_TRIVIAL_KDOC_ON_FUNCTION, KDOC_WITHOUT_PARAM_TAG, KDOC_WITHOUT_RETURN_TAG,
KDOC_WITHOUT_THROWS_TAG, MISSING_KDOC_ON_FUNCTION)) {
/**
Expand All @@ -75,7 +77,7 @@ class KdocMethods(configRules: List<RulesConfig>) : DiktatRule("kdoc-methods", c
*/
override fun logic(node: ASTNode) {
if (node.elementType == FUN && node.getFirstChildWithType(MODIFIER_LIST).isAccessibleOutside() && !node.isOverridden()) {
val config = configRules.getCommonConfiguration().value
val config = configRules.getCommonConfiguration()
val filePath = node.getRootNode().getFilePath()
val isTestMethod = node.hasTestAnnotation() || isLocatedInTest(filePath.splitPathToDirs(), config.testAnchors)
if (!isTestMethod && !node.isStandardMethod() && !node.isSingleLineGetterOrSetter()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
/**
* This rule makes each annotation applied to a class, method or constructor is on its own line. Except: if first annotation of constructor, class or method
*/
class AnnotationNewLineRule(configRules: List<RulesConfig>) : DiktatRule("annotation-new-line", configRules, listOf(ANNOTATION_NEW_LINE)) {
class AnnotationNewLineRule(configRules: List<RulesConfig>) : DiktatRule(
"annotation-new-line",
configRules,
listOf(ANNOTATION_NEW_LINE)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
CLASS, FUN, PRIMARY_CONSTRUCTOR, SECONDARY_CONSTRUCTOR -> checkAnnotation(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ import org.jetbrains.kotlin.psi.KtTryExpression
* - opening brace of lambda
* - braces around `else`/`catch`/`finally`/`while` (in `do-while` loop)
*/
class BlockStructureBraces(configRules: List<RulesConfig>) : DiktatRule("block-structure", configRules, listOf(BRACES_BLOCK_STRUCTURE_ERROR)) {
class BlockStructureBraces(configRules: List<RulesConfig>) : DiktatRule(
"block-structure",
configRules,
listOf(BRACES_BLOCK_STRUCTURE_ERROR)) {
override fun logic(node: ASTNode) {
val configuration = BlockStructureBracesConfiguration(
configRules.getRuleConfig(BRACES_BLOCK_STRUCTURE_ERROR)?.configuration ?: emptyMap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import org.jetbrains.kotlin.psi.psiUtil.astReplace
/**
* Rule that checks that all conditionals and loops have braces.
*/
class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatRule("braces-rule", configRules, listOf(NO_BRACES_IN_CONDITIONALS_AND_LOOPS)) {
class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatRule(
"braces-rule",
configRules,
listOf(NO_BRACES_IN_CONDITIONALS_AND_LOOPS)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
IF -> checkIfNode(node)
Expand All @@ -47,7 +50,12 @@ class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatR
/**
* Check braces in if-else statements. Check for both IF and ELSE needs to be done in one method to discover single-line if-else statements correctly.
*/
@Suppress("ForbiddenComment", "UnsafeCallOnNullableType", "ComplexMethod", "TOO_LONG_FUNCTION")
@Suppress(
"ForbiddenComment",
"UnsafeCallOnNullableType",
"ComplexMethod",
"TOO_LONG_FUNCTION"
)
private fun checkIfNode(node: ASTNode) {
val ifPsi = node.psi as KtIfExpression
val thenNode = ifPsi.then?.node
Expand Down
Loading

0 comments on commit f2d3a98

Please sign in to comment.