diff --git a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/WarningsTableGenerator.kt b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/WarningsTableGenerator.kt index a84210621e..4327cb36eb 100644 --- a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/WarningsTableGenerator.kt +++ b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/WarningsTableGenerator.kt @@ -16,7 +16,7 @@ const val DIKTAT_GUIDE: String = "guide/diktat-coding-convention.md#" @Suppress("MagicNumber") fun generateRulesMapping() { // excluding dummy warning - val allWarnings = Warnings.values().filterNot { it == Warnings.DUMMY_TEST_WARNING } + val allWarnings = Warnings.values().filterNot { it == Warnings.DUMMY_TEST_WARNING } as MutableList allWarnings.sortBy { warn -> val numbers = warn.ruleId.split(".") val chapter = numbers[0].toInt() diff --git a/info/gradle/wrapper/gradle-wrapper.jar b/info/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..e708b1c023 Binary files /dev/null and b/info/gradle/wrapper/gradle-wrapper.jar differ diff --git a/info/gradle/wrapper/gradle-wrapper.properties b/info/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..442d9132ea --- /dev/null +++ b/info/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/info/gradlew b/info/gradlew new file mode 100644 index 0000000000..4f906e0c81 --- /dev/null +++ b/info/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/info/gradlew.bat b/info/gradlew.bat new file mode 100644 index 0000000000..107acd32c4 --- /dev/null +++ b/info/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/info/guide/diktat-coding-convention.md b/info/guide/diktat-coding-convention.md index 124b14ff2e..d0208c183b 100644 --- a/info/guide/diktat-coding-convention.md +++ b/info/guide/diktat-coding-convention.md @@ -38,6 +38,7 @@ I [Preface](#c0) * [3.1.2 Code blocks in the source file should be separated by one blank line](#r3.1.2) * [3.1.3 Import statements order](#r3.1.3) * [3.1.4 Order of declaration parts of class-like code structures](#r3.1.4) + * [3.1.5 Order of declaration of top-level code structures](#r3.1.5) * [3.2 Braces](#c3.2) * [3.2.1 Using braces in conditional statements and loop blocks](#r3.2.1) * [3.2.2 Opening braces are placed at the end of the line in *non-empty* blocks and block structures](#r3.2.2) @@ -87,8 +88,10 @@ I [Preface](#c0) * [5.2 Function arguments](#c5.2) * [5.2.1 The lambda parameter of the function should be placed at the end of the argument list](#r5.2.1) * [5.2.2 Number of function parameters should be limited to five](#r5.2.2) - * [5.2.3 Use default values for function arguments instead of overloading them](#r5.2.3) - * [5.2.5 Long lambdas should have explicit parameters](#r5.2.4) + * [5.2.3 Use default values for function arguments instead of overloading them](#r5.2.3) + * [5.2.4 Synchronizing code inside asynchronous code](#r5.2.4) + * [5.2.5 Long lambdas should have explicit parameters](#r5.2.5) + * [5.2.6 Avoid using unnecessary, custom label](#r5.2.6) [6. Classes, interfaces, and extension functions](#c6) * [6.1 Classes](#c6.1) @@ -102,10 +105,12 @@ I [Preface](#c0) * [6.1.8 Avoid using custom getters and setters](#r6.1.8) * [6.1.9 Never use the name of a variable in the custom getter or setter (possible_bug)](#r6.1.9) * [6.1.10 No trivial getters and setters are allowed in the code](#r6.1.10) - * [6.1.11 Use 'apply' for grouping object initialization](#r6.1.11) -* [6.2 Classes](#c6.2) + * [6.1.11 Use 'apply' for grouping object initialization](#r6.1.11) + * [6.1.12 Prefer Inline classes when a class has a single property](#r6.1.12) +* [6.2 Extension functions](#c6.2) * [6.2.1 Use extension functions for making logic of classes less coupled](#r6.2.1) - * [6.2.2 No extension functions with the same name and signature if they extend base and inheritor classes (possible_bug)](#r6.2.2) + * [6.2.2 No extension functions with the same name and signature if they extend base and inheritor classes (possible_bug)](#r6.2.2) + * [6.2.3 Don't use extension functions for the class in the same file](#r6.2.3) * [6.3 Interfaces](#c6.3) * [6.4 Objects](#c6.4) * [6.4.1 Instead of using utility classes/objects, use extensions](#r6.4.1) @@ -803,6 +808,7 @@ d) **Recommendation**: One `.kt` source file should contain only one class decla e) Avoid empty files that do not contain the code or contain only imports/comments/package name +f) Unused imports should be removed #### 3.1.3 Import statements order From top to bottom, the order is the following: @@ -853,6 +859,53 @@ If the classes are meant to be used externally, and are not referenced inside th **Exception:** All variants of a `(private) val` logger should be placed at the beginning of the class (`(private) val log`, `LOG`, `logger`, etc.). +#### 3.1.5 Order of declaration of top-level code structures +Kotlin allows several top-level declaration types: classes, objects, interfaces, properties and functions. +When declaring more than one class or zero classes (e.g. only functions), as per rule [2.2.1](#r2.2.1), you should document the whole file in the header KDoc. +When declaring top-level structures, keep the following order: +1. Top-level constants and properties (following same order as properties inside a class: `const val`,`val`, `lateinit var`, `var`) +2. Interfaces, classes and objects (grouped by their visibility modifiers) +3. Extension functions +4. Other functions + +**Note**: +Extension functions shouldn't have receivers declared in the same file according to [rule 6.2.3](#r6.2.3) + +Valid example: +```kotlin +package org.cqfn.diktat.example + +const val CONSTANT = 42 + +val topLevelProperty = "String constant" + +interface IExample + +class Example : IExample + +private class Internal + +fun Other.asExample(): Example { /* ... */ } + +private fun Other.asInternal(): Internal { /* ... */ } + +fun doStuff() { /* ... */ } +``` + +**Note**: +kotlin scripts (.kts) allow arbitrary code to be placed on the top level. When writing kotlin scripts, you should first declare all properties, classes +and functions. Only then you should execute functions on top level. It is still recommended wrapping logic inside functions and avoid using top-level statements +for function calls or wrapping blocks of code in top-level scope functions like `run`. + +Example: +```kotlin +/* class declarations */ +/* function declarations */ +run { + // call functions here +} +``` + ### 3.2 Braces This section describes the general rules of using braces in your code. @@ -1232,6 +1285,23 @@ fun foo( } ``` +same should be done for function calls/constructor arguments/e.t.c + +Kotlin supports trailing commas in the following cases: + +Enumerations +Value arguments +Class properties and parameters +Function value parameters +Parameters with optional type (including setters) +Indexing suffix +Lambda parameters +when entry +Collection literals (in annotations) +Type arguments +Type parameters +Destructuring declarations + 8) If the supertype list has more than two elements, they should be separated by newlines. **Valid example:** @@ -1839,8 +1909,8 @@ val anotherVal = myVar?.also { // example 4 myVar?.let { - println("null") -} ?: run { println("not null") } + println("not null") +} ?: run { println("null") } ``` **Exceptions:** @@ -2011,6 +2081,32 @@ GlobalScope.async { #### 5.2.5 Long lambdas should have explicit parameters The lambda without parameters shouldn't be too long. If a lambda is too long, it can confuse the user. Lambda without parameters should consist of 10 lines (non-empty and non-comment) in total. + +#### 5.2.6 Avoid using unnecessary, custom label +Expressions with unnecessary, custom labels generally increase complexity and worsen the maintainability of the code. + +**Invalid example**: +```kotlin +run lab@ { + list.forEach { + return@lab + } +} +``` + +**Valid example**: +```kotlin +list.forEachIndexed { index, i -> + return@forEachIndexed +} + +lab@ for(i: Int in q) { + for (j: Int in q) { + println(i) + break@lab + } +} +``` # 6. Classes, interfaces, and extension functions ### 6.1 Classes @@ -2370,6 +2466,23 @@ fun main() { } ``` +### 6.1.12 Prefer Inline classes when a class has a single property +If a class has only one immutable property, then it can be converted to the inline class. + +Sometimes it is necessary for business logic to create a wrapper around some type. However, it introduces runtime overhead due to additional heap allocations. Moreover, if the wrapped type is primitive, the performance hit is terrible, because primitive types are usually heavily optimized by the runtime, while their wrappers don't get any special treatment. + +**Invalid example**: +```kotlin +class Password { + val value: String +} +``` + +**Valid example**: +```kotlin +inline class Password(val value: String) +``` + ### 6.2 Extension functions This section describes the rules of using extension functions in your code. @@ -2402,6 +2515,20 @@ fun printClassName(s: A) { println(s.foo()) } fun main() { printClassName(B()) } ``` +#### 6.2.3 Don't use extension functions for the class in the same file +You should not use extension functions for the class in the same file, where it is defined. + +**Invalid example**: +```kotlin +class SomeClass { + +} + +fun SomeClass.deleteAllSpaces() { + +} +``` + ### 6.3 Interfaces An `Interface` in Kotlin can contain declarations of abstract methods, as well as method implementations. What makes them different from abstract classes is that interfaces cannot store state. @@ -2448,5 +2575,20 @@ interface I { object O: I { override fun foo() {} +} +``` + +#### 6.5.1 kts files should wrap logic into top-level scope +It is still recommended wrapping logic inside functions and avoid using top-level statements for function calls or wrapping blocks of code +in top-level scope functions like `run`. + +**Valid example**: +``` +run { + // some code +} + +fun foo() { + } ``` \ No newline at end of file diff --git a/info/guide/guide-chapter-4.md b/info/guide/guide-chapter-4.md index a11465ac05..11378b79a0 100644 --- a/info/guide/guide-chapter-4.md +++ b/info/guide/guide-chapter-4.md @@ -260,8 +260,8 @@ val anotherVal = myVar?.also { // example 4 myVar?.let { - println("null") -} ?: run { println("not null") } + println("not null") +} ?: run { println("null") } ``` **Exceptions:** diff --git a/wp/sections/appendix.tex b/wp/sections/appendix.tex index 70a6b17afb..8bf08f22f9 100644 --- a/wp/sections/appendix.tex +++ b/wp/sections/appendix.tex @@ -209,6 +209,8 @@ \section*{Table of contents} \hspace{1.0cm}\hyperref[sec:3.1.4]{ 3.1.4 Order of declaration parts of class-like code structures} +\hspace{1.0cm}\hyperref[sec:3.1.5]{ 3.1.5 Order of declaration of top-level code structures} + \hspace{0.5cm}\hyperref[sec:3.2]{ 3.2 Braces} \hspace{1.0cm}\hyperref[sec:3.2.1]{ 3.2.1 Using braces in conditional statements and loop blocks} @@ -309,8 +311,12 @@ \section*{Table of contents} \hspace{1.0cm}\hyperref[sec:5.2.3]{ 5.2.3 Use default values for function arguments instead of overloading them} +\hspace{1.0cm}\hyperref[sec:5.2.4]{ 5.2.4 Synchronizing code inside asynchronous code} + \hspace{1.0cm}\hyperref[sec:5.2.5]{ 5.2.5 Long lambdas should have explicit parameters} +\hspace{1.0cm}\hyperref[sec:5.2.6]{ 5.2.6 Avoid using unnecessary, custom label} + \hspace{0.0cm}\hyperref[sec:]{} \hspace{0.0cm}\hyperref[sec:6.]{6. Classes, interfaces, and extension functions} @@ -339,12 +345,16 @@ \section*{Table of contents} \hspace{1.0cm}\hyperref[sec:6.1.11]{ 6.1.11 Use 'apply' for grouping object initialization} -\hspace{0.5cm}\hyperref[sec:6.2]{ 6.2 Classes} +\hspace{1.0cm}\hyperref[sec:6.1.12]{ 6.1.12 Prefer Inline classes when a class has a single property} + +\hspace{0.5cm}\hyperref[sec:6.2]{ 6.2 Extension functions} \hspace{1.0cm}\hyperref[sec:6.2.1]{ 6.2.1 Use extension functions for making logic of classes less coupled} \hspace{1.0cm}\hyperref[sec:6.2.2]{ 6.2.2 No extension functions with the same name and signature if they extend base and inheritor classes (possible_bug)} +\hspace{1.0cm}\hyperref[sec:6.2.3]{ 6.2.3 Don't use extension functions for the class in the same file} + \hspace{0.5cm}\hyperref[sec:6.3]{ 6.3 Interfaces} \hspace{0.5cm}\hyperref[sec:6.4]{ 6.4 Objects} @@ -542,7 +552,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}} \begin{center} -\begin{tabular}{ |p{5,0cm}|p{5,0cm}|p{5,0cm}| } +\begin{tabular}{ |p{5.0cm}|p{5.0cm}|p{5.0cm}| } \hline @@ -588,7 +598,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}} \begin{center} -\begin{tabular}{ |p{5,0cm}|p{5,0cm}|p{5,0cm}| } +\begin{tabular}{ |p{5.0cm}|p{5.0cm}|p{5.0cm}| } \hline @@ -632,7 +642,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}} \begin{center} -\begin{tabular}{ |p{7,5cm}|p{7,5cm}| } +\begin{tabular}{ |p{7.5cm}|p{7.5cm}| } \hline @@ -1583,6 +1593,8 @@ \subsubsection*{\textbf{3.1.2 Code blocks in the source file should be separated +f) Unused imports should be removed + \subsubsection*{\textbf{3.1.3 Import statements order}} \leavevmode\newline @@ -1672,6 +1684,77 @@ \subsubsection*{\textbf{3.1.4 Order of declaration parts of class-like code stru +\subsubsection*{\textbf{3.1.5 Order of declaration of top-level code structures}} +\leavevmode\newline + +\label{sec:3.1.5} + +Kotlin allows several top-level declaration types: classes, objects, interfaces, properties and functions. + +When declaring more than one class or zero classes (e.g. only functions), as per rule [2.2.1], you should document the whole file in the header KDoc. + +When declaring top-level structures, keep the following order: + +1. Top-level constants and properties (following same order as properties inside a class: \textbf{const val},\textbf{val}, \textbf{lateinit var}, \textbf{var}) + +2. Interfaces, classes and objects (grouped by their visibility modifiers) + +3. Extension functions + +4. Other functions + + + +\textbf{Note}: + +Extension functions shouldn't have receivers declared in the same file according to [rule 6.2.3] + + + +Valid example: + +\begin{lstlisting}[language=Kotlin] +package org.cqfn.diktat.example + +const val CONSTANT = 42 + +val topLevelProperty = "String constant" + +interface IExample + +class Example : IExample + +private class Internal + +fun Other.asExample(): Example { /* ... */ } + +private fun Other.asInternal(): Internal { /* ... */ } + +fun doStuff() { /* ... */ } +\end{lstlisting} + + +\textbf{Note}: + +kotlin scripts (.kts) allow arbitrary code to be placed on the top level. When writing kotlin scripts, you should first declare all properties, classes + +and functions. Only then you should execute functions on top level. It is still recommended wrapping logic inside functions and avoid using top-level statements + +for function calls or wrapping blocks of code in top-level scope functions like \textbf{run}. + + + +Example: + +\begin{lstlisting}[language=Kotlin] +/* class declarations */ +/* function declarations */ +run { + // call functions here +} +\end{lstlisting} + + \subsection*{\textbf{3.2 Braces}} \label{sec:3.2} @@ -2243,6 +2326,40 @@ \subsubsection*{\textbf{3.6.2 Rules for line-breaking}} \end{lstlisting} +same should be done for function calls/constructor arguments/e.t.c + + + +Kotlin supports trailing commas in the following cases: + + + +Enumerations + +Value arguments + +Class properties and parameters + +Function value parameters + +Parameters with optional type (including setters) + +Indexing suffix + +Lambda parameters + +when entry + +Collection literals (in annotations) + +Type arguments + +Type parameters + +Destructuring declarations + + + 8) If the supertype list has more than two elements, they should be separated by newlines. @@ -3211,8 +3328,8 @@ \subsubsection*{\textbf{4.3.3 Null-safety}} // example 4 myVar?.let { - println("null") -} ?: run { println("not null") } + println("not null") +} ?: run { println("null") } \end{lstlisting} @@ -3513,6 +3630,42 @@ \subsubsection*{\textbf{5.2.5 Long lambdas should have explicit parameters}} If a lambda is too long, it can confuse the user. Lambda without parameters should consist of 10 lines (non-empty and non-comment) in total. + + +\subsubsection*{\textbf{5.2.6 Avoid using unnecessary}} +\leavevmode\newline + +\label{sec:5.2.6} + +Expressions with unnecessary, custom labels generally increase complexity and worsen the maintainability of the code. + + + +\textbf{Invalid example}: + +\begin{lstlisting}[language=Kotlin] +run lab@ { + list.forEach { + return@lab + } +} +\end{lstlisting} + + +\textbf{Valid example}: + +\begin{lstlisting}[language=Kotlin] +list.forEachIndexed { index, i -> + return@forEachIndexed +} + +lab@ for(i: Int in q) { + for (j: Int in q) { + println(i) + break@lab + } +} +\end{lstlisting} \section*{\textbf{6. Classes}} \label{sec:6.} @@ -4025,6 +4178,35 @@ \subsubsection*{\textbf{6.1.11 Use 'apply' for grouping object initialization}} \end{lstlisting} +\subsubsection*{\textbf{6.1.12 Prefer Inline classes when a class has a single property}} +\leavevmode\newline + +\label{sec:6.1.12} + +If a class has only one immutable property, then it can be converted to the inline class. + + + +Sometimes it is necessary for business logic to create a wrapper around some type. However, it introduces runtime overhead due to additional heap allocations. Moreover, if the wrapped type is primitive, the performance hit is terrible, because primitive types are usually heavily optimized by the runtime, while their wrappers don't get any special treatment. + + + +\textbf{Invalid example}: + +\begin{lstlisting}[language=Kotlin] +class Password { + val value: String +} +\end{lstlisting} + + +\textbf{Valid example}: + +\begin{lstlisting}[language=Kotlin] +inline class Password(val value: String) +\end{lstlisting} + + \subsection*{\textbf{6.2 Extension functions}} \label{sec:6.2} @@ -4081,6 +4263,28 @@ \subsubsection*{\textbf{6.2.2 No extension functions with the same name and sign \end{lstlisting} +\subsubsection*{\textbf{6.2.3 Don't use extension functions for the class in the same file}} +\leavevmode\newline + +\label{sec:6.2.3} + +You should not use extension functions for the class in the same file, where it is defined. + + + +\textbf{Invalid example}: + +\begin{lstlisting}[language=Kotlin] +class SomeClass { + +} + +fun SomeClass.deleteAllSpaces() { + +} +\end{lstlisting} + + \subsection*{\textbf{6.3 Interfaces}} \label{sec:6.3} @@ -4157,5 +4361,29 @@ \subsubsection*{\textbf{6.4.2 Objects should be used for Stateless Interfaces}} object O: I { override fun foo() {} +} +\end{lstlisting} + + +\subsubsection*{\textbf{6.5.1 kts files should wrap logic into top-level scope}} +\leavevmode\newline + +\label{sec:6.5.1} + +It is still recommended wrapping logic inside functions and avoid using top-level statements for function calls or wrapping blocks of code + +in top-level scope functions like \textbf{run}. + + + +\textbf{Valid example}: + +\begin{lstlisting}[language=Kotlin] +run { + // some code +} + +fun foo() { + } \end{lstlisting} \ No newline at end of file