diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 5f3983c7f5..48ff9cf0dc 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -118,3 +118,10 @@ jobs:
run: |
mvn -B clean install
shell: cmd
+
+ - name: Upload gradle reports
+ if: ${{ failure() }}
+ uses: actions/upload-artifact@v2
+ with:
+ name: gradle-test-report-${{ matrix.os }}
+ path: 'diktat-gradle-plugin/build/reports/'
diff --git a/README.md b/README.md
index 0cc29e5a45..6cdd05fe59 100644
--- a/README.md
+++ b/README.md
@@ -55,11 +55,11 @@ Main features of diktat are the following:
# another option is "brew install ktlint"
```
-2. Load diKTat manually: [here](https://github.com/cqfn/diKTat/releases/download/v0.4.0/diktat.jar)
+2. Load diKTat manually: [here](https://github.com/cqfn/diKTat/releases/download/v0.4.2/diktat.jar)
**OR** use curl:
```bash
- $ curl -sSLO https://github.com/cqfn/diKTat/releases/download/v0.4.0/diktat-0.4.0.jar
+ $ curl -sSLO https://github.com/cqfn/diKTat/releases/download/v0.4.2/diktat-0.4.2.jar
```
3. Finally, run KTlint (with diKTat injected) to check your `*.kt` files in `dir/your/dir`:
@@ -110,7 +110,7 @@ This plugin is available since version 0.1.5. You can see how the plugin is conf
Add this plugin to your `build.gradle.kts`:
```kotlin
plugins {
- id("org.cqfn.diktat.diktat-gradle-plugin") version "0.4.0"
+ id("org.cqfn.diktat.diktat-gradle-plugin") version "0.4.2"
}
```
@@ -121,7 +121,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath("org.cqfn.diktat:diktat-gradle-plugin:0.4.0")
+ classpath("org.cqfn.diktat:diktat-gradle-plugin:0.4.2")
}
}
@@ -194,7 +194,7 @@ spotless {
```kotlin
spotless {
kotlin {
- diktat("0.4.0").configFile("full/path/to/diktat-analysis.yml")
+ diktat("0.4.2").configFile("full/path/to/diktat-analysis.yml")
}
}
```
@@ -225,7 +225,7 @@ Diktat can be run via spotless-maven-plugin since version 2.8.0
```xml
- 0.4.0
+ 0.4.2
full/path/to/diktat-analysis.yml
```
diff --git a/detekt-config.yml b/detekt-config.yml
index 4268f5235e..f3e24ba4a3 100644
--- a/detekt-config.yml
+++ b/detekt-config.yml
@@ -9,6 +9,7 @@ build:
config:
validation: true
+ warningsAsErrors: false
# when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
excludes: ''
@@ -16,11 +17,18 @@ processors:
active: true
exclude:
- 'DetektProgressListener'
+ # - 'KtFileCountProcessor'
+ # - 'PackageCountProcessor'
+ # - 'ClassCountProcessor'
# - 'FunctionCountProcessor'
# - 'PropertyCountProcessor'
- # - 'ClassCountProcessor'
- # - 'PackageCountProcessor'
- # - 'KtFileCountProcessor'
+ # - 'ProjectComplexityProcessor'
+ # - 'ProjectCognitiveComplexityProcessor'
+ # - 'ProjectLLOCProcessor'
+ # - 'ProjectCLOCProcessor'
+ # - 'ProjectLOCProcessor'
+ # - 'ProjectSLOCProcessor'
+ # - 'LicenseHeaderLoaderExtension'
console-reports:
active: true
@@ -31,52 +39,49 @@ console-reports:
# - 'FindingsReport'
- 'FileBasedFindingsReport'
+output-reports:
+ active: true
+ exclude:
+ # - 'TxtOutputReport'
+ # - 'XmlOutputReport'
+ # - 'HtmlOutputReport'
+
comments:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
AbsentOrWrongFileLicense:
- excludes: ['**/resources/**']
active: false
-
licenseTemplateFile: 'license.template'
+ licenseTemplateIsRegex: false
CommentOverPrivateFunction:
- excludes: ['**/resources/**']
active: false
CommentOverPrivateProperty:
- excludes: ['**/resources/**']
active: false
EndOfSentenceFormat:
- excludes: ['**/resources/**']
active: false
endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
UndocumentedPublicClass:
- excludes: ['**/resources/**']
active: false
searchInNestedClass: true
searchInInnerClass: true
searchInInnerObject: true
searchInInnerInterface: true
UndocumentedPublicFunction:
- excludes: ['**/resources/**']
active: false
UndocumentedPublicProperty:
- excludes: ['**/resources/**']
active: false
complexity:
active: true
ComplexCondition:
- excludes: ['**/resources/**']
active: false
threshold: 4
ComplexInterface:
- excludes: ['**/resources/**']
active: false
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
ComplexMethod:
- excludes: ['**/resources/**']
active: true
threshold: 15
ignoreSingleWhenExpression: false
@@ -84,19 +89,15 @@ complexity:
ignoreNestingFunctions: false
nestingFunctions: [run, let, apply, with, also, use, forEach, isNotNull, ifNull]
LabeledExpression:
- excludes: ['**/resources/**']
active: false
ignoredLabels: []
LargeClass:
- excludes: ['**/resources/**']
active: true
threshold: 600
LongMethod:
- excludes: ['**/resources/**']
active: true
threshold: 60
LongParameterList:
- excludes: ['**/resources/**']
active: true
functionThreshold: 6
constructorThreshold: 7
@@ -104,13 +105,14 @@ complexity:
ignoreDataClasses: true
ignoreAnnotated: []
MethodOverloading:
- excludes: ['**/resources/**']
- active: false
+ active: true
threshold: 6
+ NamedArguments:
+ active: false
+ threshold: 3
NestedBlockDepth:
- excludes: ['**/resources/**']
- active: true
- threshold: 5
+ active: false
+ threshold: 4
ReplaceSafeCallChainWithRun:
active: false
StringLiteralDuplication:
@@ -135,88 +137,69 @@ complexity:
coroutines:
active: true
GlobalCoroutineUsage:
- excludes: ['**/resources/**']
- active: false
+ active: true
RedundantSuspendModifier:
- excludes: ['**/resources/**']
- active: false
+ active: true
+ SleepInsteadOfDelay:
+ active: true
SuspendFunWithFlowReturnType:
- active: false
+ active: true
empty-blocks:
active: true
EmptyCatchBlock:
- excludes: ['**/resources/**']
active: true
allowedExceptionNameRegex: '_|(ignore|expected).*'
EmptyClassBlock:
- excludes: ['**/resources/**']
active: true
EmptyDefaultConstructor:
- excludes: ['**/resources/**']
active: true
EmptyDoWhileBlock:
- excludes: ['**/resources/**']
active: true
EmptyElseBlock:
- excludes: ['**/resources/**']
active: true
EmptyFinallyBlock:
- excludes: ['**/resources/**']
active: true
EmptyForBlock:
- excludes: ['**/resources/**']
active: true
EmptyFunctionBlock:
- excludes: ['**/resources/**']
active: true
ignoreOverridden: false
EmptyIfBlock:
- excludes: ['**/resources/**']
active: true
EmptyInitBlock:
- excludes: ['**/resources/**']
active: true
EmptyKtFile:
- excludes: ['**/resources/**']
active: true
EmptySecondaryConstructor:
- excludes: ['**/resources/**']
active: true
EmptyTryBlock:
- excludes: ['**/resources/**']
active: true
EmptyWhenBlock:
- excludes: ['**/resources/**']
active: true
EmptyWhileBlock:
- excludes: ['**/resources/**']
active: true
exceptions:
active: true
ExceptionRaisedInUnexpectedLocation:
- excludes: ['**/resources/**']
- active: false
+ active: true
methodNames: [toString, hashCode, equals, finalize]
InstanceOfCheckForException:
active: false
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
NotImplementedDeclaration:
- excludes: ['**/resources/**']
- active: false
+ active: true
+ ObjectExtendsThrowable:
+ active: true
PrintStackTrace:
- excludes: ['**/resources/**']
- active: false
+ active: true
RethrowCaughtException:
- excludes: ['**/resources/**']
- active: false
+ active: true
ReturnFromFinally:
- excludes: ['**/resources/**']
- active: false
+ active: true
ignoreLabeled: false
SwallowedException:
- excludes: ['**/resources/**']
active: false
ignoredExceptionTypes:
- InterruptedException
@@ -225,21 +208,18 @@ exceptions:
- MalformedURLException
allowedExceptionNameRegex: '_|(ignore|expected).*'
ThrowingExceptionFromFinally:
- excludes: ['**/resources/**']
- active: false
+ active: true
ThrowingExceptionInMain:
- excludes: ['**/resources/**']
- active: false
+ active: true
ThrowingExceptionsWithoutMessageOrCause:
- active: false
+ active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
exceptions:
- IllegalArgumentException
- IllegalStateException
- IOException
ThrowingNewInstanceOfSameException:
- excludes: ['**/resources/**']
- active: false
+ active: true
TooGenericExceptionCaught:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
@@ -254,7 +234,6 @@ exceptions:
- Throwable
allowedExceptionNameRegex: '_|(ignore|expected).*'
TooGenericExceptionThrown:
- excludes: ['**/resources/**']
active: true
exceptionNames:
- Error
@@ -267,158 +246,129 @@ formatting:
android: false
autoCorrect: true
AnnotationOnSeparateLine:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
AnnotationSpacing:
active: true
+ autoCorrect: true
ArgumentListWrapping:
active: true
+ autoCorrect: true
ChainWrapping:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
CommentSpacing:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
EnumEntryNameCase:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
Filename:
- excludes: ['**/resources/**']
active: true
FinalNewline:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
insertFinalNewLine: true
ImportOrdering:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
layout: 'idea'
Indentation:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
indentSize: 4
continuationIndentSize: 4
MaximumLineLength:
- excludes: ['**/resources/**']
active: true
maxLineLength: 180
ModifierOrdering:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
MultiLineIfElse:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoBlankLineBeforeRbrace:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoConsecutiveBlankLines:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoEmptyClassBody:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoEmptyFirstLineInMethodBlock:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
NoLineBreakAfterElse:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoLineBreakBeforeAssignment:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoMultipleSpaces:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoSemicolons:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoTrailingSpaces:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoUnitReturn:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoUnusedImports:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
NoWildcardImports:
- excludes: ['**/resources/**']
active: true
PackageName:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
ParameterListWrapping:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
indentSize: 4
+ SpacingAroundAngleBrackets:
+ active: true
+ autoCorrect: true
SpacingAroundColon:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundComma:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundCurly:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundDot:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundDoubleColon:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
SpacingAroundKeyword:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundOperators:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundParens:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
SpacingAroundRangeOperator:
- excludes: ['**/resources/**']
+ active: true
+ autoCorrect: true
+ SpacingAroundUnaryOperator:
active: true
autoCorrect: true
SpacingBetweenDeclarationsWithAnnotations:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
SpacingBetweenDeclarationsWithComments:
- excludes: ['**/resources/**']
active: false
autoCorrect: true
StringTemplate:
- excludes: ['**/resources/**']
active: true
autoCorrect: true
@@ -465,19 +415,20 @@ naming:
excludeClassPattern: '$^'
ignoreOverridden: true
InvalidPackageDeclaration:
- excludes: ['**/resources/**']
active: false
+ excludes: ['*.kts']
rootPackage: ''
MatchingDeclarationName:
- excludes: ['**/resources/**']
active: true
mustBeFirst: true
MemberNameEqualsClassName:
- excludes: ['**/resources/**']
active: true
ignoreOverridden: true
+ NoNameShadowing:
+ active: false
NonBooleanPropertyPrefixedWithIs:
- active: true
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
ObjectPropertyNaming:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
@@ -513,58 +464,50 @@ naming:
performance:
active: true
ArrayPrimitive:
- excludes: ['**/resources/**']
active: true
ForEachOnRange:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
SpreadOperator:
- active: false
+ active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
UnnecessaryTemporaryInstantiation:
- excludes: ['**/resources/**']
active: true
potential-bugs:
active: true
- Deprecation:
- excludes: ['**/resources/**']
+ CastToNullableType:
active: false
+ Deprecation:
+ active: true
+ DontDowncastCollectionTypes:
+ active: true
DuplicateCaseInWhenExpression:
- excludes: ['**/resources/**']
active: true
EqualsAlwaysReturnsTrueOrFalse:
- excludes: ['**/resources/**']
active: true
EqualsWithHashCodeExist:
- excludes: ['**/resources/**']
active: true
+ ExitOutsideMain:
+ active: false
ExplicitGarbageCollectionCall:
- excludes: ['**/resources/**']
active: true
HasPlatformType:
- excludes: ['**/resources/**']
active: false
IgnoredReturnValue:
- excludes: ['**/resources/**']
- active: false
+ active: true
restrictToAnnotatedMethods: true
returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult']
ImplicitDefaultLocale:
- excludes: ['**/resources/**']
active: false
ImplicitUnitReturnType:
- excludes: ['**/resources/**']
active: false
allowExplicitReturnType: true
InvalidRange:
- excludes: ['**/resources/**']
active: true
IteratorHasNextCallsNextMethod:
- excludes: ['**/resources/**']
active: true
IteratorNotThrowingNoSuchElementException:
- excludes: ['**/resources/**']
active: true
LateinitUsage:
active: false
@@ -572,39 +515,33 @@ potential-bugs:
excludeAnnotatedProperties: []
ignoreOnClassesPattern: ''
MapGetWithNotNullAssertionOperator:
- excludes: ['**/resources/**']
active: false
MissingWhenCase:
- excludes: ['**/resources/**']
active: true
+ allowElseExpression: true
NullableToStringCall:
active: false
RedundantElseInWhen:
- excludes: ['**/resources/**']
active: true
UnconditionalJumpStatementInLoop:
- excludes: ['**/resources/**']
active: false
UnnecessaryNotNullOperator:
- excludes: ['**/resources/**']
- active: false
+ active: true
UnnecessarySafeCall:
- excludes: ['**/resources/**']
+ active: true
+ UnreachableCatchBlock:
active: false
UnreachableCode:
- excludes: ['**/resources/**']
active: true
UnsafeCallOnNullableType:
- excludes: ['**/resources/**']
active: true
UnsafeCast:
- excludes: ['**/resources/**']
+ active: true
+ UnusedUnaryOperator:
active: false
UselessPostfixExpression:
- excludes: ['**/resources/**']
active: false
WrongEqualsTypeParameter:
- excludes: ['**/resources/**']
active: true
style:
@@ -612,67 +549,58 @@ style:
ClassOrdering:
active: true
CollapsibleIfStatements:
- excludes: ['**/resources/**']
active: false
DataClassContainsFunctions:
- excludes: ['**/resources/**']
active: false
conversionFunctionPrefix: 'to'
DataClassShouldBeImmutable:
- excludes: ['**/resources/**']
active: false
+ DestructuringDeclarationWithTooManyEntries:
+ active: true
+ maxDestructuringEntries: 3
EqualsNullCall:
- excludes: ['**/resources/**']
active: true
EqualsOnSignatureLine:
- excludes: ['**/resources/**']
active: false
ExplicitCollectionElementAccessMethod:
- excludes: ['**/resources/**']
active: false
ExplicitItLambdaParameter:
- excludes: ['**/resources/**']
active: false
ExpressionBodySyntax:
- excludes: ['**/resources/**']
active: false
includeLineWrapping: false
ForbiddenComment:
- excludes: ['**/resources/**']
active: true
values: ['TODO:', 'STOPSHIP:']
allowedPatterns: ''
ForbiddenImport:
- excludes: ['**/resources/**']
active: false
imports: []
forbiddenPatterns: ''
ForbiddenMethodCall:
- excludes: ['**/resources/**']
active: false
methods: ['kotlin.io.println', 'kotlin.io.print']
ForbiddenPublicDataClass:
- excludes: ['**/resources/**']
- active: false
+ active: true
+ excludes: ['**']
ignorePackages: ['*.internal', '*.internal.*']
ForbiddenVoid:
- excludes: ['**/resources/**']
active: false
ignoreOverridden: false
ignoreUsageInGenerics: false
FunctionOnlyReturningConstant:
- excludes: ['**/resources/**']
active: true
ignoreOverridableFunction: true
+ ignoreActualFunction: true
excludedFunctions: 'describeContents'
excludeAnnotatedFunction: ['dagger.Provides']
LibraryCodeMustSpecifyReturnType:
- excludes: ['**/resources/**']
- active: false
+ active: true
+ excludes: ['**']
LibraryEntitiesShouldNotBePublic:
- active: false
+ active: true
+ excludes: ['**']
LoopWithTooManyJumpStatements:
- excludes: ['**/resources/**']
active: true
maxJumpCount: 1
MagicNumber:
@@ -688,59 +616,46 @@ style:
ignoreNamedArgument: true
ignoreEnums: false
ignoreRanges: false
+ ignoreExtensionFunctions: true
MandatoryBracesIfStatements:
- excludes: ['**/resources/**']
active: false
MandatoryBracesLoops:
- excludes: ['**/resources/**']
active: false
MaxLineLength:
- excludes: ['**/resources/**']
active: true
maxLineLength: 180
excludePackageStatements: true
excludeImportStatements: true
excludeCommentStatements: false
MayBeConst:
- excludes: ['**/resources/**']
active: true
ModifierOrder:
- excludes: ['**/resources/**']
active: true
- NestedClassesVisibility:
- excludes: ['**/resources/**']
+ MultilineLambdaItParameter:
active: false
+ NestedClassesVisibility:
+ active: true
NewLineAtEndOfFile:
- excludes: ['**/resources/**']
active: true
NoTabs:
- excludes: ['**/resources/**']
active: false
OptionalAbstractKeyword:
- excludes: ['**/resources/**']
active: true
OptionalUnit:
- excludes: ['**/resources/**']
active: false
OptionalWhenBraces:
- excludes: ['**/resources/**']
active: false
PreferToOverPairSyntax:
- excludes: ['**/resources/**']
active: false
ProtectedMemberInFinalClass:
- excludes: ['**/resources/**']
active: true
RedundantExplicitType:
- excludes: ['**/resources/**']
active: false
RedundantHigherOrderMapUsage:
active: true
RedundantVisibilityModifierRule:
- excludes: ['**/resources/**']
active: false
ReturnCount:
- excludes: ['**/resources/**']
active: false
max: 4
excludedFunctions: 'equals'
@@ -748,67 +663,50 @@ style:
excludeReturnFromLambda: true
excludeGuardClauses: false
SafeCast:
- excludes: ['**/resources/**']
active: true
SerialVersionUIDInSerializableClass:
- excludes: ['**/resources/**']
- active: false
+ active: true
SpacingBetweenPackageAndImports:
- excludes: ['**/resources/**']
active: false
ThrowsCount:
- excludes: ['**/resources/**']
active: true
max: 2
TrailingWhitespace:
- excludes: ['**/resources/**']
active: false
UnderscoresInNumericLiterals:
- excludes: ['**/resources/**']
active: false
acceptableDecimalLength: 5
UnnecessaryAbstractClass:
- excludes: ['**/resources/**']
active: true
excludeAnnotatedClasses: ['dagger.Module']
UnnecessaryAnnotationUseSiteTarget:
- excludes: ['**/resources/**']
active: false
UnnecessaryApply:
- excludes: ['**/resources/**']
+ active: true
+ UnnecessaryFilter:
active: false
UnnecessaryInheritance:
- excludes: ['**/resources/**']
active: true
UnnecessaryLet:
- excludes: ['**/resources/**']
active: false
UnnecessaryParentheses:
- excludes: ['**/resources/**']
active: false
UntilInsteadOfRangeTo:
- excludes: ['**/resources/**']
active: false
UnusedImports:
- excludes: ['**/resources/**']
active: false
UnusedPrivateClass:
- excludes: ['**/resources/**']
active: true
UnusedPrivateMember:
- excludes: ['**/resources/**']
active: false
allowedNames: '(_|ignored|expected|serialVersionUID)'
UseArrayLiteralsInAnnotations:
- excludes: ['**/resources/**']
active: false
UseCheckNotNull:
- active: true
+ active: false
UseCheckOrError:
- excludes: ['**/resources/**']
active: false
UseDataClass:
- excludes: ['**/resources/**']
active: false
excludeAnnotatedClasses: []
allowVars: false
@@ -817,22 +715,21 @@ style:
UseIfEmptyOrIfBlank:
active: true
UseIfInsteadOfWhen:
- excludes: ['**/resources/**']
+ active: false
+ UseIsNullOrEmpty:
+ active: true
+ UseOrEmpty:
active: false
UseRequire:
- excludes: ['**/resources/**']
active: false
UseRequireNotNull:
active: true
UselessCallOnNotNull:
- excludes: ['**/resources/**']
active: true
UtilityClassWithPublicConstructor:
- excludes: ['**/resources/**']
active: true
VarCouldBeVal:
- excludes: ['**/resources/**']
- active: false
+ active: true
WildcardImport:
active: true
excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
diff --git a/diktat-analysis.yml b/diktat-analysis.yml
index 3c93bfdfff..6ec429c6d7 100644
--- a/diktat-analysis.yml
+++ b/diktat-analysis.yml
@@ -8,6 +8,7 @@
disabledChapters: ""
testDirs: test
kotlinVersion: 1.4.30
+ srcDirectories: "main"
# Checks that the Class/Enum/Interface name does not match Pascal case
- name: CLASS_NAME_INCORRECT
enabled: true
@@ -489,4 +490,7 @@
enabled: true
# If file contains class, then it can't contain extension functions for the same class
- name: EXTENSION_FUNCTION_WITH_CLASS
+ enabled: true
+# Check if kts script contains other functions except run code
+- name: RUN_IN_SCRIPT
enabled: true
\ No newline at end of file
diff --git a/diktat-common/pom.xml b/diktat-common/pom.xml
index 9414be5f08..faa40164b7 100644
--- a/diktat-common/pom.xml
+++ b/diktat-common/pom.xml
@@ -9,7 +9,7 @@
org.cqfn.diktat
diktat-parent
- 0.4.1-SNAPSHOT
+ 0.4.3-SNAPSHOT
@@ -25,7 +25,7 @@
com.charleskorn.kaml
kaml
- 0.27.0
+ 0.28.3
commons-cli
diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/ApplicationProperties.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/ApplicationProperties.kt
index 6a9d5ec3a2..b0219179f6 100644
--- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/ApplicationProperties.kt
+++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/reader/ApplicationProperties.kt
@@ -10,6 +10,7 @@ import kotlin.system.exitProcess
/**
* Base class for working with properties files.
*/
+@Suppress("SwallowedException")
open class ApplicationProperties(propertiesFileName: String) {
/**
* The [Properties] loaded from a file.
diff --git a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt
index 771e063597..9cc8ac6f61 100644
--- a/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt
+++ b/diktat-common/src/main/kotlin/org/cqfn/diktat/common/config/rules/RulesConfigReader.kt
@@ -101,7 +101,11 @@ data class CommonConfiguration(private val configuration: Map?)
* List of directory names which will be used to detect test sources
*/
val testAnchors: List by lazy {
- (configuration ?: emptyMap()).getOrDefault("testDirs", "test").split(',')
+ val testDirs = (configuration ?: emptyMap()).getOrDefault("testDirs", "test").split(',')
+ if (testDirs.any { !it.toLowerCase().endsWith("test") }) {
+ log.error("test directory names should end with `test`")
+ }
+ testDirs
}
/**
@@ -130,6 +134,17 @@ data class CommonConfiguration(private val configuration: Map?)
}
}
+ /**
+ * Get source directories from configuration
+ */
+ val srcDirectories: List by lazy {
+ val srcDirs = configuration?.get("srcDirectories")?.split(",")?.map { it.trim() } ?: listOf("main")
+ if (srcDirs.any { !it.toLowerCase().endsWith("main") }) {
+ log.error("source directory names should end with `main`")
+ }
+ srcDirs
+ }
+
/**
* False if configuration has been read from config file, true if defaults are used
*/
diff --git a/diktat-gradle-plugin/build.gradle.kts b/diktat-gradle-plugin/build.gradle.kts
index daa17dcf97..a19e566b81 100644
--- a/diktat-gradle-plugin/build.gradle.kts
+++ b/diktat-gradle-plugin/build.gradle.kts
@@ -3,7 +3,7 @@ import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.getCurr
plugins {
`java-gradle-plugin`
- kotlin("jvm") version "1.4.30"
+ kotlin("jvm") version "1.4.31"
jacoco
id("pl.droidsonroids.jacoco.testkit") version "1.0.7"
}
@@ -61,6 +61,7 @@ tasks.withType {
languageVersion = "1.3"
apiVersion = "1.3"
jvmTarget = "1.8"
+ useIR = true
}
dependsOn.add(generateVersionsFile)
@@ -98,6 +99,7 @@ tasks.getByName("functionalTest") {
testClassesDirs = functionalTest.output.classesDirs
classpath = functionalTest.runtimeClasspath
maxParallelForks = Runtime.getRuntime().availableProcessors()
+ maxHeapSize = "1024m"
doLast {
if (getCurrentOperatingSystem().isWindows) {
// workaround for https://github.com/koral--/jacoco-gradle-testkit-plugin/issues/9
diff --git a/diktat-gradle-plugin/gradle-plugin-marker/pom.xml b/diktat-gradle-plugin/gradle-plugin-marker/pom.xml
index 15d3e32309..b0d110d91d 100644
--- a/diktat-gradle-plugin/gradle-plugin-marker/pom.xml
+++ b/diktat-gradle-plugin/gradle-plugin-marker/pom.xml
@@ -4,7 +4,7 @@
diktat-gradle-plugin
org.cqfn.diktat
- 0.4.1-SNAPSHOT
+ 0.4.3-SNAPSHOT
4.0.0
diff --git a/diktat-gradle-plugin/pom.xml b/diktat-gradle-plugin/pom.xml
index d49c4baff5..1ea559437c 100644
--- a/diktat-gradle-plugin/pom.xml
+++ b/diktat-gradle-plugin/pom.xml
@@ -5,7 +5,7 @@
diktat-parent
org.cqfn.diktat
- 0.4.1-SNAPSHOT
+ 0.4.3-SNAPSHOT
4.0.0
diff --git a/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt b/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
index f244e2e035..2c23d98bc9 100644
--- a/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
+++ b/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
@@ -41,7 +41,7 @@ internal fun runDiktat(testProjectDir: TemporaryFolder,
) = GradleRunner.create()
.run(configureRunner)
.withProjectDir(testProjectDir.root)
- .withArguments(*arguments.toTypedArray(), DiktatGradlePlugin.DIKTAT_CHECK_TASK)
+ .withArguments(arguments + DiktatGradlePlugin.DIKTAT_CHECK_TASK)
.withPluginClasspath()
.withJaCoCo(testsCounter.incrementAndGet())
.forwardOutput()
diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
index b705d03d0b..ac809fff15 100644
--- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
+++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt
@@ -144,7 +144,7 @@ open class DiktatJavaExecTaskBase @Inject constructor(
}
} else {
flag.append("--reporter=plain")
- log.warn("Unknown reporter was specified. Falling back to plain reporter.")
+ log.debug("Unknown reporter was specified. Falling back to plain reporter.")
}
}
diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
index 58ee144069..20bda86b63 100644
--- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
+++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/Utils.kt
@@ -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(
val function: T.() -> V?,
owner: Any? = null,
@@ -21,6 +27,11 @@ class KotlinClosure1(
// 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 Any.closureOf(action: T.() -> Unit): Closure =
KotlinClosure1(action, this, this)
diff --git a/diktat-maven-plugin/pom.xml b/diktat-maven-plugin/pom.xml
index e97937871a..b6082a3ae7 100644
--- a/diktat-maven-plugin/pom.xml
+++ b/diktat-maven-plugin/pom.xml
@@ -5,7 +5,7 @@
diktat-parent
org.cqfn.diktat
- 0.4.1-SNAPSHOT
+ 0.4.3-SNAPSHOT
4.0.0
diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml
index 92df63b1a1..bb753aa0e7 100644
--- a/diktat-rules/pom.xml
+++ b/diktat-rules/pom.xml
@@ -9,7 +9,7 @@
org.cqfn.diktat
diktat-parent
- 0.4.1-SNAPSHOT
+ 0.4.3-SNAPSHOT
diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
index fdfc9db868..5e7861546d 100644
--- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt
+++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
@@ -5,6 +5,8 @@ package generated
import kotlin.String
public object WarningNames {
+ public const val DUMMY_TEST_WARNING: String = "DUMMY_TEST_WARNING"
+
public const val PACKAGE_NAME_MISSING: String = "PACKAGE_NAME_MISSING"
public const val PACKAGE_NAME_INCORRECT_CASE: String = "PACKAGE_NAME_INCORRECT_CASE"
@@ -251,4 +253,6 @@ public object WarningNames {
public const val INLINE_CLASS_CAN_BE_USED: String = "INLINE_CLASS_CAN_BE_USED"
public const val EXTENSION_FUNCTION_WITH_CLASS: String = "EXTENSION_FUNCTION_WITH_CLASS"
+
+ public const val RUN_IN_SCRIPT: String = "RUN_IN_SCRIPT"
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt
index dd3fb7a7ac..52031ff781 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt
@@ -6,7 +6,9 @@ import org.cqfn.diktat.common.config.rules.isRuleEnabled
import org.cqfn.diktat.ruleset.utils.hasSuppress
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
-typealias EmitType = ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit)
+typealias EmitType = ((offset: Int,
+ errorMessage: String,
+ canBeAutoCorrected: Boolean) -> Unit)
typealias ListOfList = MutableList>
@@ -18,11 +20,20 @@ typealias ListOfPairs = MutableList>
* @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,
private val warn: String) : Rule {
+ // ======== dummy test warning ======
+ DUMMY_TEST_WARNING(true, "0.0.0", "this is a dummy warning that can be used for manual testing of fixer/checker"),
+
// ======== chapter 1 ========
PACKAGE_NAME_MISSING(true, "1.2.1", "no package name declared in a file"),
PACKAGE_NAME_INCORRECT_CASE(true, "1.2.1", "package name should be completely in a lower case"),
@@ -104,7 +115,7 @@ enum class Warnings(
COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression should be replaced with variable"),
// FixMe: autofixing will be added for this rule
- STRING_CONCATENATION(false, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"),
+ STRING_CONCATENATION(true, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"),
TOO_MANY_BLANK_LINES(true, "3.7.1", "too many consecutive blank lines"),
WRONG_WHITESPACE(true, "3.8.1", "incorrect usage of whitespaces for code separation"),
TOO_MANY_CONSECUTIVE_SPACES(true, "3.8.1", "too many consecutive spaces"),
@@ -160,6 +171,7 @@ enum class Warnings(
OBJECT_IS_PREFERRED(true, "6.4.2", "it is better to use object for stateless classes"),
INLINE_CLASS_CAN_BE_USED(true, "6.1.12", "inline class can be used"),
EXTENSION_FUNCTION_WITH_CLASS(false, "6.2.3", "do not use extension functions for the class defined in the same file"),
+ RUN_IN_SCRIPT(true, "6.5.1", "wrap blocks of code in top-level scope functions like `run`"),
;
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/dummy/DummyWarning.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/dummy/DummyWarning.kt
new file mode 100644
index 0000000000..d7bb77507b
--- /dev/null
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/dummy/DummyWarning.kt
@@ -0,0 +1,24 @@
+package org.cqfn.diktat.ruleset.dummy
+
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.Warnings
+import org.cqfn.diktat.ruleset.rules.DiktatRule
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+
+/**
+ * Dummy warning used for testing and debug purposes.
+ * Can be used in manual testing.
+ */
+class DummyWarning(configRules: List) : DiktatRule(
+ "dummy-rule",
+ configRules,
+ listOf(
+ Warnings.FILE_NAME_INCORRECT,
+ Warnings.FILE_NAME_MATCH_CLASS
+ )
+) {
+ private lateinit var filePath: String
+
+ @Suppress("EmptyFunctionBlock")
+ override fun logic(node: ASTNode) {}
+}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
index 56bd730e75..f97829a82d 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
@@ -4,6 +4,7 @@ import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.RulesConfigReader
import org.cqfn.diktat.ruleset.constants.Warnings
+import org.cqfn.diktat.ruleset.dummy.DummyWarning
import org.cqfn.diktat.ruleset.rules.chapter1.FileNaming
import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming
import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming
@@ -62,6 +63,7 @@ import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsInFileRule
import org.cqfn.diktat.ruleset.rules.chapter6.ExtensionFunctionsSameNameRule
import org.cqfn.diktat.ruleset.rules.chapter6.ImplicitBackingPropertyRule
import org.cqfn.diktat.ruleset.rules.chapter6.PropertyAccessorFields
+import org.cqfn.diktat.ruleset.rules.chapter6.RunInScript
import org.cqfn.diktat.ruleset.rules.chapter6.TrivialPropertyAccessors
import org.cqfn.diktat.ruleset.rules.chapter6.UselessSupertype
import org.cqfn.diktat.ruleset.rules.chapter6.classes.AbstractClassesRule
@@ -103,7 +105,10 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
it?.takeIf { File(it).exists() }
}
- @Suppress("LongMethod", "TOO_LONG_FUNCTION")
+ @Suppress(
+ "LongMethod",
+ "TOO_LONG_FUNCTION",
+ "SpreadOperator")
override fun get(): RuleSet {
log.debug("Will run $DIKTAT_RULE_SET_ID with $diktatConfigFile" +
" (it can be placed to the run directory or the default file from resources will be used)")
@@ -133,6 +138,9 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
// We don't have a way to enforce a specific order, so we should just be careful when adding new rules to this list and, when possible,
// cover new rules in smoke test as well. If a rule needs to be at a specific position in a list, please add comment explaining it (like for NewlinesRule).
val rules = listOf(
+ // test warning that can be used for manual testing of diktat
+ ::DummyWarning,
+
// comments & documentation
::CommentsRule,
::SingleConstructorRule, // this rule can add properties to a primary constructor, so should be before KdocComments
@@ -164,7 +172,6 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::CheckInverseMethodRule,
::StatelessClassesRule,
::ImplicitBackingPropertyRule,
- ::StringTemplateFormatRule,
::DataClassesRule,
::LocalVariablesRule,
::SmartCastRule,
@@ -180,8 +187,10 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::AnnotationNewLineRule,
::SortRule,
::StringConcatenationRule,
+ ::StringTemplateFormatRule,
::AccurateCalculationsRule,
::LineLength,
+ ::RunInScript,
::TypeAliasRule,
::OverloadingArgumentsFunction,
::FunctionLength,
@@ -204,7 +213,8 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::FileStructureRule, // this rule should be right before indentation because it should operate on already valid code
::NewlinesRule, // newlines need to be inserted right before fixing indentation
::WhiteSpaceRule, // this rule should be after other rules that can cause wrong spacing
- ::IndentationRule // indentation rule should be the last because it fixes formatting after all the changes done by previous rules
+ ::IndentationRule, // indentation rule should be the last because it fixes formatting after all the changes done by previous rules
+
)
.map {
it.invoke(configRules)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/FileNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/FileNaming.kt
index 26fc069de3..8c89d666d8 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/FileNaming.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/FileNaming.kt
@@ -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) : DiktatRule("file-naming", configRules, listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)) {
+class FileNaming(configRules: List) : DiktatRule(
+ "file-naming",
+ configRules,
+ listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)) {
private lateinit var filePath: String
override fun logic(node: ASTNode) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt
index f80c6a6561..36831d91b4 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/IdentifierNaming.kt
@@ -79,13 +79,15 @@ 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) : DiktatRule("identifier-naming", configRules,
+class IdentifierNaming(configRules: List) : 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,
IDENTIFIER_LENGTH, OBJECT_NAME_INCORRECT)) {
private val allMethodPrefixes by lazy {
- if (configuration.allowedBooleanPrefixes.isNullOrEmpty()) {
+ if (configuration.allowedBooleanPrefixes.isEmpty()) {
booleanMethodPrefixes
} else {
booleanMethodPrefixes + configuration.allowedBooleanPrefixes.filter { it.isNotEmpty() }
@@ -148,7 +150,11 @@ class IdentifierNaming(configRules: List) : 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 {
// special case for Destructuring declarations that can be treated as parameters in lambda:
var namesOfVariables = extractVariableIdentifiers(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/PackageNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/PackageNaming.kt
index 78b0c08dc8..6e22883fbd 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/PackageNaming.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter1/PackageNaming.kt
@@ -1,5 +1,6 @@
package org.cqfn.diktat.ruleset.rules.chapter1
+import org.cqfn.diktat.common.config.rules.CommonConfiguration
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getCommonConfiguration
import org.cqfn.diktat.ruleset.constants.Warnings.INCORRECT_PACKAGE_SEPARATOR
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.lexer.KtTokens.PACKAGE_KEYWORD
import org.slf4j.LoggerFactory
+
import java.util.concurrent.atomic.AtomicInteger
/**
@@ -33,7 +35,9 @@ 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) : DiktatRule("package-naming", configRules,
+class PackageNaming(configRules: List) : 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
@@ -45,7 +49,7 @@ class PackageNaming(configRules: List) : DiktatRule("package-naming
if (node.elementType == PACKAGE_DIRECTIVE) {
val filePath = node.getRootNode().getFilePath()
// calculating package name based on the directory where the file is placed
- val realPackageName = calculateRealPackageName(filePath)
+ val realPackageName = calculateRealPackageName(filePath, configuration)
// if node isLeaf - this means that there is no package name declared
if (node.isLeaf() && !filePath.isKotlinScript()) {
@@ -54,7 +58,7 @@ class PackageNaming(configRules: List) : DiktatRule("package-naming
}
// getting all identifiers from existing package name into the list like [org, diktat, project]
- val wordsInPackageName = node.findAllNodesWithSpecificType(IDENTIFIER)
+ val wordsInPackageName = node.findAllDescendantsWithSpecificType(IDENTIFIER)
// no need to check that packageIdentifiers is empty, because in this case parsing will fail
checkPackageName(wordsInPackageName, node)
@@ -90,7 +94,7 @@ class PackageNaming(configRules: List) : DiktatRule("package-naming
*
* @return list with words that are parts of package name like [org, diktat, name]
*/
- private fun calculateRealPackageName(fileName: String): List {
+ private fun calculateRealPackageName(fileName: String, configuration: CommonConfiguration): List {
val filePathParts = fileName.splitPathToDirs()
return if (!filePathParts.contains(PACKAGE_PATH_ANCHOR)) {
@@ -102,8 +106,9 @@ class PackageNaming(configRules: List) : DiktatRule("package-naming
// 1) getting a path after the base project directory (after "src" directory)
// 2) removing src/main/kotlin/java/e.t.c dirs and removing file name
// 3) adding company's domain name at the beginning
+ val allDirs = languageDirNames + configuration.srcDirectories + configuration.testAnchors
val fileSubDir = filePathParts.subList(filePathParts.lastIndexOf(PACKAGE_PATH_ANCHOR), filePathParts.size - 1)
- .dropWhile { languageDirNames.contains(it) }
+ .dropWhile { allDirs.contains(it) }
// no need to add DOMAIN_NAME to the package name if it is already in path
val domainPrefix = if (!fileSubDir.joinToString(PACKAGE_SEPARATOR).startsWith(domainName)) domainName.split(PACKAGE_SEPARATOR) else emptyList()
domainPrefix + fileSubDir
@@ -257,6 +262,6 @@ class PackageNaming(configRules: List) : DiktatRule("package-naming
* Directories that are supposed to be first in sources file paths, relative to [PACKAGE_PATH_ANCHOR].
* For kotlin multiplatform projects directories for targets from [kmmTargets] are supported.
*/
- val languageDirNames = listOf("src", "main", "test", "java", "kotlin") + kmmTargets.flatMap { listOf("${it}Main", "${it}Test") }
+ val languageDirNames = listOf("src", "java", "kotlin") + kmmTargets.flatMap { listOf("${it}Main", "${it}Test") }
}
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/CommentsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/CommentsRule.kt
index 38d860bcbd..1029eb5330 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/CommentsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/CommentsRule.kt
@@ -4,7 +4,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.ListOfPairs
import org.cqfn.diktat.ruleset.constants.Warnings.COMMENTED_OUT_CODE
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT
@@ -23,7 +23,10 @@ import org.jetbrains.kotlin.resolve.ImportPath
* No commented out code is allowed, including imports.
*/
@Suppress("ForbiddenComment")
-class CommentsRule(configRules: List) : DiktatRule("comments", configRules, listOf(COMMENTED_OUT_CODE)) {
+class CommentsRule(configRules: List) : DiktatRule(
+ "comments",
+ configRules,
+ listOf(COMMENTED_OUT_CODE)) {
private lateinit var ktPsiFactory: KtPsiFactory
override fun logic(node: ASTNode) {
@@ -49,7 +52,7 @@ class CommentsRule(configRules: List) : DiktatRule("comments", conf
val errorNodesWithText: ListOfPairs = mutableListOf()
val eolCommentsOffsetToText = getOffsetsToTextBlocksFromEolComments(node, errorNodesWithText)
val blockCommentsOffsetToText = node
- .findAllNodesWithSpecificType(BLOCK_COMMENT)
+ .findAllDescendantsWithSpecificType(BLOCK_COMMENT)
.map {
errorNodesWithText.add(it to it.text.trim().removeSurrounding("/*", "*/"))
it.startOffset to it.text.trim().removeSurrounding("/*", "*/")
@@ -77,7 +80,7 @@ class CommentsRule(configRules: List) : DiktatRule("comments", conf
}
.filter { (_, parsedNode) ->
parsedNode
- .findAllNodesWithSpecificType(TokenType.ERROR_ELEMENT)
+ .findAllDescendantsWithSpecificType(TokenType.ERROR_ELEMENT)
.isEmpty()
}
.forEach { (offset, parsedNode) ->
@@ -94,7 +97,7 @@ class CommentsRule(configRules: List) : DiktatRule("comments", conf
*/
private fun getOffsetsToTextBlocksFromEolComments(node: ASTNode, errorNodesWithText: ListOfPairs): List> {
val comments = node
- .findAllNodesWithSpecificType(EOL_COMMENT)
+ .findAllDescendantsWithSpecificType(EOL_COMMENT)
.filter { !it.text.contains(eolCommentStart) || isCodeAfterCommentStart(it.text) }
return if (comments.isNotEmpty()) {
val result = mutableListOf(mutableListOf(comments.first()))
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
index 05c5a95469..0b7541f012 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
@@ -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) : DiktatRule("header-comment", configRules,
+class HeaderCommentRule(configRules: List) : 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) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/CommentsFormatting.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/CommentsFormatting.kt
index 97599a127e..0f3192a298 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/CommentsFormatting.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/CommentsFormatting.kt
@@ -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) : DiktatRule("kdoc-comments-codeblocks-formatting", configRules,
+class CommentsFormatting(configRules: List) : DiktatRule(
+ "kdoc-comments-codeblocks-formatting",
+ configRules,
listOf(COMMENT_WHITE_SPACE, FIRST_COMMENT_NO_BLANK_LINE,
IF_ELSE_COMMENTS, WRONG_NEWLINES_AROUND_KDOC)) {
/**
@@ -268,10 +270,10 @@ class CommentsFormatting(configRules: List) : DiktatRule("kdoc-comm
EOL_COMMENT -> (node as LeafPsiElement).replaceWithText("// $commentText")
BLOCK_COMMENT -> (node as LeafPsiElement).replaceWithText("/* $commentText")
KDOC -> {
- node.findAllNodesWithSpecificType(KDOC_TEXT).forEach {
+ node.findAllDescendantsWithSpecificType(KDOC_TEXT).forEach {
modifyKdocText(it, configuration)
}
- node.findAllNodesWithSpecificType(KDOC_CODE_BLOCK_TEXT).forEach {
+ node.findAllDescendantsWithSpecificType(KDOC_CODE_BLOCK_TEXT).forEach {
modifyKdocText(it, configuration)
}
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt
index d6cd89709c..96842d2c7f 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt
@@ -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) : DiktatRule("kdoc-comments", configRules,
+class KdocComments(configRules: List) : 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)) {
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt
index 5fd4df686b..5d85013673 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt
@@ -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) : DiktatRule("kdoc-formatting", configRules,
+class KdocFormatting(configRules: List) : 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)) {
@@ -168,7 +170,7 @@ class KdocFormatting(configRules: List) : DiktatRule("kdoc-formatti
}
)
- @Suppress("UnsafeCallOnNullableType")
+ @Suppress("UnsafeCallOnNullableType", "TOO_LONG_FUNCTION")
private fun checkBasicTagsOrder(node: ASTNode) {
val kdocTags = node.kDocTags()
// distinct basic tags which are present in current KDoc, in proper order
@@ -200,10 +202,13 @@ class KdocFormatting(configRules: List) : DiktatRule("kdoc-formatti
.filter { basicTagsOrdered.contains(it.knownTag) }
.map { it.node }
- basicTagsOrdered.forEachIndexed { index, tag ->
- val tagNode = kdocTags.find { it.knownTag == tag }!!.node
- kdocSection.addChild(tagNode.clone() as CompositeElement, basicTagChildren[index])
- kdocSection.removeChild(basicTagChildren[index])
+ val correctKdocOrder = basicTags
+ .sortedBy { basicTagsOrdered.indexOf(it.knownTag) }
+ .map { it.node }
+
+ basicTagChildren.mapIndexed { index, astNode ->
+ kdocSection.addChild(correctKdocOrder[index].clone() as CompositeElement, astNode)
+ kdocSection.removeChild(astNode)
}
}
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
index a0fd9878d3..fda5c905e0 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
@@ -10,7 +10,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTION
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.KotlinParser
import org.cqfn.diktat.ruleset.utils.appendNewlineMergingWhiteSpace
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.findChildAfter
import org.cqfn.diktat.ruleset.utils.getBodyLines
import org.cqfn.diktat.ruleset.utils.getFilePath
@@ -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) : DiktatRule("kdoc-methods", configRules,
+class KdocMethods(configRules: List) : 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)) {
/**
@@ -153,7 +155,7 @@ class KdocMethods(configRules: List) : DiktatRule("kdoc-methods", c
private fun getExplicitlyThrownExceptions(node: ASTNode): Set {
val codeBlock = node.getFirstChildWithType(BLOCK)
- val throwKeywords = codeBlock?.findAllNodesWithSpecificType(THROW)
+ val throwKeywords = codeBlock?.findAllDescendantsWithSpecificType(THROW)
return throwKeywords
?.map { it.psi as KtThrowExpression }
?.mapNotNull { it.thrownExpression?.referenceExpression()?.text }
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/AnnotationNewLineRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/AnnotationNewLineRule.kt
index dbd1f16b1a..ec025d6f15 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/AnnotationNewLineRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/AnnotationNewLineRule.kt
@@ -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) : DiktatRule("annotation-new-line", configRules, listOf(ANNOTATION_NEW_LINE)) {
+class AnnotationNewLineRule(configRules: List) : 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)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BlockStructureBraces.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BlockStructureBraces.kt
index fd1b254786..a2b5b43144 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BlockStructureBraces.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BlockStructureBraces.kt
@@ -7,6 +7,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.BRACES_BLOCK_STRUCTURE_ERROR
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.*
+import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.BODY
import com.pinterest.ktlint.core.ast.ElementType.CATCH
@@ -49,7 +50,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) : DiktatRule("block-structure", configRules, listOf(BRACES_BLOCK_STRUCTURE_ERROR)) {
+class BlockStructureBraces(configRules: List) : 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()
@@ -88,7 +92,7 @@ class BlockStructureBraces(configRules: List) : DiktatRule("block-s
val catchBlocks = tryBlock.catchClauses.map { it.node }
val finallyBlock = tryBlock.finallyBlock?.node
checkOpenBraceOnSameLine(tryBlock.node, BLOCK, configuration)
- val allMiddleSpaceNodes = node.findAllNodesWithSpecificType(CATCH).map { it.treePrev }
+ val allMiddleSpaceNodes = node.findAllDescendantsWithSpecificType(CATCH).map { it.treePrev }
checkMidBrace(allMiddleSpaceNodes, node, CATCH_KEYWORD)
catchBlocks.forEach {
checkOpenBraceOnSameLine(it, BLOCK, configuration)
@@ -97,7 +101,7 @@ class BlockStructureBraces(configRules: List) : DiktatRule("block-s
finallyBlock?.let { block ->
checkOpenBraceOnSameLine(block, BLOCK, configuration)
checkCloseBrace(block.findChildByType(BLOCK)!!, configuration)
- val newAllMiddleSpaceNodes = node.findAllNodesWithSpecificType(FINALLY).map { it.treePrev }
+ val newAllMiddleSpaceNodes = node.findAllDescendantsWithSpecificType(FINALLY).map { it.treePrev }
checkMidBrace(newAllMiddleSpaceNodes, node, FINALLY_KEYWORD)
}
}
@@ -230,6 +234,11 @@ class BlockStructureBraces(configRules: List) : DiktatRule("block-s
return
}
val space = node.findChildByType(RBRACE)!!.treePrev
+ node.findParentNodeWithSpecificType(ElementType.LAMBDA_ARGUMENT)?.let {
+ if (space.text.isEmpty()) {
+ return
+ }
+ }
if (checkBraceNode(space)) {
BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "no newline before closing brace",
(space.treeNext ?: node.findChildByType(RBRACE))!!.startOffset, node) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
index d03b9a7dab..40ce08799a 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
@@ -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) : DiktatRule("braces-rule", configRules, listOf(NO_BRACES_IN_CONDITIONALS_AND_LOOPS)) {
+class BracesInConditionalsAndLoopsRule(configRules: List) : DiktatRule(
+ "braces-rule",
+ configRules,
+ listOf(NO_BRACES_IN_CONDITIONALS_AND_LOOPS)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
IF -> checkIfNode(node)
@@ -47,7 +50,12 @@ class BracesInConditionalsAndLoopsRule(configRules: List) : 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
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ClassLikeStructuresOrderRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ClassLikeStructuresOrderRule.kt
index d3ef6941cc..12a6d7ba4f 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ClassLikeStructuresOrderRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ClassLikeStructuresOrderRule.kt
@@ -41,7 +41,9 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
/**
* Rule that checks order of declarations inside classes, interfaces and objects.
*/
-class ClassLikeStructuresOrderRule(configRules: List) : DiktatRule("class-like-structures", configRules,
+class ClassLikeStructuresOrderRule(configRules: List) : DiktatRule(
+ "class-like-structures",
+ configRules,
listOf(BLANK_LINE_BETWEEN_PROPERTIES, WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS_BODY) {
@@ -51,7 +53,12 @@ class ClassLikeStructuresOrderRule(configRules: List) : DiktatRule(
}
}
- @Suppress("UnsafeCallOnNullableType", "LongMethod", "ComplexMethod", "TOO_LONG_FUNCTION")
+ @Suppress(
+ "UnsafeCallOnNullableType",
+ "LongMethod",
+ "ComplexMethod",
+ "TOO_LONG_FUNCTION"
+ )
private fun checkDeclarationsOrderInClass(node: ASTNode) {
val allProperties = node.getChildren(TokenSet.create(PROPERTY))
val constProperties = allProperties.filter { it.findLeafWithSpecificType(CONST_KEYWORD) != null }.toMutableList()
@@ -73,7 +80,7 @@ class ClassLikeStructuresOrderRule(configRules: List) : DiktatRule(
node
.parents()
.last()
- .findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ .findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
.any { ref ->
ref.parent({ it == classNode }) == null && ref.text.contains(identifierNode.text)
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ConsecutiveSpacesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ConsecutiveSpacesRule.kt
index caac371b96..73411dc604 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ConsecutiveSpacesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/ConsecutiveSpacesRule.kt
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement
* 2) If saveInitialFormattingForEnums is true then white spaces in enums will not be affected
*
*/
-class ConsecutiveSpacesRule(configRules: List) : DiktatRule("too-many-spaces", configRules, listOf(TOO_MANY_CONSECUTIVE_SPACES)) {
+class ConsecutiveSpacesRule(configRules: List) : DiktatRule(
+ "too-many-spaces",
+ configRules,
+ listOf(TOO_MANY_CONSECUTIVE_SPACES)) {
override fun logic(node: ASTNode) {
val configuration = TooManySpacesRuleConfiguration(
configRules.getRuleConfig(TOO_MANY_CONSECUTIVE_SPACES)?.configuration ?: emptyMap())
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EmptyBlock.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EmptyBlock.kt
index 4525e11ea7..8a1dd69960 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EmptyBlock.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EmptyBlock.kt
@@ -5,27 +5,33 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.Warnings.EMPTY_BLOCK_STRUCTURE_ERROR
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findLBrace
-import org.cqfn.diktat.ruleset.utils.findLeafWithSpecificType
-import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType
-import org.cqfn.diktat.ruleset.utils.hasParent
-import org.cqfn.diktat.ruleset.utils.isBlockEmpty
-import org.cqfn.diktat.ruleset.utils.isOverridden
-import org.cqfn.diktat.ruleset.utils.isPascalCase
+import org.cqfn.diktat.ruleset.utils.*
+import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.FILE
import com.pinterest.ktlint.core.ast.ElementType.FUNCTION_LITERAL
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
+import com.pinterest.ktlint.core.ast.ElementType.LAMBDA_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.LPAR
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
+import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* Rule that checks if empty code blocks (`{ }`) are used and checks their formatting.
*/
-class EmptyBlock(configRules: List) : DiktatRule("empty-block-structure", configRules, listOf(EMPTY_BLOCK_STRUCTURE_ERROR)) {
+class EmptyBlock(configRules: List) : DiktatRule(
+ "empty-block-structure",
+ configRules,
+ listOf(EMPTY_BLOCK_STRUCTURE_ERROR)) {
override fun logic(node: ASTNode) {
val configuration = EmptyBlockStyleConfiguration(
configRules.getRuleConfig(EMPTY_BLOCK_STRUCTURE_ERROR)?.configuration ?: emptyMap()
@@ -38,9 +44,12 @@ class EmptyBlock(configRules: List) : DiktatRule("empty-block-struc
checkEmptyBlock(newNode, configuration)
}
- @Suppress("UnsafeCallOnNullableType")
+ private fun isNewLine(node: ASTNode) =
+ node.findChildByType(WHITE_SPACE)?.text?.contains("\n") ?: false
+
+ @Suppress("UnsafeCallOnNullableType", "TOO_LONG_FUNCTION")
private fun checkEmptyBlock(node: ASTNode, configuration: EmptyBlockStyleConfiguration) {
- if (node.treeParent.isOverridden() || isAnonymousSamClass(node)) {
+ if (node.treeParent.isOverridden() || isAnonymousSamClass(node) || isLambdaUsedAsFunction(node)) {
return
}
if (node.isBlockEmpty()) {
@@ -48,6 +57,19 @@ class EmptyBlock(configRules: List) : DiktatRule("empty-block-struc
EMPTY_BLOCK_STRUCTURE_ERROR.warn(configRules, emitWarn, isFixMode, "empty blocks are forbidden unless it is function with override keyword",
node.startOffset, node)
} else {
+ node.findParentNodeWithSpecificType(ElementType.LAMBDA_ARGUMENT)?.let {
+ // Lambda body is always has a BLOCK -> run { } - (LBRACE, WHITE_SPACE, BLOCK "", RBRACE)
+ if (isNewLine(node)) {
+ val freeText = "do not put newlines in empty lambda"
+ EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) {
+ val whiteSpaceNode = node.findChildByType(WHITE_SPACE)
+ whiteSpaceNode?.let {
+ node.replaceChild(whiteSpaceNode, PsiWhiteSpaceImpl(" "))
+ }
+ }
+ }
+ return
+ }
val space = node.findChildByType(RBRACE)!!.treePrev
if (configuration.emptyBlockNewline && !space.text.contains("\n")) {
EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block",
@@ -68,11 +90,11 @@ class EmptyBlock(configRules: List) : DiktatRule("empty-block-struc
}
}
- @Suppress("UnsafeCallOnNullableType", "WRONG_WHITESPACE")
- private fun isAnonymousSamClass(node: ASTNode) : Boolean =
+ @Suppress("UnsafeCallOnNullableType")
+ private fun isAnonymousSamClass(node: ASTNode): Boolean =
if (node.elementType == FUNCTION_LITERAL && node.hasParent(CALL_EXPRESSION)) {
- // We are checking identifier because it is not class in AST
- // , SAM conversions are indistinguishable from lambdas.
+ // We are checking identifier because it is not class in AST,
+ // SAM conversions are indistinguishable from lambdas.
// So we just verify that identifier is in PascalCase
val valueArgument = node.findParentNodeWithSpecificType(CALL_EXPRESSION)!!
valueArgument.findLeafWithSpecificType(IDENTIFIER)?.text?.isPascalCase() ?: false
@@ -80,6 +102,25 @@ class EmptyBlock(configRules: List) : DiktatRule("empty-block-struc
false
}
+ @Suppress("UnsafeCallOnNullableType")
+ private fun isLambdaUsedAsFunction(node: ASTNode): Boolean {
+ val parents = node.parents()
+ return when {
+ parents.any { it.elementType == CALL_EXPRESSION } -> {
+ val callExpression = parents.find { it.elementType == CALL_EXPRESSION }!!
+ // excepting cases like list.map { }. In this case call expression will not have value argument list
+ // And in this case: Parser.parse({}, some, thing) it will have value argument list
+ callExpression.hasChildOfType(VALUE_ARGUMENT_LIST)
+ }
+ parents.any { it.elementType == LAMBDA_EXPRESSION } -> {
+ val lambdaExpression = parents.find { it.elementType == LAMBDA_EXPRESSION }!!
+ // cases like A({}). Here Lambda expression is used as a value parameter.
+ lambdaExpression.treeParent.elementType == VALUE_PARAMETER
+ }
+ else -> false
+ }
+ }
+
/**
* [RuleConfiguration] for empty blocks formatting
*/
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt
index cbe6e1c8e9..fe607214c4 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/EnumsSeparated.kt
@@ -26,7 +26,10 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
/**
* Rule that checks enum classes formatting
*/
-class EnumsSeparated(configRules: List) : DiktatRule("enum-separated", configRules, listOf(ENUMS_SEPARATED)) {
+class EnumsSeparated(configRules: List) : DiktatRule(
+ "enum-separated",
+ configRules,
+ listOf(ENUMS_SEPARATED)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS && node.hasChildOfType(CLASS_BODY)) {
if (node.isClassEnum()) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
index 777ab27ba4..f35eff56be 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
@@ -57,7 +57,10 @@ import java.net.URL
* Rule can fix long binary expressions in condition inside `if` and in property declarations and one line functions
*/
@Suppress("ForbiddenComment")
-class LineLength(configRules: List) : DiktatRule("line-length", configRules, listOf(LONG_LINE)) {
+class LineLength(configRules: List) : DiktatRule(
+ "line-length",
+ configRules,
+ listOf(LONG_LINE)) {
private lateinit var positionByOffset: (Int) -> Pair
override fun logic(node: ASTNode) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LongNumericalValuesSeparatedRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LongNumericalValuesSeparatedRule.kt
index 93da60e617..556b79cff2 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LongNumericalValuesSeparatedRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LongNumericalValuesSeparatedRule.kt
@@ -16,7 +16,10 @@ import java.lang.StringBuilder
/**
* Rule that checks if numerical separators (`_`) are used for long numerical literals
*/
-class LongNumericalValuesSeparatedRule(configRules: List) : DiktatRule("long-numerical-values", configRules, listOf(LONG_NUMERICAL_VALUES_SEPARATED)) {
+class LongNumericalValuesSeparatedRule(configRules: List) : DiktatRule(
+ "long-numerical-values",
+ configRules,
+ listOf(LONG_NUMERICAL_VALUES_SEPARATED)) {
override fun logic(node: ASTNode) {
val configuration = LongNumericalValuesConfiguration(
configRules.getRuleConfig(LONG_NUMERICAL_VALUES_SEPARATED)?.configuration ?: emptyMap())
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MultipleModifiersSequence.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MultipleModifiersSequence.kt
index 113c2b9077..a326996341 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MultipleModifiersSequence.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/MultipleModifiersSequence.kt
@@ -15,7 +15,10 @@ import org.jetbrains.kotlin.psi.psiUtil.children
/**
* @property configRules
*/
-class MultipleModifiersSequence(configRules: List) : DiktatRule("multiple-modifiers", configRules, listOf(WRONG_MULTIPLE_MODIFIERS_ORDER)) {
+class MultipleModifiersSequence(configRules: List) : DiktatRule(
+ "multiple-modifiers",
+ configRules,
+ listOf(WRONG_MULTIPLE_MODIFIERS_ORDER)) {
override fun logic(node: ASTNode) {
if (node.elementType == MODIFIER_LIST) {
checkModifierList(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/NullableTypeRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/NullableTypeRule.kt
index 8215f3b5a5..7d0bb0e8fb 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/NullableTypeRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/NullableTypeRule.kt
@@ -4,7 +4,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.NULLABLE_PROPERTY_TYPE
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.KotlinParser
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.hasChildOfType
import com.pinterest.ktlint.core.ast.ElementType.BOOLEAN_CONSTANT
@@ -37,7 +37,10 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType
/**
* Rule that checks if nullable types are used and suggest to substitute them with non-nullable
*/
-class NullableTypeRule(configRules: List) : DiktatRule("nullable-type", configRules, listOf(NULLABLE_PROPERTY_TYPE)) {
+class NullableTypeRule(configRules: List) : DiktatRule(
+ "nullable-type",
+ configRules,
+ listOf(NULLABLE_PROPERTY_TYPE)) {
override fun logic(node: ASTNode) {
if (node.elementType == PROPERTY) {
checkProperty(node)
@@ -50,7 +53,7 @@ class NullableTypeRule(configRules: List) : DiktatRule("nullable-ty
val typeReferenceNode = node.findChildByType(TYPE_REFERENCE)!!
// check that property has nullable type, right value one of allow expression
if (!node.hasChildOfType(NULL) &&
- node.findAllNodesWithSpecificType(DOT_QUALIFIED_EXPRESSION).isEmpty() &&
+ node.findAllDescendantsWithSpecificType(DOT_QUALIFIED_EXPRESSION).isEmpty() &&
typeReferenceNode.hasChildOfType(NULLABLE_TYPE) &&
typeReferenceNode.findChildByType(NULLABLE_TYPE)!!.hasChildOfType(QUEST) &&
(node.findChildByType(CALL_EXPRESSION)?.findChildByType(REFERENCE_EXPRESSION) == null ||
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SingleLineStatementsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SingleLineStatementsRule.kt
index 806dafa989..a4fc8ec5e4 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SingleLineStatementsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SingleLineStatementsRule.kt
@@ -18,7 +18,10 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet
/**
* Rule that looks for multiple statements on a single line separated with a `;` and splits them in multiple lines.
*/
-class SingleLineStatementsRule(configRules: List) : DiktatRule("statement", configRules, listOf(MORE_THAN_ONE_STATEMENT_PER_LINE)) {
+class SingleLineStatementsRule(configRules: List) : DiktatRule(
+ "statement",
+ configRules,
+ listOf(MORE_THAN_ONE_STATEMENT_PER_LINE)) {
override fun logic(node: ASTNode) {
checkSemicolon(node)
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
index 935e7805f7..fb1d2a3a90 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/SortRule.kt
@@ -5,7 +5,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_DECLARATIONS_ORDER
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.hasChildOfType
import org.cqfn.diktat.ruleset.utils.isClassEnum
@@ -31,7 +31,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
/**
* Rule that sorts class properties and enum members alphabetically
*/
-class SortRule(configRules: List) : DiktatRule("sort-rule", configRules, listOf(WRONG_DECLARATIONS_ORDER)) {
+class SortRule(configRules: List) : DiktatRule(
+ "sort-rule",
+ configRules,
+ listOf(WRONG_DECLARATIONS_ORDER)) {
override fun logic(node: ASTNode) {
val configuration = SortRuleConfiguration(
configRules.getRuleConfig(WRONG_DECLARATIONS_ORDER)?.configuration ?: emptyMap()
@@ -78,7 +81,7 @@ class SortRule(configRules: List) : DiktatRule("sort-rule", configR
nonSortList: List,
node: ASTNode) {
val isEnum = nonSortList.first().elementType == ENUM_ENTRY
- val spaceBefore = if (node.findAllNodesWithSpecificType(EOL_COMMENT).isNotEmpty() && isEnum) {
+ val spaceBefore = if (node.findAllDescendantsWithSpecificType(EOL_COMMENT).isNotEmpty() && isEnum) {
nonSortList.last().run {
if (this.hasChildOfType(EOL_COMMENT) && !this.hasChildOfType(COMMA)) {
this.addChild(LeafPsiElement(COMMA, ","), this.findChildByType(EOL_COMMENT))
@@ -139,7 +142,7 @@ class SortRule(configRules: List) : DiktatRule("sort-rule", configR
val hasTrailingComma = (sortList.last() != enumEntryList.last() && enumEntryList.last().hasChildOfType(COMMA))
swapSortNodes(sortList, enumEntryList, node)
if (!hasTrailingComma) {
- val lastEntry = node.findAllNodesWithSpecificType(ENUM_ENTRY).last()
+ val lastEntry = node.findAllDescendantsWithSpecificType(ENUM_ENTRY).last()
lastEntry.removeChild(lastEntry.findChildByType(COMMA)!!)
}
if (isEndSpace) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringConcatenationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringConcatenationRule.kt
index cdd52d046d..0c3fee3624 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringConcatenationRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringConcatenationRule.kt
@@ -3,23 +3,39 @@ package org.cqfn.diktat.ruleset.rules.chapter3
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.STRING_CONCATENATION
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
-import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType
-import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
+import org.cqfn.diktat.ruleset.utils.*
import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.OPERATION_REFERENCE
import com.pinterest.ktlint.core.ast.ElementType.PLUS
import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import org.jetbrains.kotlin.psi.KtBinaryExpression
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtConstantExpression
+import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtOperationExpression
+import org.jetbrains.kotlin.psi.KtParenthesizedExpression
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
/**
* This rule covers checks and fixes related to string concatenation.
* Rule 3.8 prohibits string concatenation and suggests to use string templates instead
- * // FixMe: fixes will be added
- * // FixMe: .toString() method and functions that return strings are not supported
+ * if this expressions fits one line. For example:
+ * """ string """ + "string" will be converted to "string string"
+ * "string " + 1 will be converted to "string 1"
+ * "string one " + "string two "
*/
-class StringConcatenationRule(configRules: List) : DiktatRule("string-concatenation", configRules, listOf(STRING_CONCATENATION)) {
+class StringConcatenationRule(configRules: List) : DiktatRule(
+ "string-concatenation",
+ configRules,
+ listOf(
+ STRING_CONCATENATION
+ )
+) {
override fun logic(node: ASTNode) {
if (node.elementType == BINARY_EXPRESSION) {
// searching top-level binary expression to detect any operations with "plus" (+)
@@ -33,25 +49,56 @@ class StringConcatenationRule(configRules: List) : DiktatRule("stri
private fun isSingleLineStatement(node: ASTNode): Boolean =
!node.text.contains("\n")
- private fun detectStringConcatenation(node: ASTNode) {
- node.findAllNodesWithSpecificType(BINARY_EXPRESSION).find { detectStringConcatenationInExpression(it, node) }
+ /**
+ * This method works only with top-level binary expressions. It should be checked before the call.
+ */
+ @Suppress("AVOID_NULL_CHECKS")
+ private fun detectStringConcatenation(topLevelBinaryExpr: ASTNode) {
+ val allBinaryExpressions = topLevelBinaryExpr.findAllDescendantsWithSpecificType(BINARY_EXPRESSION)
+ val nodeWithBug = allBinaryExpressions.find { isDetectStringConcatenationInExpression(it) }
+
+ if (nodeWithBug != null) {
+ STRING_CONCATENATION.warnAndFix(
+ configRules, emitWarn,
+ this.isFixMode, topLevelBinaryExpr.text.lines().first(), nodeWithBug.startOffset, nodeWithBug
+ ) {
+ fixBinaryExpressionWithConcatenation(nodeWithBug)
+ loop(topLevelBinaryExpr.treeParent)
+ }
+ }
}
- @Suppress("FUNCTION_BOOLEAN_PREFIX")
- private fun detectStringConcatenationInExpression(node: ASTNode, parentNode: ASTNode): Boolean {
- assert(node.elementType == BINARY_EXPRESSION)
- val firstChild = node.firstChildNode
- return if (isPlusBinaryExpression(node) && firstChild.elementType == STRING_TEMPLATE) {
- STRING_CONCATENATION.warn(configRules, emitWarn, this.isFixMode, parentNode.text, firstChild.startOffset, firstChild)
- true
- } else {
- false
+ private fun loop(parentTopLevelBinaryExpr: ASTNode) {
+ val allBinaryExpressions = parentTopLevelBinaryExpr.findAllDescendantsWithSpecificType(BINARY_EXPRESSION)
+ val nodeWithBug = allBinaryExpressions.find { isDetectStringConcatenationInExpression(it) }
+
+ val bugDetected = nodeWithBug != null
+ if (bugDetected) {
+ fixBinaryExpressionWithConcatenation(nodeWithBug)
+ loop(parentTopLevelBinaryExpr)
}
}
+ /**
+ * We can detect string concatenation by the first (left) operand in binary expression.
+ * If it is of type string - then we found string concatenation.
+ * If the right value is not a constant string then don't change them to template.
+ */
+ private fun isDetectStringConcatenationInExpression(node: ASTNode): Boolean {
+ require(node.elementType == BINARY_EXPRESSION) {
+ "cannot process non binary expression in the process of detecting string concatenation"
+ }
+ val firstChild = node.firstChildNode
+ val lastChild = node.lastChildNode
+ return isPlusBinaryExpression(node) && isStringVar(firstChild, lastChild)
+ }
+
+ private fun isStringVar(firstChild: ASTNode, lastChild: ASTNode) = firstChild.elementType == STRING_TEMPLATE ||
+ ((firstChild.text.endsWith("toString()")) && firstChild.elementType == DOT_QUALIFIED_EXPRESSION && lastChild.elementType == STRING_TEMPLATE)
+
@Suppress("COMMENT_WHITE_SPACE")
private fun isPlusBinaryExpression(node: ASTNode): Boolean {
- assert(node.elementType == BINARY_EXPRESSION)
+ require(node.elementType == BINARY_EXPRESSION)
// binary expression
// / | \
// expr1 operationRef expr2
@@ -60,4 +107,112 @@ class StringConcatenationRule(configRules: List) : DiktatRule("stri
return operationReference
?.getFirstChildWithType(PLUS) != null
}
+
+ private fun fixBinaryExpressionWithConcatenation(node: ASTNode?) {
+ val binaryExpressionPsi = node?.psi as KtBinaryExpression
+ val parentNode = node.treeParent
+ val textNode = checkKtExpression(binaryExpressionPsi)
+ val newNode = KotlinParser().createNode("\"$textNode\"")
+ parentNode.replaceChild(node, newNode)
+ }
+
+ private fun isPlusBinaryExpressionAndFirstElementString(binaryExpressionNode: KtBinaryExpression) =
+ (binaryExpressionNode.left is KtStringTemplateExpression) && PLUS == binaryExpressionNode.operationToken
+
+ @Suppress(
+ "TOO_LONG_FUNCTION",
+ "NESTED_BLOCK",
+ "SAY_NO_TO_VAR",
+ "ComplexMethod")
+ private fun checkKtExpression(binaryExpressionPsi: KtBinaryExpression): String {
+ var lvalueText = binaryExpressionPsi.left?.text?.trim('"')
+ val rvalueText = binaryExpressionPsi.right?.text
+
+ if (binaryExpressionPsi.isLvalueDotQualifiedExpression() && binaryExpressionPsi.firstChild.text.endsWith("toString()")) {
+ // =========== (1 + 2).toString() -> ${(1 + 2)}
+ val leftText = binaryExpressionPsi.firstChild.firstChild.text
+ lvalueText = "\${$leftText}"
+ }
+ if (binaryExpressionPsi.isLvalueReferenceExpression() || binaryExpressionPsi.isLvalueConstantExpression()) {
+ return binaryExpressionPsi.text
+ }
+ if (binaryExpressionPsi.isLvalueBinaryExpression()) {
+ val rightValue = checkKtExpression(binaryExpressionPsi.left as KtBinaryExpression)
+ val rightEx = binaryExpressionPsi.right
+ val rightVal = if (binaryExpressionPsi.isRvalueParenthesized()) {
+ checkKtExpression(rightEx?.children?.get(0) as KtBinaryExpression)
+ } else {
+ (rightEx?.text?.trim('"'))
+ }
+ if (binaryExpressionPsi.left?.text == rightValue) {
+ return binaryExpressionPsi.text
+ }
+ return "$rightValue$rightVal"
+ } else if (binaryExpressionPsi.isRvalueConstantExpression() || binaryExpressionPsi.isRvalueStringTemplateExpression()) {
+ // =========== "a " + "b" -> "a b"
+ val rvalueTextNew = rvalueText?.trim('"')
+ return "$lvalueText$rvalueTextNew"
+ } else if (binaryExpressionPsi.isRvalueCallExpression() || binaryExpressionPsi.isRvalueDotQualifiedExpression() || binaryExpressionPsi.isRvalueOperation()) {
+ // =========== "a " + foo() -> "a ${foo()}}"
+ return "$lvalueText\${$rvalueText}"
+ } else if (binaryExpressionPsi.isRvalueReferenceExpression()) {
+ // =========== "a " + b -> "a $b"
+ return "$lvalueText$$rvalueText"
+ } else if (binaryExpressionPsi.isRvalueParenthesized()) {
+ val binExpression = binaryExpressionPsi.right?.children?.first()
+ if (binExpression is KtBinaryExpression) {
+ if (isPlusBinaryExpressionAndFirstElementString(binExpression)) {
+ val rightValue = checkKtExpression(binExpression)
+ return "$lvalueText$rightValue"
+ } else if (binExpression.isLvalueBinaryExpression()) {
+ val rightValue = checkKtExpression(binExpression.left as KtBinaryExpression)
+ val rightEx = binExpression.right
+ val rightVal = if (binExpression.isRvalueParenthesized()) {
+ checkKtExpression(rightEx?.children?.get(0) as KtBinaryExpression)
+ } else {
+ (rightEx?.text?.trim('"'))
+ }
+ if (binExpression.left?.text == rightValue) {
+ return "$lvalueText\${$rvalueText}"
+ }
+ return "$lvalueText$rightValue$rightVal"
+ }
+ }
+ return "$lvalueText\${$rvalueText}"
+ }
+ return binaryExpressionPsi.text
+ }
+
+ private fun KtBinaryExpression.isRvalueConstantExpression() =
+ this.right is KtConstantExpression
+
+ private fun KtBinaryExpression.isRvalueStringTemplateExpression() =
+ this.right is KtStringTemplateExpression
+
+ private fun KtBinaryExpression.isRvalueCallExpression() =
+ this.right is KtCallExpression
+
+ private fun KtBinaryExpression.isRvalueReferenceExpression() =
+ this.right is KtReferenceExpression
+
+ private fun KtBinaryExpression.isRvalueDotQualifiedExpression() =
+ this.right is KtDotQualifiedExpression
+
+ private fun KtBinaryExpression.isRvalueParenthesized() =
+ this.right is KtParenthesizedExpression
+
+ private fun KtBinaryExpression.isRvalueOperation() =
+ this.right is KtOperationExpression
+
+ private fun KtBinaryExpression.isLvalueDotQualifiedExpression() =
+ this.left is KtDotQualifiedExpression
+
+ private fun KtBinaryExpression.isLvalueBinaryExpression() =
+ this.left is KtBinaryExpression
+
+ private fun KtBinaryExpression.isLvalueReferenceExpression() =
+ this.left is KtReferenceExpression
+
+ private fun KtBinaryExpression.isLvalueConstantExpression() =
+ this.left is KtConstantExpression
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringTemplateFormatRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringTemplateFormatRule.kt
index 00563eaf29..7d20ed9548 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringTemplateFormatRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/StringTemplateFormatRule.kt
@@ -1,11 +1,10 @@
package org.cqfn.diktat.ruleset.rules.chapter3
import org.cqfn.diktat.common.config.rules.RulesConfig
-import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.STRING_TEMPLATE_CURLY_BRACES
import org.cqfn.diktat.ruleset.constants.Warnings.STRING_TEMPLATE_QUOTES
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.hasAnyChildOfTypes
import com.pinterest.ktlint.core.ast.ElementType.ARRAY_ACCESS_EXPRESSION
@@ -28,7 +27,9 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
*
* FixMe: The important caveat here: in "$foo" kotlin compiler adds implicit call to foo.toString() in case foo type is not string.
*/
-class StringTemplateFormatRule(configRules: List) : DiktatRule("string-template-format", configRules,
+class StringTemplateFormatRule(configRules: List) : DiktatRule(
+ "string-template-format",
+ configRules,
listOf(STRING_TEMPLATE_CURLY_BRACES, STRING_TEMPLATE_QUOTES)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
@@ -82,13 +83,13 @@ class StringTemplateFormatRule(configRules: List) : DiktatRule("str
@Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX")
private fun bracesCanBeOmitted(node: ASTNode): Boolean {
val onlyOneRefExpr = node
- .findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ .findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
.singleOrNull()
?.treeParent
?.elementType == LONG_STRING_TEMPLATE_ENTRY
val isArrayAccessExpression = node // this should be omitted in previous expression, used for safe warranties
- .findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ .findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
.singleOrNull()
?.treeParent
?.elementType == ARRAY_ACCESS_EXPRESSION
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt
index acf14c8ad6..c1061ff8cc 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt
@@ -49,7 +49,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
* [12] Destructuring declarations
*/
@Suppress("TOO_LONG_FUNCTION")
-class TrailingCommaRule(configRules: List) : DiktatRule("trailing-comma", configRules, listOf(TRAILING_COMMA)) {
+class TrailingCommaRule(configRules: List) : DiktatRule(
+ "trailing-comma",
+ configRules,
+ listOf(TRAILING_COMMA)) {
private val commonConfig = configRules.getCommonConfiguration()
private val trailingConfig = this.configRules.getRuleConfig(TRAILING_COMMA)?.configuration ?: emptyMap()
private val configuration by lazy {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/WhenMustHaveElseRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/WhenMustHaveElseRule.kt
index f40c874d7f..98f289431a 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/WhenMustHaveElseRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/WhenMustHaveElseRule.kt
@@ -36,7 +36,10 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
* The compiler can issue a warning when it is missing.
*/
@Suppress("ForbiddenComment")
-class WhenMustHaveElseRule(configRules: List) : DiktatRule("no-else-in-when", configRules, listOf(WHEN_WITHOUT_ELSE)) {
+class WhenMustHaveElseRule(configRules: List) : DiktatRule(
+ "no-else-in-when",
+ configRules,
+ listOf(WHEN_WITHOUT_ELSE)) {
override fun logic(node: ASTNode) {
if (node.elementType == ElementType.WHEN && isStatement(node)) {
checkEntries(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/BlankLinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/BlankLinesRule.kt
index 2cd8be0c2e..d638f25096 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/BlankLinesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/BlankLinesRule.kt
@@ -3,6 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter3.files
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.TOO_MANY_BLANK_LINES
import org.cqfn.diktat.ruleset.rules.DiktatRule
+import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.leaveExactlyNumNewLines
import org.cqfn.diktat.ruleset.utils.leaveOnlyOneNewLine
@@ -12,6 +13,7 @@ import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
import com.pinterest.ktlint.core.ast.ElementType.FILE
import com.pinterest.ktlint.core.ast.ElementType.FUNCTION_LITERAL
+import com.pinterest.ktlint.core.ast.ElementType.LAMBDA_ARGUMENT
import com.pinterest.ktlint.core.ast.ElementType.LBRACE
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.SCRIPT
@@ -23,7 +25,10 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
* 1. Checks that no more than two consecutive blank lines are used in a row
* 2. Checks that blank lines are not put in the beginning or at the end of code blocks with curly braces
*/
-class BlankLinesRule(configRules: List) : DiktatRule("blank-lines", configRules, listOf(TOO_MANY_BLANK_LINES)) {
+class BlankLinesRule(configRules: List) : DiktatRule(
+ "blank-lines",
+ configRules,
+ listOf(TOO_MANY_BLANK_LINES)) {
override fun logic(node: ASTNode) {
if (node.elementType == WHITE_SPACE) {
// note that no blank lines counts as one newline
@@ -41,6 +46,13 @@ class BlankLinesRule(configRules: List) : DiktatRule("blank-lines",
it.elementType == BLOCK && it.treeParent?.elementType != SCRIPT ||
it.elementType == CLASS_BODY || it.elementType == FUNCTION_LITERAL
}) {
+ node.findParentNodeWithSpecificType(LAMBDA_ARGUMENT)?.let {
+ // Lambda body is always has a BLOCK -> run { } - (LBRACE, WHITE_SPACE, BLOCK "", RBRACE)
+ if (node.treeNext.text.isEmpty()) {
+ return
+ }
+ }
+
if ((node.treeNext.elementType == RBRACE) xor (node.treePrev.elementType == LBRACE)) {
// if both are present, this is not beginning or end
// if both are null, then this block is empty and is handled in another rule
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileSize.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileSize.kt
index e9d8086b2b..461c9ec925 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileSize.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileSize.kt
@@ -15,7 +15,10 @@ import org.slf4j.LoggerFactory
/**
* Rule that checks number of lines in a file
*/
-class FileSize(configRules: List) : DiktatRule("file-size", configRules, listOf(FILE_IS_TOO_LONG)) {
+class FileSize(configRules: List) : DiktatRule(
+ "file-size",
+ configRules,
+ listOf(FILE_IS_TOO_LONG)) {
private val configuration by lazy {
FileSizeConfiguration(
this.configRules.getRuleConfig(FILE_IS_TOO_LONG)?.configuration ?: emptyMap()
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt
index d0cbb3f466..f1c70a54dc 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/FileStructureRule.kt
@@ -14,7 +14,7 @@ import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.rules.chapter1.PackageNaming.Companion.PACKAGE_SEPARATOR
import org.cqfn.diktat.ruleset.utils.StandardPlatforms
import org.cqfn.diktat.ruleset.utils.copyrightWords
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.handleIncorrectOrder
import org.cqfn.diktat.ruleset.utils.moveChildBefore
import org.cqfn.diktat.ruleset.utils.operatorMap
@@ -55,7 +55,9 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
* 4. Ensures imports are ordered alphabetically without blank lines
* 5. Ensures there are no wildcard imports
*/
-class FileStructureRule(configRules: List) : DiktatRule("file-structure", configRules,
+class FileStructureRule(configRules: List) : DiktatRule(
+ "file-structure",
+ configRules,
listOf(FILE_CONTAINS_ONLY_COMMENTS, FILE_INCORRECT_BLOCKS_ORDER, FILE_NO_BLANK_LINE_BETWEEN_BLOCKS,
FILE_UNORDERED_IMPORTS, FILE_WILDCARD_IMPORTS, UNUSED_IMPORT)) {
private val domainName by lazy {
@@ -106,7 +108,10 @@ class FileStructureRule(configRules: List) : DiktatRule("file-struc
return hasCode
}
- @Suppress("ComplexMethod", "TOO_LONG_FUNCTION")
+ @Suppress(
+ "ComplexMethod",
+ "TOO_LONG_FUNCTION",
+ "SpreadOperator")
private fun checkCodeBlocksOrderAndEmptyLines(node: ASTNode) {
// From KtFile.kt: 'scripts have no package directive, all other files must have package directives'.
// Kotlin compiler itself enforces it's position in the file if it is present.
@@ -245,7 +250,7 @@ class FileStructureRule(configRules: List) : DiktatRule("file-struc
}
private fun findAllReferences(node: ASTNode) {
- node.findAllNodesWithSpecificType(OPERATION_REFERENCE).forEach { ref ->
+ node.findAllDescendantsWithSpecificType(OPERATION_REFERENCE).forEach { ref ->
if (!ref.isPartOf(IMPORT_DIRECTIVE)) {
val references = operatorMap.filterValues { it == ref.text }
if (references.isNotEmpty()) {
@@ -256,7 +261,7 @@ class FileStructureRule(configRules: List) : DiktatRule("file-struc
}
}
}
- node.findAllNodesWithSpecificType(REFERENCE_EXPRESSION).forEach {
+ node.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION).forEach {
if (!it.isPartOf(IMPORT_DIRECTIVE)) {
// the importedName method removes the quotes, but the node.text method does not
refSet.add(it.text.replace("`", ""))
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
index 79a3aac6cc..a2c6220c0a 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
@@ -33,11 +33,15 @@ import com.pinterest.ktlint.core.ast.ElementType.FILE
import com.pinterest.ktlint.core.ast.ElementType.LBRACE
import com.pinterest.ktlint.core.ast.ElementType.LBRACKET
import com.pinterest.ktlint.core.ast.ElementType.LITERAL_STRING_TEMPLATE_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_END
+import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_START
import com.pinterest.ktlint.core.ast.ElementType.LPAR
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.RBRACKET
import com.pinterest.ktlint.core.ast.ElementType.RPAR
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY
import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
import com.pinterest.ktlint.core.ast.ElementType.THEN
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
@@ -56,6 +60,8 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
+import java.lang.StringBuilder
+import kotlin.math.abs
/**
* Rule that checks indentation. The following general rules are checked:
@@ -64,7 +70,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
* Additionally, a set of CustomIndentationChecker objects checks all WHITE_SPACE node if they are exceptions from general rules.
* @see CustomIndentationChecker
*/
-class IndentationRule(configRules: List) : DiktatRule("indentation", configRules, listOf(WRONG_INDENTATION)) {
+class IndentationRule(configRules: List) : DiktatRule(
+ "indentation",
+ configRules,
+ listOf(WRONG_INDENTATION)) {
private val configuration: IndentationConfig by lazy {
IndentationConfig(configRules.getRuleConfig(WRONG_INDENTATION)?.configuration ?: emptyMap())
}
@@ -177,14 +186,19 @@ class IndentationRule(configRules: List) : DiktatRule("indentation"
}
val expectedIndent = checkResult?.expectedIndent ?: indentError.expected
- if (checkResult?.adjustNext == true) {
+ if (checkResult?.adjustNext == true && astNode.parents().none { it.elementType == LONG_STRING_TEMPLATE_ENTRY }) {
val exceptionInitiatorNode = astNode.getExceptionalIndentInitiator()
context.addException(exceptionInitiatorNode, expectedIndent - indentError.expected, checkResult.includeLastChild)
}
+
+ if (astNode.treeParent.elementType == LONG_STRING_TEMPLATE_ENTRY && indentError.expected != indentError.actual) {
+ context.addException(astNode.treeParent, abs(indentError.expected - indentError.actual), false)
+ }
+
if (checkResult?.isCorrect != true && expectedIndent != indentError.actual) {
WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "expected $expectedIndent but was ${indentError.actual}",
whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1, whiteSpace.node) {
- checkStringLiteral(whiteSpace, expectedIndent)
+ checkStringLiteral(whiteSpace, expectedIndent, indentError.actual)
whiteSpace.node.indentBy(expectedIndent)
}
}
@@ -193,7 +207,11 @@ class IndentationRule(configRules: List) : DiktatRule("indentation"
/**
* Checks if it is triple-quoted string template with trimIndent() or trimMargin() function.
*/
- private fun checkStringLiteral(whiteSpace: PsiWhiteSpace, expectedIndent: Int) {
+ private fun checkStringLiteral(
+ whiteSpace: PsiWhiteSpace,
+ expectedIndent: Int,
+ actualIndent: Int
+ ) {
val nextNode = whiteSpace.node.treeNext
if (nextNode != null &&
nextNode.elementType == DOT_QUALIFIED_EXPRESSION &&
@@ -203,19 +221,23 @@ class IndentationRule(configRules: List) : DiktatRule("indentation"
it == "trimIndent()" ||
it == "trimMargin()"
} == true) {
- fixStringLiteral(whiteSpace, expectedIndent)
+ fixStringLiteral(whiteSpace, expectedIndent, actualIndent)
}
}
/**
* If it is triple-quoted string template we need to indent all its parts
*/
- private fun fixStringLiteral(whiteSpace: PsiWhiteSpace, expectedIndent: Int) {
+ private fun fixStringLiteral(
+ whiteSpace: PsiWhiteSpace,
+ expectedIndent: Int,
+ actualIndent: Int
+ ) {
val textIndent = " ".repeat(expectedIndent + INDENT_SIZE)
val templateEntries = whiteSpace.node.treeNext.firstChildNode.getAllChildrenWithType(LITERAL_STRING_TEMPLATE_ENTRY)
- templateEntries.forEach {
- if (!it.text.contains("\n") && it.text.isNotBlank()) {
- (it.firstChildNode as LeafPsiElement).rawReplaceWithText(textIndent + it.firstChildNode.text.trim())
+ templateEntries.forEach { node ->
+ if (!node.text.contains("\n")) {
+ fixFirstTemplateEntries(node, textIndent, actualIndent)
}
}
(templateEntries.last().firstChildNode as LeafPsiElement)
@@ -226,12 +248,43 @@ class IndentationRule(configRules: List) : DiktatRule("indentation"
.trim())
}
+ /**
+ * This method fixes all lines of string template except the last one
+ * Also it considers $foo insertions in string
+ */
+ private fun fixFirstTemplateEntries(
+ node: ASTNode,
+ textIndent: String,
+ actualIndent: Int
+ ) {
+ val correctedText = StringBuilder()
+ // shift of the node depending on its initial string template indent
+ val nodeStartIndent = if (node.firstChildNode.text.takeWhile { it == ' ' }.count() - actualIndent - INDENT_SIZE > 0) {
+ node.firstChildNode.text.takeWhile { it == ' ' }.count() - actualIndent - INDENT_SIZE
+ } else {
+ 0
+ }
+ val isPrevStringTemplate = node.treePrev.elementType in stringLiteralTokens
+ val isNextStringTemplate = node.treeNext.elementType in stringLiteralTokens
+ when {
+ // if string template is before literal_string
+ isPrevStringTemplate && !isNextStringTemplate -> correctedText.append(node.firstChildNode.text.trimEnd())
+ // if string template is after literal_string
+ !isPrevStringTemplate && isNextStringTemplate -> correctedText.append(textIndent + " ".repeat(nodeStartIndent) + node.firstChildNode.text.trimStart())
+ // if there is no string template in literal_string
+ !isPrevStringTemplate && !isNextStringTemplate -> correctedText.append(textIndent + " ".repeat(nodeStartIndent) + node.firstChildNode.text.trim())
+ isPrevStringTemplate && isNextStringTemplate -> correctedText.append(node.firstChildNode.text)
+ node.text.isBlank() -> correctedText.append(textIndent)
+ else -> {}
+ }
+ (node.firstChildNode as LeafPsiElement).rawReplaceWithText(correctedText.toString())
+ }
+
private fun ASTNode.getExceptionalIndentInitiator() = treeParent.let { parent ->
when (parent.psi) {
// fixme: custom logic for determining exceptional indent initiator, should be moved elsewhere
- is KtDotQualifiedExpression ->
- // get the topmost expression to keep extended indent for the whole chain of dot call expressions
- parents().takeWhile { it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION }.last()
+ // get the topmost expression to keep extended indent for the whole chain of dot call expressions
+ is KtDotQualifiedExpression -> parents().takeWhile { it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION }.last()
is KtIfExpression -> parent.findChildByType(THEN) ?: parent.findChildByType(ELSE) ?: parent
is KtLoopExpression -> (parent.psi as KtLoopExpression).body?.node ?: parent
else -> parent
@@ -325,15 +378,16 @@ class IndentationRule(configRules: List) : DiktatRule("indentation"
companion object {
const val INDENT_SIZE = 4
- private val increasingTokens = listOf(LPAR, LBRACE, LBRACKET)
- private val decreasingTokens = listOf(RPAR, RBRACE, RBRACKET)
+ private val increasingTokens = listOf(LPAR, LBRACE, LBRACKET, LONG_TEMPLATE_ENTRY_START)
+ private val decreasingTokens = listOf(RPAR, RBRACE, RBRACKET, LONG_TEMPLATE_ENTRY_END)
private val matchingTokens = increasingTokens.zip(decreasingTokens)
+ private val stringLiteralTokens = listOf(SHORT_STRING_TEMPLATE_ENTRY, LONG_STRING_TEMPLATE_ENTRY)
}
}
/**
* @property expected expected indentation as a number of spaces
- * @property actual actial indentation as a number of spaces
+ * @property actual actual indentation as a number of spaces
*/
internal data class IndentationError(val expected: Int, val actual: Int)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/NewlinesRule.kt
index d75eedfcaa..c005759454 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/NewlinesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/NewlinesRule.kt
@@ -8,7 +8,19 @@ import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION
import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.*
+import org.cqfn.diktat.ruleset.utils.appendNewlineMergingWhiteSpace
+import org.cqfn.diktat.ruleset.utils.emptyBlockList
+import org.cqfn.diktat.ruleset.utils.extractLineOfText
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType
+import org.cqfn.diktat.ruleset.utils.getIdentifierName
+import org.cqfn.diktat.ruleset.utils.hasParent
+import org.cqfn.diktat.ruleset.utils.isBeginByNewline
+import org.cqfn.diktat.ruleset.utils.isEol
+import org.cqfn.diktat.ruleset.utils.isFollowedByNewline
+import org.cqfn.diktat.ruleset.utils.isSingleLineIfElse
+import org.cqfn.diktat.ruleset.utils.leaveOnlyOneNewLine
+import org.cqfn.diktat.ruleset.utils.log
import com.pinterest.ktlint.core.ast.ElementType.ANDAND
import com.pinterest.ktlint.core.ast.ElementType.ARROW
@@ -51,6 +63,7 @@ import com.pinterest.ktlint.core.ast.ElementType.PLUS
import com.pinterest.ktlint.core.ast.ElementType.PLUSEQ
import com.pinterest.ktlint.core.ast.ElementType.POSTFIX_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR
+import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.RETURN
import com.pinterest.ktlint.core.ast.ElementType.RETURN_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS
@@ -77,6 +90,7 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtParameterList
import org.jetbrains.kotlin.psi.KtSuperTypeList
+import org.jetbrains.kotlin.psi.KtValueArgumentList
import org.jetbrains.kotlin.psi.psiUtil.children
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
@@ -95,7 +109,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
* 10. Complex expression inside condition replaced with new variable
*/
@Suppress("ForbiddenComment")
-class NewlinesRule(configRules: List) : DiktatRule("newlines", configRules, listOf(COMPLEX_EXPRESSION, REDUNDANT_SEMICOLON, WRONG_NEWLINES)) {
+class NewlinesRule(configRules: List) : DiktatRule(
+ "newlines",
+ configRules,
+ listOf(COMPLEX_EXPRESSION, REDUNDANT_SEMICOLON, WRONG_NEWLINES)) {
private val configuration by lazy {
NewlinesRuleConfiguration(configRules.getRuleConfig(WRONG_NEWLINES)?.configuration ?: emptyMap())
}
@@ -106,9 +123,10 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
in lineBreakBeforeOperators -> handleOperatorWithLineBreakBefore(node)
LPAR -> handleOpeningParentheses(node)
COMMA -> handleComma(node)
+ COLON -> handleColon(node)
BLOCK -> handleLambdaBody(node)
RETURN -> handleReturnStatement(node)
- SUPER_TYPE_LIST, VALUE_PARAMETER_LIST -> handleList(node)
+ SUPER_TYPE_LIST, VALUE_PARAMETER_LIST, VALUE_ARGUMENT_LIST -> handleList(node)
else -> {
}
}
@@ -226,6 +244,40 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
}
}
+ /**
+ * Check that newline is not placed before or after a colon
+ */
+ private fun handleColon(node: ASTNode) {
+ val prevNewLine = node
+ .parent({ it.treePrev != null }, strict = false)
+ ?.treePrev
+ ?.takeIf {
+ it.elementType == WHITE_SPACE && it.text.contains("\n")
+ }
+ prevNewLine?.let { whiteSpace ->
+ WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "newline shouldn't be placed before colon", node.startOffset, node) {
+ whiteSpace.treeParent.removeChild(whiteSpace)
+
+ if (node.hasParent(VALUE_PARAMETER_LIST)) {
+ // If inside the list of arguments the rule for a new line before the colon has worked,
+ // then we delete the new line on both sides of the colon
+ whiteSpace.treeParent?.let {
+ val nextNewLine = node
+ .parent({ it.treeNext != null }, strict = false)
+ ?.treeNext
+ ?.takeIf {
+ it.elementType == WHITE_SPACE && it.text.contains("\n")
+ }
+ nextNewLine?.let {
+ it.treeParent.addChild(PsiWhiteSpaceImpl(" "), it)
+ it.treeParent.removeChild(it)
+ }
+ }
+ }
+ }
+ }
+ }
+
private fun handleLambdaBody(node: ASTNode) {
if (node.treeParent.elementType == FUNCTION_LITERAL) {
val isSingleLineLambda = node.treeParent.text.lines().size == 1
@@ -299,15 +351,28 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
* Checks that members of [VALUE_PARAMETER_LIST] (list of function parameters at declaration site) are separated with newlines.
* Also checks that entries of [SUPER_TYPE_LIST] are separated by newlines.
*/
+ @Suppress("ComplexMethod")
private fun handleList(node: ASTNode) {
- if (node.elementType == VALUE_PARAMETER_LIST && node.treeParent.elementType.let { it == FUNCTION_TYPE || it == FUNCTION_TYPE_RECEIVER }) {
+ if (node.elementType == VALUE_PARAMETER_LIST) {
+ // do not check list parameter in lambda
+ node.findParentNodeWithSpecificType(LAMBDA_ARGUMENT)?.let {
+ return
+ }
// do not check other value lists
+ if (node.treeParent.elementType.let { it == FUNCTION_TYPE || it == FUNCTION_TYPE_RECEIVER }) {
+ return
+ }
+ }
+
+ if (node.elementType == VALUE_ARGUMENT_LIST && node.siblings(forward = false).any { it.elementType == REFERENCE_EXPRESSION }) {
+ // check that it is not function invocation, but only supertype constructor calls
return
}
val (numEntries, entryType) = when (node.elementType) {
VALUE_PARAMETER_LIST -> (node.psi as KtParameterList).parameters.size to "value parameters"
SUPER_TYPE_LIST -> (node.psi as KtSuperTypeList).entries.size to "supertype list entries"
+ VALUE_ARGUMENT_LIST -> (node.psi as KtValueArgumentList).arguments.size to "value arguments"
else -> {
log.warn("Unexpected node element type ${node.elementType}")
return
@@ -315,7 +380,10 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
}
if (numEntries > configuration.maxParametersInOneLine) {
when (node.elementType) {
- VALUE_PARAMETER_LIST -> handleFirstValueParameter(node)
+ VALUE_PARAMETER_LIST -> handleFirstValue(node, VALUE_PARAMETER, "first parameter should be placed on a separate line " +
+ "or all other parameters should be aligned with it in declaration of <${node.getParentIdentifier()}>")
+ VALUE_ARGUMENT_LIST -> handleFirstValue(node, VALUE_ARGUMENT, "first value argument (%s) should be placed on the new line " +
+ "or all other parameters should be aligned with it")
else -> {
}
}
@@ -324,24 +392,33 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
}
}
- private fun handleFirstValueParameter(node: ASTNode) = node
+ private fun handleFirstValue(node: ASTNode,
+ filterType: IElementType,
+ warnText: String) = node
.children()
.takeWhile { !it.textContains('\n') }
- .filter { it.elementType == VALUE_PARAMETER }
+ .filter { it.elementType == filterType }
.toList()
.takeIf { it.size > 1 }
- ?.let {
- WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "first parameter should be placed on a separate line " +
- "or all other parameters should be aligned with it in declaration of <${node.getParentIdentifier()}>", node.startOffset, node) {
- node.appendNewlineMergingWhiteSpace(
- it.first()
- .treePrev
- .takeIf { it.elementType == WHITE_SPACE },
- it.first()
- )
+ ?.let { list ->
+ val freeText = if (filterType == VALUE_ARGUMENT) {
+ warnText.format(list.first().text)
+ } else {
+ warnText
+ }
+ WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) {
+ list.first().treePrev?.let {
+ node.appendNewlineMergingWhiteSpace(
+ list.first()
+ .treePrev
+ .takeIf { it.elementType == WHITE_SPACE },
+ list.first()
+ )
+ }
}
}
+ @Suppress("AVOID_NULL_CHECKS")
private fun handleValueParameterList(node: ASTNode, entryType: String) = node
.children()
.filter {
@@ -351,8 +428,13 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
.toList()
.takeIf { it.isNotEmpty() }
?.let { invalidCommas ->
+ val warnText = if (node.getParentIdentifier() != null) {
+ "$entryType should be placed on different lines in declaration of <${node.getParentIdentifier()}>"
+ } else {
+ "$entryType should be placed on different lines"
+ }
WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode,
- "$entryType should be placed on different lines in declaration of <${node.getParentIdentifier()}>", node.startOffset, node) {
+ warnText, node.startOffset, node) {
invalidCommas.forEach { comma ->
val nextWhiteSpace = comma.treeNext.takeIf { it.elementType == WHITE_SPACE }
comma.appendNewlineMergingWhiteSpace(nextWhiteSpace, nextWhiteSpace?.treeNext ?: comma.treeNext)
@@ -485,7 +567,7 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
parents().takeWhile { it.elementType in chainExpressionTypes && it.elementType != LAMBDA_ARGUMENT }
private fun isMultilineLambda(node: ASTNode): Boolean =
- node.findAllNodesWithSpecificType(LAMBDA_ARGUMENT)
+ node.findAllDescendantsWithSpecificType(LAMBDA_ARGUMENT)
.firstOrNull()
?.text
?.count { it == '\n' } ?: -1 > 0
@@ -499,7 +581,7 @@ class NewlinesRule(configRules: List) : DiktatRule("newlines", conf
val firstCallee = mutableListOf().also {
getOrderedCallExpressions(psi, it)
}.first()
- findAllNodesWithSpecificType(firstCallee.elementType, false).first() === this@isFirstCall
+ findAllDescendantsWithSpecificType(firstCallee.elementType, false).first() === this@isFirstCall
} ?: false
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
index ac967e1e46..473d3b3627 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
/**
* Rule that checks order in top level
*/
-class TopLevelOrderRule(configRules: List) : DiktatRule("top-level-order", configRules, listOf(TOP_LEVEL_ORDER)) {
+class TopLevelOrderRule(configRules: List) : DiktatRule(
+ "top-level-order",
+ configRules,
+ listOf(TOP_LEVEL_ORDER)) {
override fun logic(node: ASTNode) {
if (node.elementType == FILE) {
checkNode(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/WhiteSpaceRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/WhiteSpaceRule.kt
index b05694f620..254de14f43 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/WhiteSpaceRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/WhiteSpaceRule.kt
@@ -92,7 +92,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
* 10. There should be no spaces between prefix/postfix operator (like `!!` or `++`) and it's operand
*/
@Suppress("ForbiddenComment")
-class WhiteSpaceRule(configRules: List) : DiktatRule("horizontal-whitespace", configRules, listOf(WRONG_WHITESPACE)) {
+class WhiteSpaceRule(configRules: List) : DiktatRule(
+ "horizontal-whitespace",
+ configRules,
+ listOf(WRONG_WHITESPACE)) {
@Suppress("ComplexMethod")
override fun logic(node: ASTNode) {
when (node.elementType) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/identifiers/LocalVariablesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/identifiers/LocalVariablesRule.kt
index 6f5c2880c9..8170e552c3 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/identifiers/LocalVariablesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/identifiers/LocalVariablesRule.kt
@@ -37,7 +37,10 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
* * Only properties without initialization or initialized with expressions based on constants are supported.
* * Properties initialized with constructor calls cannot be distinguished from method call and are no supported.
*/
-class LocalVariablesRule(configRules: List) : DiktatRule("local-variables", configRules, listOf(LOCAL_VARIABLE_EARLY_DECLARATION)) {
+class LocalVariablesRule(configRules: List) : DiktatRule(
+ "local-variables",
+ configRules,
+ listOf(LOCAL_VARIABLE_EARLY_DECLARATION)) {
override fun logic(node: ASTNode) {
if (node.elementType == FILE) {
// collect all local properties and associate with corresponding references
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/ImmutableValNoVarRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/ImmutableValNoVarRule.kt
index 1c1c41889b..520772ed87 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/ImmutableValNoVarRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/ImmutableValNoVarRule.kt
@@ -19,7 +19,10 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
* because `var` variables can be reassigned several times in the business logic. Of course, in some scenarios with loops or accumulators only `var`s can be used and are allowed.
* FixMe: here we should also raise warnings for a reassignment of a var (if var has no assignments except in declaration - it can be final)
*/
-class ImmutableValNoVarRule(configRules: List) : DiktatRule("no-var-rule", configRules, listOf(SAY_NO_TO_VAR)) {
+class ImmutableValNoVarRule(configRules: List) : DiktatRule(
+ "no-var-rule",
+ configRules,
+ listOf(SAY_NO_TO_VAR)) {
override fun logic(node: ASTNode) {
if (node.elementType == ElementType.FILE) {
// we will raise warning for cases when var property has no assignments
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/NullChecksRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/NullChecksRule.kt
index 366cff96d0..6627d48e15 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/NullChecksRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/NullChecksRule.kt
@@ -26,7 +26,10 @@ import org.jetbrains.kotlin.psi.KtIfExpression
* This rule check and fixes explicit null checks (explicit comparison with `null`)
* There are several code-structures that can be used in Kotlin to avoid null-checks. For example: `?:`, `.let {}`, `.also {}`, e.t.c
*/
-class NullChecksRule(configRules: List) : DiktatRule("null-checks", configRules, listOf(AVOID_NULL_CHECKS)) {
+class NullChecksRule(configRules: List) : DiktatRule(
+ "null-checks",
+ configRules,
+ listOf(AVOID_NULL_CHECKS)) {
override fun logic(node: ASTNode) {
if (node.elementType == CONDITION) {
node.parent(IF)?.let {
@@ -58,7 +61,7 @@ class NullChecksRule(configRules: List) : DiktatRule("null-checks",
}
private fun conditionInIfStatement(node: ASTNode) {
- node.findAllNodesWithSpecificType(BINARY_EXPRESSION).forEach { binaryExprNode ->
+ node.findAllDescendantsWithSpecificType(BINARY_EXPRESSION).forEach { binaryExprNode ->
val condition = (binaryExprNode.psi as KtBinaryExpression)
if (isNullCheckBinaryExpression(condition)) {
when (condition.operationToken) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
index 45bbe16327..f0a09c9aae 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
@@ -4,7 +4,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.SMART_CAST_NEEDED
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.KotlinParser
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
@@ -39,7 +39,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* Rule that detects redundant explicit casts
*/
-class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rule", configRules, listOf(SMART_CAST_NEEDED)) {
+class SmartCastRule(configRules: List) : DiktatRule(
+ "smart-cast-rule",
+ configRules,
+ listOf(SMART_CAST_NEEDED)) {
override fun logic(node: ASTNode) {
if (node.elementType == FILE) {
val usages = collectLocalPropertiesWithUsages(node)
@@ -114,8 +117,7 @@ class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rul
@Suppress("TYPE_ALIAS")
private fun groupIsAndAsExpr(isExpr: List,
asExpr: List,
- prop: KtProperty)
- : Map> {
+ prop: KtProperty): Map> {
if (isExpr.isEmpty() && asExpr.isNotEmpty()) {
handleZeroIsCase(asExpr, prop)
return emptyMap()
@@ -170,7 +172,7 @@ class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rul
thenBlock?.let {
// Find all as expressions that are inside this current block
val asList = thenBlock
- .findAllNodesWithSpecificType(BINARY_WITH_TYPE)
+ .findAllDescendantsWithSpecificType(BINARY_WITH_TYPE)
.filter {
it.text.contains(" as ") && it.findParentNodeWithSpecificType(BLOCK) == thenBlock
}
@@ -178,7 +180,7 @@ class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rul
checkAsExpressions(asList, blocks)
}
?: run {
- val asList = then.findAllNodesWithSpecificType(BINARY_WITH_TYPE).filter { it.text.contains(KtTokens.AS_KEYWORD.value) }
+ val asList = then.findAllDescendantsWithSpecificType(BINARY_WITH_TYPE).filter { it.text.contains(KtTokens.AS_KEYWORD.value) }
checkAsExpressions(asList, blocks)
}
}
@@ -227,7 +229,7 @@ class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rul
val type = entry.getFirstChildWithType(WHEN_CONDITION_IS_PATTERN)!!
.getFirstChildWithType(TYPE_REFERENCE)?.text
- val callExpr = entry.findAllNodesWithSpecificType(BINARY_WITH_TYPE).firstOrNull()
+ val callExpr = entry.findAllDescendantsWithSpecificType(BINARY_WITH_TYPE).firstOrNull()
val blocks = listOf(IsExpressions(identifier, type ?: ""))
callExpr?.let {
@@ -264,8 +266,7 @@ class SmartCastRule(configRules: List) : DiktatRule("smart-cast-rul
* @return Map of property and list of expressions
*/
@Suppress("TYPE_ALIAS")
- private fun collectReferenceList(propertiesToUsages: Map>)
- : Map> =
+ private fun collectReferenceList(propertiesToUsages: Map>): Map> =
propertiesToUsages.mapValues { (_, value) ->
value.filter { entry ->
entry.parent.node.elementType == IS_EXPRESSION || entry.parent.node.elementType == BINARY_WITH_TYPE
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/TypeAliasRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/TypeAliasRule.kt
index 41e46615b6..cd8d5dca83 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/TypeAliasRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/TypeAliasRule.kt
@@ -19,7 +19,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
* This rule checks if variable has long type reference and two or more nested generics.
* Length type reference can be configured
*/
-class TypeAliasRule(configRules: List) : DiktatRule("type-alias", configRules, listOf(TYPE_ALIAS)) {
+class TypeAliasRule(configRules: List) : DiktatRule(
+ "type-alias",
+ configRules,
+ listOf(TYPE_ALIAS)) {
override fun logic(node: ASTNode) {
if (node.elementType == TYPE_REFERENCE && node
.parents()
@@ -34,7 +37,7 @@ class TypeAliasRule(configRules: List) : DiktatRule("type-alias", c
*/
private fun checkTypeReference(node: ASTNode, config: TypeAliasConfiguration) {
if (node.textLength > config.typeReferenceLength) {
- if (node.findAllNodesWithSpecificType(LT).size > 1 || node.findAllNodesWithSpecificType(VALUE_PARAMETER).size > 1) {
+ if (node.findAllDescendantsWithSpecificType(LT).size > 1 || node.findAllDescendantsWithSpecificType(VALUE_PARAMETER).size > 1) {
TYPE_ALIAS.warn(configRules, emitWarn, isFixMode, "too long type reference", node.startOffset, node)
}
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/VariableGenericTypeDeclarationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/VariableGenericTypeDeclarationRule.kt
index 07a24644c7..b2027af05c 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/VariableGenericTypeDeclarationRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/VariableGenericTypeDeclarationRule.kt
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.psi.KtProperty
* Recommended: val myVariable: Map = emptyMap()
*/
// FIXME: we now don't have access to return types, so we can perform this check only if explicit type is present, but should be able also if it's not.
-class VariableGenericTypeDeclarationRule(configRules: List) : DiktatRule("variable-generic-type", configRules,
+class VariableGenericTypeDeclarationRule(configRules: List) : DiktatRule(
+ "variable-generic-type",
+ configRules,
listOf(GENERIC_VARIABLE_WRONG_DECLARATION)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/calculations/AccurateCalculationsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/calculations/AccurateCalculationsRule.kt
index cb40295a68..fb32b85809 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/calculations/AccurateCalculationsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/calculations/AccurateCalculationsRule.kt
@@ -24,7 +24,10 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
* Exception: allows arithmetic operations only when absolute value of result is immediately used in comparison
* Fixme: detect variables by type, not only floating-point literals
*/
-class AccurateCalculationsRule(configRules: List) : DiktatRule("accurate-calculations", configRules, listOf(FLOAT_IN_ACCURATE_CALCULATIONS)) {
+class AccurateCalculationsRule(configRules: List) : DiktatRule(
+ "accurate-calculations",
+ configRules,
+ listOf(FLOAT_IN_ACCURATE_CALCULATIONS)) {
private fun KtCallExpression?.isAbsOfFloat() = this
?.run {
(calleeExpression as? KtNameReferenceExpression)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AsyncAndSyncRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AsyncAndSyncRule.kt
index 927b6fb637..e79140b160 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AsyncAndSyncRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AsyncAndSyncRule.kt
@@ -17,7 +17,10 @@ import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
/**
* This rule finds if using runBlocking in asynchronous code
*/
-class AsyncAndSyncRule(configRules: List) : DiktatRule("sync-in-async", configRules, listOf(RUN_BLOCKING_INSIDE_ASYNC)) {
+class AsyncAndSyncRule(configRules: List) : DiktatRule(
+ "sync-in-async",
+ configRules,
+ listOf(RUN_BLOCKING_INSIDE_ASYNC)) {
private val asyncList = listOf("async", "launch")
override fun logic(node: ASTNode) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AvoidNestedFunctionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AvoidNestedFunctionsRule.kt
index e597764640..56e047d780 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AvoidNestedFunctionsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/AvoidNestedFunctionsRule.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter5
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.AVOID_NESTED_FUNCTIONS
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.hasChildOfType
import org.cqfn.diktat.ruleset.utils.hasParent
@@ -24,7 +24,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* This rule checks for nested functions and warns if it finds any.
*/
-class AvoidNestedFunctionsRule(configRules: List) : DiktatRule("avoid-nested-functions", configRules, listOf(AVOID_NESTED_FUNCTIONS)) {
+class AvoidNestedFunctionsRule(configRules: List) : DiktatRule(
+ "avoid-nested-functions",
+ configRules,
+ listOf(AVOID_NESTED_FUNCTIONS)) {
override fun logic(node: ASTNode) {
if (node.elementType == FUN) {
handleNestedFunctions(node)
@@ -40,7 +43,7 @@ class AvoidNestedFunctionsRule(configRules: List) : DiktatRule("avo
AVOID_NESTED_FUNCTIONS.warnAndFix(configRules, emitWarn, isFixMode, "fun $funcName", node.startOffset, node,
canBeAutoCorrected = checkFunctionReferences(node)) {
// We take last nested function, then add and remove child from bottom to top
- val lastFunc = node.findAllNodesWithSpecificType(FUN).last()
+ val lastFunc = node.findAllDescendantsWithSpecificType(FUN).last()
val funcSeq = lastFunc
.parents()
.filter { it.elementType == FUN }
@@ -75,14 +78,14 @@ class AvoidNestedFunctionsRule(configRules: List) : DiktatRule("avo
@Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX")
private fun checkFunctionReferences(func: ASTNode): Boolean {
val localProperties: MutableList = mutableListOf()
- localProperties.addAll(func.findAllNodesWithSpecificType(PROPERTY))
+ localProperties.addAll(func.findAllDescendantsWithSpecificType(PROPERTY))
val propertiesNames: List = mutableListOf().apply {
addAll(localProperties.map { it.getFirstChildWithType(IDENTIFIER)!!.text })
addAll(getParameterNames(func))
}
.toList()
- return func.findAllNodesWithSpecificType(REFERENCE_EXPRESSION).all { propertiesNames.contains(it.text) }
+ return func.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION).all { propertiesNames.contains(it.text) }
}
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CheckInverseMethodRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CheckInverseMethodRule.kt
index 8c92f48a18..c5504798d7 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CheckInverseMethodRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CheckInverseMethodRule.kt
@@ -22,7 +22,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
* This rule checks if inverse method can be used.
* For example if there is !isEmpty() on collection call that it changes it to isNotEmpty()
*/
-class CheckInverseMethodRule(configRules: List) : DiktatRule("inverse-method", configRules, listOf(INVERSE_FUNCTION_PREFERRED)) {
+class CheckInverseMethodRule(configRules: List) : DiktatRule(
+ "inverse-method",
+ configRules,
+ listOf(INVERSE_FUNCTION_PREFERRED)) {
override fun logic(node: ASTNode) {
if (node.elementType == CALL_EXPRESSION && node.text in methodMap.keys) {
checkCallExpressionName(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CustomLabel.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CustomLabel.kt
index 518040b1b3..2359b3a8aa 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CustomLabel.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/CustomLabel.kt
@@ -17,7 +17,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* Rule that checks using custom label
*/
-class CustomLabel(configRules: List) : DiktatRule("custom-label", configRules, listOf(CUSTOM_LABEL)) {
+class CustomLabel(configRules: List) : DiktatRule(
+ "custom-label",
+ configRules,
+ listOf(CUSTOM_LABEL)) {
private val forEachReference = listOf("forEach", "forEachIndexed")
private val labels = listOf("@loop", "@forEach", "@forEachIndexed")
private val stopWords = listOf(RETURN, BREAK, CONTINUE)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionArgumentsSize.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionArgumentsSize.kt
index 5f07f8a9f2..a5ee1f9045 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionArgumentsSize.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionArgumentsSize.kt
@@ -14,7 +14,10 @@ import org.jetbrains.kotlin.psi.KtFunction
/**
* Rule that checks that function doesn't contains too many parameters
*/
-class FunctionArgumentsSize(configRules: List) : DiktatRule("argument-size", configRules, listOf(TOO_MANY_PARAMETERS)) {
+class FunctionArgumentsSize(configRules: List) : DiktatRule(
+ "argument-size",
+ configRules,
+ listOf(TOO_MANY_PARAMETERS)) {
private val configuration: FunctionArgumentsSizeConfiguration by lazy {
FunctionArgumentsSizeConfiguration(configRules.getRuleConfig(TOO_MANY_PARAMETERS)?.configuration ?: emptyMap())
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionLength.kt
index 670fef2377..22de1d88a6 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionLength.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/FunctionLength.kt
@@ -14,7 +14,10 @@ import org.jetbrains.kotlin.psi.KtFunction
/**
* Rule 5.1.1 check function length
*/
-class FunctionLength(configRules: List) : DiktatRule("function-length", configRules, listOf(TOO_LONG_FUNCTION)) {
+class FunctionLength(configRules: List) : DiktatRule(
+ "function-length",
+ configRules,
+ listOf(TOO_LONG_FUNCTION)) {
override fun logic(node: ASTNode) {
val configuration = FunctionLengthConfiguration(
configRules.getRuleConfig(TOO_LONG_FUNCTION)?.configuration ?: emptyMap()
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaLengthRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaLengthRule.kt
index f231535a74..de961cdbde 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaLengthRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaLengthRule.kt
@@ -13,7 +13,10 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
/**
* Rule 5.2.5 check lambda length without parameters
*/
-class LambdaLengthRule(configRules: List) : DiktatRule("lambda-length", configRules, listOf(TOO_MANY_LINES_IN_LAMBDA)) {
+class LambdaLengthRule(configRules: List) : DiktatRule(
+ "lambda-length",
+ configRules,
+ listOf(TOO_MANY_LINES_IN_LAMBDA)) {
private val configuration by lazy {
LambdaLengthConfiguration(
this.configRules.getRuleConfig(TOO_MANY_LINES_IN_LAMBDA)?.configuration ?: emptyMap()
@@ -35,7 +38,7 @@ class LambdaLengthRule(configRules: List) : DiktatRule("lambda-leng
node.treeParent.removeChild(node)
}
}
- val isIt = copyNode.findAllNodesWithSpecificType(ElementType.REFERENCE_EXPRESSION).map { re -> re.text }.contains("it")
+ val isIt = copyNode.findAllDescendantsWithSpecificType(ElementType.REFERENCE_EXPRESSION).map { re -> re.text }.contains("it")
val parameters = node.findChildByType(ElementType.FUNCTION_LITERAL)?.findChildByType(ElementType.VALUE_PARAMETER_LIST)
if (parameters == null && isIt) {
TOO_MANY_LINES_IN_LAMBDA.warn(configRules, emitWarn, isFixMode,
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaParameterOrder.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaParameterOrder.kt
index 28bf1a9082..efa7d4fae2 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaParameterOrder.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/LambdaParameterOrder.kt
@@ -15,7 +15,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
/**
* Rule that checks if parameter with function type is the last in parameter list
*/
-class LambdaParameterOrder(configRules: List) : DiktatRule("lambda-parameter-order", configRules, listOf(LAMBDA_IS_NOT_LAST_PARAMETER)) {
+class LambdaParameterOrder(configRules: List) : DiktatRule(
+ "lambda-parameter-order",
+ configRules,
+ listOf(LAMBDA_IS_NOT_LAST_PARAMETER)) {
override fun logic(node: ASTNode) {
if (node.elementType == ElementType.FUN) {
checkArguments(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/NestedFunctionBlock.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/NestedFunctionBlock.kt
index dc537c668a..bb93f77f3e 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/NestedFunctionBlock.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/NestedFunctionBlock.kt
@@ -5,7 +5,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.Warnings.NESTED_BLOCK
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.hasChildOfType
import com.pinterest.ktlint.core.ast.ElementType.CLASS
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
/**
* Rule 5.1.2 Nested blokcs
*/
-class NestedFunctionBlock(configRules: List) : DiktatRule("nested-block", configRules, listOf(NESTED_BLOCK)) {
+class NestedFunctionBlock(configRules: List) : DiktatRule(
+ "nested-block",
+ configRules,
+ listOf(NESTED_BLOCK)) {
private val configuration: NestedBlockConfiguration by lazy {
NestedBlockConfiguration(configRules.getRuleConfig(NESTED_BLOCK)?.configuration ?: emptyMap())
}
@@ -32,7 +35,7 @@ class NestedFunctionBlock(configRules: List) : DiktatRule("nested-b
}
private fun countNestedBlocks(node: ASTNode, maxNestedBlockCount: Long) {
- node.findAllNodesWithSpecificType(LBRACE)
+ node.findAllDescendantsWithSpecificType(LBRACE)
.reversed()
.forEach { lbraceNode ->
val blockParent = lbraceNode
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/OverloadingArgumentsFunction.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/OverloadingArgumentsFunction.kt
index 632db0609e..9c67a1b7fe 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/OverloadingArgumentsFunction.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter5/OverloadingArgumentsFunction.kt
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
/**
* Rule that suggests to use functions with default parameters instead of multiple overloads
*/
-class OverloadingArgumentsFunction(configRules: List) : DiktatRule("overloading-default-values", configRules,
+class OverloadingArgumentsFunction(configRules: List) : DiktatRule(
+ "overloading-default-values",
+ configRules,
listOf(WRONG_OVERLOADING_FUNCTION_ARGUMENTS)) {
override fun logic(node: ASTNode) {
if (node.elementType == FUN) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidEmptyPrimaryConstructor.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidEmptyPrimaryConstructor.kt
index d90f3ae304..d5a500660f 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidEmptyPrimaryConstructor.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidEmptyPrimaryConstructor.kt
@@ -11,7 +11,10 @@ import org.jetbrains.kotlin.psi.KtClass
/**
* This rule checks if a class has an empty primary constructor.
*/
-class AvoidEmptyPrimaryConstructor(configRules: List) : DiktatRule("avoid-empty-primary-constructor", configRules, listOf(EMPTY_PRIMARY_CONSTRUCTOR)) {
+class AvoidEmptyPrimaryConstructor(configRules: List) : DiktatRule(
+ "avoid-empty-primary-constructor",
+ configRules,
+ listOf(EMPTY_PRIMARY_CONSTRUCTOR)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS) {
checkClass(node.psi as KtClass)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidUtilityClass.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidUtilityClass.kt
index 627f55f1e2..9144aac9d5 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidUtilityClass.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/AvoidUtilityClass.kt
@@ -24,7 +24,10 @@ import org.jetbrains.kotlin.psi.psiUtil.children
/**
* Rule 6.4.1 checks that class/object, with a word "util" in its name, has only functions.
*/
-class AvoidUtilityClass(configRules: List) : DiktatRule("avoid-utility-class", configRules, listOf(AVOID_USING_UTILITY_CLASS)) {
+class AvoidUtilityClass(configRules: List) : DiktatRule(
+ "avoid-utility-class",
+ configRules,
+ listOf(AVOID_USING_UTILITY_CLASS)) {
override fun logic(node: ASTNode) {
val config = configRules.getCommonConfiguration()
val filePath = node.getRootNode().getFilePath()
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/CustomGetterSetterRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/CustomGetterSetterRule.kt
index 9002f51737..647ef47b84 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/CustomGetterSetterRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/CustomGetterSetterRule.kt
@@ -15,7 +15,10 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
/**
* Inspection that checks that no custom getters and setters are used for properties.
*/
-class CustomGetterSetterRule(configRules: List) : DiktatRule("custom-getter-setter", configRules, listOf(CUSTOM_GETTERS_SETTERS)) {
+class CustomGetterSetterRule(configRules: List) : DiktatRule(
+ "custom-getter-setter",
+ configRules,
+ listOf(CUSTOM_GETTERS_SETTERS)) {
override fun logic(node: ASTNode) {
if (node.elementType == PROPERTY_ACCESSOR) {
checkForCustomGetersSetters(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsInFileRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsInFileRule.kt
index e3a66b4389..ebf37b90d5 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsInFileRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsInFileRule.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.EXTENSION_FUNCTION_WITH_CLASS
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
@@ -20,7 +20,9 @@ import org.jetbrains.kotlin.psi.KtFunction
/**
* This rule checks if there are any extension functions for the class in the same file, where it is defined
*/
-class ExtensionFunctionsInFileRule(configRules: List) : DiktatRule("extension-functions-class-file", configRules,
+class ExtensionFunctionsInFileRule(configRules: List) : DiktatRule(
+ "extension-functions-class-file",
+ configRules,
listOf(EXTENSION_FUNCTION_WITH_CLASS)) {
override fun logic(node: ASTNode) {
if (node.elementType == ElementType.FILE) {
@@ -34,7 +36,7 @@ class ExtensionFunctionsInFileRule(configRules: List) : DiktatRule(
@Suppress("UnsafeCallOnNullableType")
private fun collectAllClassNames(node: ASTNode): List {
- val classes = node.findAllNodesWithSpecificType(CLASS)
+ val classes = node.findAllDescendantsWithSpecificType(CLASS)
return classes.map { (it.psi as KtClass).name!! }
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt
index 58e4ea2f72..0ff833175a 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.EXTENSION_FUNCTION_SAME_SIGNATURE
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.findChildAfter
import org.cqfn.diktat.ruleset.utils.findChildBefore
import org.cqfn.diktat.ruleset.utils.findLeafWithSpecificType
@@ -32,7 +32,9 @@ internal typealias SimilarSignatures = List) : DiktatRule("extension-functions-same-name", configRules,
+class ExtensionFunctionsSameNameRule(configRules: List) : DiktatRule(
+ "extension-functions-same-name",
+ configRules,
listOf(EXTENSION_FUNCTION_SAME_SIGNATURE)) {
override fun logic(node: ASTNode) {
/**
@@ -51,7 +53,7 @@ class ExtensionFunctionsSameNameRule(configRules: List) : DiktatRul
@Suppress("UnsafeCallOnNullableType", "TYPE_ALIAS")
private fun collectAllRelatedClasses(node: ASTNode): List> {
val classListWithInheritance = node
- .findAllNodesWithSpecificType(CLASS)
+ .findAllDescendantsWithSpecificType(CLASS)
.filterNot { (it.psi as KtClass).isInterface() }
.filter { it.hasChildOfType(SUPER_TYPE_LIST) }
@@ -70,7 +72,7 @@ class ExtensionFunctionsSameNameRule(configRules: List) : DiktatRul
@Suppress("UnsafeCallOnNullableType", "TYPE_ALIAS")
private fun collectAllExtensionFunctions(node: ASTNode): SimilarSignatures {
- val extensionFunctionList = node.findAllNodesWithSpecificType(FUN).filter { it.hasChildOfType(TYPE_REFERENCE) && it.hasChildOfType(DOT) }
+ val extensionFunctionList = node.findAllDescendantsWithSpecificType(FUN).filter { it.hasChildOfType(TYPE_REFERENCE) && it.hasChildOfType(DOT) }
val distinctFunctionSignatures: MutableMap = mutableMapOf() // maps function signatures on node it is used by
val extensionFunctionsPairs: MutableList> = mutableListOf() // pairs extension functions with same signature
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ImplicitBackingPropertyRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ImplicitBackingPropertyRule.kt
index be46671279..53a4398bd2 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ImplicitBackingPropertyRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/ImplicitBackingPropertyRule.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.NO_CORRESPONDING_PROPERTY
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.hasAnyChildOfTypes
import org.cqfn.diktat.ruleset.utils.hasChildOfType
@@ -24,7 +24,9 @@ import org.jetbrains.kotlin.psi.KtProperty
/**
* This rule checks if there is a backing property for field with property accessors, in case they don't use field keyword
*/
-class ImplicitBackingPropertyRule(configRules: List) : DiktatRule("implicit-backing-property", configRules,
+class ImplicitBackingPropertyRule(configRules: List) : DiktatRule(
+ "implicit-backing-property",
+ configRules,
listOf(NO_CORRESPONDING_PROPERTY)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS_BODY) {
@@ -48,7 +50,7 @@ class ImplicitBackingPropertyRule(configRules: List) : DiktatRule("
}
private fun validateAccessors(node: ASTNode, propsWithBackSymbol: List) {
- val accessors = node.findAllNodesWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) } // exclude get with expression body
+ val accessors = node.findAllDescendantsWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) } // exclude get with expression body
accessors.filter { it.hasChildOfType(GET_KEYWORD) }.forEach { handleGetAccessors(it, node, propsWithBackSymbol) }
accessors.filter { it.hasChildOfType(SET_KEYWORD) }.forEach { handleSetAccessors(it, node, propsWithBackSymbol) }
@@ -60,12 +62,12 @@ class ImplicitBackingPropertyRule(configRules: List) : DiktatRule("
node: ASTNode,
propsWithBackSymbol: List) {
val refExprs = accessor
- .findAllNodesWithSpecificType(RETURN)
+ .findAllDescendantsWithSpecificType(RETURN)
.filterNot { it.hasChildOfType(DOT_QUALIFIED_EXPRESSION) }
- .flatMap { it.findAllNodesWithSpecificType(REFERENCE_EXPRESSION) }
+ .flatMap { it.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION) }
val localProps = accessor
- .findAllNodesWithSpecificType(PROPERTY)
+ .findAllDescendantsWithSpecificType(PROPERTY)
.map { (it.psi as KtProperty).name!! }
// If refExprs is empty then we assume that it returns some constant
if (refExprs.isNotEmpty()) {
@@ -78,7 +80,7 @@ class ImplicitBackingPropertyRule(configRules: List) : DiktatRule("
accessor: ASTNode,
node: ASTNode,
propsWithBackSymbol: List) {
- val refExprs = accessor.findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ val refExprs = accessor.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
// In set we don't check for local properties. At least one reference expression should contain field or _prop
if (refExprs.isNotEmpty()) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/PropertyAccessorFields.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/PropertyAccessorFields.kt
index cdd75f4376..7ac7671968 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/PropertyAccessorFields.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/PropertyAccessorFields.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.isGoingAfter
import com.pinterest.ktlint.core.ast.ElementType.BLOCK
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.psi.KtProperty
/**
* Rule check that never use the name of a variable in the custom getter or setter
*/
-class PropertyAccessorFields(configRules: List) : DiktatRule("getter-setter-fields", configRules, listOf(WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR)) {
+class PropertyAccessorFields(configRules: List) : DiktatRule(
+ "getter-setter-fields",
+ configRules,
+ listOf(WRONG_NAME_OF_VARIABLE_INSIDE_ACCESSOR)) {
override fun logic(node: ASTNode) {
if (node.elementType == PROPERTY_ACCESSOR) {
checkPropertyAccessor(node)
@@ -31,7 +34,7 @@ class PropertyAccessorFields(configRules: List) : DiktatRule("gette
private fun checkPropertyAccessor(node: ASTNode) {
val leftValue = node.treeParent.findChildByType(IDENTIFIER) ?: return
val firstReferenceWithSameName = node
- .findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ .findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
.mapNotNull { it.findChildByType(IDENTIFIER) }
.firstOrNull {
it.text == leftValue.text &&
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt
new file mode 100644
index 0000000000..ab48e27c1b
--- /dev/null
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt
@@ -0,0 +1,80 @@
+package org.cqfn.diktat.ruleset.rules.chapter6
+
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.EmitType
+import org.cqfn.diktat.ruleset.constants.Warnings.RUN_IN_SCRIPT
+import org.cqfn.diktat.ruleset.utils.*
+
+import com.pinterest.ktlint.core.Rule
+import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.LAMBDA_ARGUMENT
+import com.pinterest.ktlint.core.ast.ElementType.LAMBDA_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.PARENTHESIZED
+import com.pinterest.ktlint.core.ast.ElementType.SCRIPT_INITIALIZER
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement
+
+/**
+ * Rule that checks if kts script contains other functions except run code
+ * In .kts files allow use only property declaration, function, classes, and code inside `run` block
+ * In gradle.kts files allow to call expression and dot qualified expression in addition to everything used in .kts files
+ */
+class RunInScript(private val configRules: List) : Rule("run-script") {
+ private var isFixMode: Boolean = false
+ private lateinit var emitWarn: EmitType
+
+ override fun visit(
+ node: ASTNode,
+ autoCorrect: Boolean,
+ emit: EmitType
+ ) {
+ isFixMode = autoCorrect
+ emitWarn = emit
+
+ if (node.elementType == SCRIPT_INITIALIZER && node.getRootNode().getFilePath().isKotlinScript()) {
+ if (node.getRootNode().getFilePath().isGradleScript()) {
+ checkGradleNode(node)
+ } else {
+ checkScript(node)
+ }
+ }
+ }
+
+ private fun checkGradleNode(node: ASTNode) {
+ val astNode = if (node.firstChildNode.elementType == PARENTHESIZED) {
+ node.firstChildNode
+ } else {
+ node
+ }
+ if (!astNode.hasChildOfType(CALL_EXPRESSION) && !astNode.hasChildOfType(DOT_QUALIFIED_EXPRESSION)) {
+ RUN_IN_SCRIPT.warnAndFix(configRules, emitWarn, isFixMode, astNode.text, astNode.startOffset, astNode) {
+ val parent = astNode.treeParent
+ val newNode = KotlinParser().createNode("run {\n ${astNode.text}\n} \n")
+ val newScript = CompositeElement(SCRIPT_INITIALIZER)
+ parent.addChild(newScript, astNode)
+ newScript.addChild(newNode)
+ parent.removeChild(astNode)
+ }
+ }
+ }
+
+ private fun checkScript(node: ASTNode) {
+ val isLambdaArgument = node.firstChildNode.hasChildOfType(LAMBDA_ARGUMENT)
+ val isLambdaInsideValueArgument = node.firstChildNode.findChildByType(VALUE_ARGUMENT_LIST)?.findChildByType(VALUE_ARGUMENT)?.findChildByType(LAMBDA_EXPRESSION) != null
+ if (!(isLambdaArgument || isLambdaInsideValueArgument)) {
+ RUN_IN_SCRIPT.warnAndFix(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) {
+ if (node.firstChildNode.elementType != DOT_QUALIFIED_EXPRESSION) {
+ val parent = node.treeParent
+ val newNode = KotlinParser().createNode("run {\n ${node.text}\n} \n")
+ val newScript = CompositeElement(SCRIPT_INITIALIZER)
+ parent.addChild(newScript, node)
+ newScript.addChild(newNode)
+ parent.removeChild(node)
+ }
+ }
+ }
+ }
+}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/TrivialPropertyAccessors.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/TrivialPropertyAccessors.kt
index c2c821a9c1..144cf594b1 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/TrivialPropertyAccessors.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/TrivialPropertyAccessors.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.getIdentifierName
import org.cqfn.diktat.ruleset.utils.hasChildOfType
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.psi.KtPropertyAccessor
/**
* This rule checks if there are any trivial getters and setters and, if so, deletes them
*/
-class TrivialPropertyAccessors(configRules: List) : DiktatRule("trivial-property-accessors", configRules,
+class TrivialPropertyAccessors(configRules: List) : DiktatRule(
+ "trivial-property-accessors",
+ configRules,
listOf(TRIVIAL_ACCESSORS_ARE_NOT_RECOMMENDED)) {
override fun logic(node: ASTNode) {
if (node.elementType == PROPERTY_ACCESSOR) {
@@ -68,7 +70,7 @@ class TrivialPropertyAccessors(configRules: List) : DiktatRule("tri
@Suppress("UnsafeCallOnNullableType")
private fun handleGetAccessor(node: ASTNode) {
// It handles both cases: get() = ... and get() { return ... }
- val references = node.findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ val references = node.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
if (references.singleOrNull()?.text == "field") {
raiseWarning(node)
} else if (node.getChildren(null).size == ONE_CHILD_IN_ARRAY) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/UselessSupertype.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/UselessSupertype.kt
index c740b623aa..9e041c9309 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/UselessSupertype.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/UselessSupertype.kt
@@ -31,7 +31,10 @@ import java.util.HashMap
* Explicit supertype qualification should not be used if there is not clash between called methods
* fixme can't fix supertypes that are defined in other files.
*/
-class UselessSupertype(configRules: List) : DiktatRule("useless-override", configRules, listOf(USELESS_SUPERTYPE)) {
+class UselessSupertype(configRules: List) : DiktatRule(
+ "useless-override",
+ configRules,
+ listOf(USELESS_SUPERTYPE)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS) {
checkClass(node)
@@ -44,7 +47,7 @@ class UselessSupertype(configRules: List) : DiktatRule("useless-ove
?.findAllNodesWithCondition({ it.elementType in superType })
?.takeIf { it.isNotEmpty() } ?: return
val qualifiedSuperCalls = node
- .findAllNodesWithSpecificType(DOT_QUALIFIED_EXPRESSION)
+ .findAllDescendantsWithSpecificType(DOT_QUALIFIED_EXPRESSION)
.mapNotNull { findFunWithSuper(it) }
.ifEmpty { return }
if (superNodes.size == 1) {
@@ -90,10 +93,10 @@ class UselessSupertype(configRules: List) : DiktatRule("useless-ove
private fun findFunWithSuper(node: ASTNode) = Pair(
node.findChildByType(SUPER_EXPRESSION)
?.findChildByType(TYPE_REFERENCE)
- ?.findAllNodesWithSpecificType(IDENTIFIER)
+ ?.findAllDescendantsWithSpecificType(IDENTIFIER)
?.firstOrNull(),
node.findChildByType(CALL_EXPRESSION)
- ?.findAllNodesWithSpecificType(IDENTIFIER)
+ ?.findAllDescendantsWithSpecificType(IDENTIFIER)
?.firstOrNull())
.run {
if (first == null || second == null) null else first!! to second!!
@@ -111,7 +114,7 @@ class UselessSupertype(configRules: List) : DiktatRule("useless-ove
private fun findAllSupers(superTypeList: List, methodsName: List): Map? {
val fileNode = superTypeList.first().parent({ it.elementType == FILE })!!
val superNodesIdentifier = superTypeList.map {
- it.findAllNodesWithSpecificType(IDENTIFIER)
+ it.findAllDescendantsWithSpecificType(IDENTIFIER)
.first()
.text
}
@@ -124,7 +127,7 @@ class UselessSupertype(configRules: List) : DiktatRule("useless-ove
}
val functionNameMap: HashMap = hashMapOf()
superNodes.forEach { classBody ->
- val overrideFunctions = classBody.findAllNodesWithSpecificType(FUN)
+ val overrideFunctions = classBody.findAllDescendantsWithSpecificType(FUN)
.filter {
(if (classBody.treeParent.hasChildOfType(CLASS_KEYWORD)) it.findChildByType(MODIFIER_LIST)!!.hasChildOfType(OPEN_KEYWORD) else true) &&
it.getIdentifierName()!!.text in methodsName
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/AbstractClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/AbstractClassesRule.kt
index 4755f42b55..66b9fcc8c6 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/AbstractClassesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/AbstractClassesRule.kt
@@ -19,7 +19,10 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
/**
* Checks if abstract class has any abstract method. If not, warns that class should not be abstract
*/
-class AbstractClassesRule(configRules: List) : DiktatRule("abstract-classes", configRules, listOf(CLASS_SHOULD_NOT_BE_ABSTRACT)) {
+class AbstractClassesRule(configRules: List) : DiktatRule(
+ "abstract-classes",
+ configRules,
+ listOf(CLASS_SHOULD_NOT_BE_ABSTRACT)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS) {
val classBody = node.getFirstChildWithType(CLASS_BODY) ?: return
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/CompactInitialization.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/CompactInitialization.kt
index 807459f103..f9dfcf8b9c 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/CompactInitialization.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/CompactInitialization.kt
@@ -27,7 +27,10 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
* FixMe: When assigned variable's name is also a `this@apply`'s property, it should be changed to qualified name,
* e.g `this@Foo`. But for this we need a mechanism to determine declaration scope and it's label.
*/
-class CompactInitialization(configRules: List) : DiktatRule("class-compact-initialization", configRules, listOf(COMPACT_OBJECT_INITIALIZATION)) {
+class CompactInitialization(configRules: List) : DiktatRule(
+ "class-compact-initialization",
+ configRules,
+ listOf(COMPACT_OBJECT_INITIALIZATION)) {
private val kotlinParser by lazy { KotlinParser() }
override fun logic(node: ASTNode) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/DataClassesRule.kt
index de0f2b6d91..2f03d76afa 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/DataClassesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/DataClassesRule.kt
@@ -31,7 +31,10 @@ import org.jetbrains.kotlin.psi.KtPrimaryConstructor
/**
* This rule checks if class can be made as data class
*/
-class DataClassesRule(configRules: List) : DiktatRule("data-classes", configRules, listOf(USE_DATA_CLASS)) {
+class DataClassesRule(configRules: List) : DiktatRule(
+ "data-classes",
+ configRules,
+ listOf(USE_DATA_CLASS)) {
override fun logic(node: ASTNode) {
if (node.elementType == CLASS) {
handleClass(node)
@@ -53,7 +56,11 @@ class DataClassesRule(configRules: List) : DiktatRule("data-classes
USE_DATA_CLASS.warn(configRules, emitWarn, isFixMode, "${(node.psi as KtClass).name}", node.startOffset, node)
}
- @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX", "ComplexMethod")
+ @Suppress(
+ "UnsafeCallOnNullableType",
+ "FUNCTION_BOOLEAN_PREFIX",
+ "ComplexMethod"
+ )
private fun ASTNode.canBeDataClass(): Boolean {
val isNotPropertyInClassBody = findChildByType(CLASS_BODY)?.let { (it.psi as KtClassBody).properties.isEmpty() } ?: true
val constructorParametersNames: MutableList = mutableListOf()
@@ -75,7 +82,7 @@ class DataClassesRule(configRules: List) : DiktatRule("data-classes
if (constructorParametersNames.isNotEmpty()) {
val initBlocks = findChildByType(CLASS_BODY)?.getAllChildrenWithType(CLASS_INITIALIZER)
initBlocks?.forEach { init ->
- val refExpressions = init.findAllNodesWithSpecificType(REFERENCE_EXPRESSION)
+ val refExpressions = init.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION)
if (refExpressions.any { it.text in constructorParametersNames }) {
return false
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/InlineClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/InlineClassesRule.kt
index 24ba0c7fbd..7b42eba4a4 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/InlineClassesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/InlineClassesRule.kt
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
/**
* This rule checks if inline class can be used.
*/
-class InlineClassesRule(configRules: List) : DiktatRule("inline-classes", configRules, listOf(INLINE_CLASS_CAN_BE_USED)) {
+class InlineClassesRule(configRules: List) : DiktatRule(
+ "inline-classes",
+ configRules,
+ listOf(INLINE_CLASS_CAN_BE_USED)) {
override fun logic(node: ASTNode) {
val configuration = configRules.getCommonConfiguration()
if (node.elementType == CLASS && configuration.kotlinVersion >= ktVersion) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleConstructorRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleConstructorRule.kt
index 802c2fde9c..6924e6a508 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleConstructorRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleConstructorRule.kt
@@ -33,7 +33,10 @@ import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
* This rule ensures that if a class has a single constructor, this constructor is primary.
* Secondary constructor is converted into primary, statements that are not assignments are moved into an `init` block.
*/
-class SingleConstructorRule(configRules: List) : DiktatRule("single-constructor", configRules, listOf(SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY)) {
+class SingleConstructorRule(configRules: List) : DiktatRule(
+ "single-constructor",
+ configRules,
+ listOf(SINGLE_CONSTRUCTOR_SHOULD_BE_PRIMARY)) {
private val kotlinParser by lazy { KotlinParser() }
override fun logic(node: ASTNode) {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleInitRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleInitRule.kt
index eaa56a3620..8e0fbf7649 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleInitRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/SingleInitRule.kt
@@ -25,7 +25,10 @@ import org.jetbrains.kotlin.psi.psiUtil.children
/**
* The rule that checks whether a class has a single `init` block or multiple. Having multiple `init` blocks is a bad practice.
*/
-class SingleInitRule(configRules: List) : DiktatRule("multiple-init-block", configRules, listOf(MULTIPLE_INIT_BLOCKS)) {
+class SingleInitRule(configRules: List) : DiktatRule(
+ "multiple-init-block",
+ configRules,
+ listOf(MULTIPLE_INIT_BLOCKS)) {
override fun logic(node: ASTNode) {
when (node.elementType) {
CLASS_BODY -> handleInitBlocks(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/StatelessClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/StatelessClassesRule.kt
index 7ed2903736..f3c6ab6eb0 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/StatelessClassesRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/classes/StatelessClassesRule.kt
@@ -3,7 +3,7 @@ package org.cqfn.diktat.ruleset.rules.chapter6.classes
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.OBJECT_IS_PREFERRED
import org.cqfn.diktat.ruleset.rules.DiktatRule
-import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType
+import org.cqfn.diktat.ruleset.utils.findAllDescendantsWithSpecificType
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.hasChildOfType
@@ -27,15 +27,18 @@ import org.jetbrains.kotlin.psi.KtClass
/**
* This rule checks if class is stateless and if so changes it to object.
*/
-class StatelessClassesRule(configRules: List) : DiktatRule("stateless-class", configRules, listOf(OBJECT_IS_PREFERRED)) {
+class StatelessClassesRule(configRules: List) : DiktatRule(
+ "stateless-class",
+ configRules,
+ listOf(OBJECT_IS_PREFERRED)) {
override fun logic(node: ASTNode) {
// Fixme: We should find interfaces in all project and then check them
if (node.elementType == FILE) {
val interfacesNodes = node
- .findAllNodesWithSpecificType(CLASS)
+ .findAllDescendantsWithSpecificType(CLASS)
.filter { it.hasChildOfType(INTERFACE_KEYWORD) }
node
- .findAllNodesWithSpecificType(CLASS)
+ .findAllDescendantsWithSpecificType(CLASS)
.filterNot { it.hasChildOfType(INTERFACE_KEYWORD) }
.forEach { handleClass(it, interfacesNodes) }
}
@@ -61,8 +64,8 @@ class StatelessClassesRule(configRules: List) : DiktatRule("statele
private fun isStatelessClass(node: ASTNode): Boolean {
val properties = (node.psi as KtClass).getProperties()
- val functions = node.findAllNodesWithSpecificType(FUN)
- return properties.isNullOrEmpty() &&
+ val functions = node.findAllDescendantsWithSpecificType(FUN)
+ return properties.isEmpty() &&
functions.isNotEmpty() &&
!(node.psi as KtClass).hasExplicitPrimaryConstructor()
}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
index 8352db3b0f..9905f4a9a3 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
@@ -3,7 +3,11 @@
* FixMe: fix suppressed inspections on KDocs
*/
-@file:Suppress("FILE_NAME_MATCH_CLASS", "KDOC_WITHOUT_RETURN_TAG", "KDOC_WITHOUT_PARAM_TAG")
+@file:Suppress(
+ "FILE_NAME_MATCH_CLASS",
+ "KDOC_WITHOUT_RETURN_TAG",
+ "KDOC_WITHOUT_PARAM_TAG"
+)
package org.cqfn.diktat.ruleset.utils
@@ -88,7 +92,7 @@ fun ASTNode.isTextLengthInRange(range: IntRange): Boolean = this.textLength in r
* @return node with type [IDENTIFIER] or null if it is not present
*/
fun ASTNode.getIdentifierName(): ASTNode? =
- this.getChildren(null).find { it.elementType == ElementType.IDENTIFIER }
+ this.getFirstChildWithType(ElementType.IDENTIFIER)
/**
* getting first child name with TYPE_PARAMETER_LIST type
@@ -96,20 +100,12 @@ fun ASTNode.getIdentifierName(): ASTNode? =
* @return a node with type TYPE_PARAMETER_LIST or null if it is not present
*/
fun ASTNode.getTypeParameterList(): ASTNode? =
- this.getChildren(null).find { it.elementType == ElementType.TYPE_PARAMETER_LIST }
-
-/**
- * getting all children that have IDENTIFIER type
- *
- * @return a list of nodes
- */
-fun ASTNode.getAllIdentifierChildren(): List =
- this.getChildren(null).filter { it.elementType == ElementType.IDENTIFIER }
+ this.getFirstChildWithType(ElementType.TYPE_PARAMETER_LIST)
/**
* @return true if this node contains no error elements, false otherwise
*/
-fun ASTNode.isCorrect() = this.findAllNodesWithSpecificType(TokenType.ERROR_ELEMENT).isEmpty()
+fun ASTNode.isCorrect() = this.findAllDescendantsWithSpecificType(TokenType.ERROR_ELEMENT).isEmpty()
/**
* obviously returns list with children that match particular element type
@@ -139,7 +135,7 @@ fun ASTNode.replaceWhiteSpaceText(beforeNode: ASTNode, text: String) {
* @return a node or null if it was not found
*/
fun ASTNode.getFirstChildWithType(elementType: IElementType): ASTNode? =
- this.getChildren(null).find { it.elementType == elementType }
+ this.findChildByType(elementType)
/**
* Checks if the symbols in this node are at the end of line
@@ -411,14 +407,15 @@ fun ASTNode.numNewLines() = text.count { it == '\n' }
/**
* This method performs tree traversal and returns all nodes with specific element type
*/
-fun ASTNode.findAllNodesWithSpecificType(elementType: IElementType, withSelf: Boolean = true) =
+fun ASTNode.findAllDescendantsWithSpecificType(elementType: IElementType, withSelf: Boolean = true) =
findAllNodesWithCondition({ it.elementType == elementType }, withSelf)
/**
* This method performs tree traversal and returns all nodes which satisfy the condition
*/
@Suppress("LAMBDA_IS_NOT_LAST_PARAMETER")
-fun ASTNode.findAllNodesWithCondition(condition: (ASTNode) -> Boolean, withSelf: Boolean = true): List