pinterest / ktlint

An anti-bikeshedding Kotlin linter with built-in formatter
https://pinterest.github.io/ktlint/
MIT License
6.07k stars 504 forks source link

Internal Error occurred when formatting a file with multi-line KDoc comment #2535

Closed moritasoshi closed 4 months ago

moritasoshi commented 5 months ago

Expected Behavior

Observed Behavior

An Internal Error occurred when formatting a file with multi-line KDoc comment.

The file I tried to format:

class ClassA
/**
 * some comment 
 */(paramA: String)

Executed command & Error logs(stacktrace):

$ ktlint --format ClassA.kt
/tmp/ClassA.kt:0:0: Internal Error (rule 'standard:parameter-list-wrapping') in ClassA.kt at position '0:0. Please create a ticket at https://github.com/pinterest/ktlint/issues and provide the source code that triggered an error.
com.pinterest.ktlint.rule.engine.api.KtLintRuleException: Rule 'standard:parameter-list-wrapping' throws exception in file 'ClassA.kt' at position (0:0)
   Rule maintainer: KtLint
   Issue tracker  : https://github.com/pinterest/ktlint/issues
   Repository     : https://github.com/pinterest/ktlint
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRule(RuleExecutionContext.kt:65)
    at com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine$format$3.invoke(KtLintRuleEngine.kt:146)
    at com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine$format$3.invoke(KtLintRuleEngine.kt:145)
    at com.pinterest.ktlint.rule.engine.internal.VisitorProvider$visitor$3.invoke(VisitorProvider.kt:46)
    at com.pinterest.ktlint.rule.engine.internal.VisitorProvider$visitor$3.invoke(VisitorProvider.kt:44)
    at com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine.format(KtLintRuleEngine.kt:145)
    at com.pinterest.ktlint.cli.internal.KtlintCommandLine.format(KtlintCommandLine.kt:485)
    at com.pinterest.ktlint.cli.internal.KtlintCommandLine.process(KtlintCommandLine.kt:471)
    at com.pinterest.ktlint.cli.internal.KtlintCommandLine.access$process(KtlintCommandLine.kt:57)
    at com.pinterest.ktlint.cli.internal.KtlintCommandLine$lintFiles$3.invoke$lambda$0(KtlintCommandLine.kt:411)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.NullPointerException: Cannot invoke "org.jetbrains.kotlin.com.intellij.psi.impl.source.codeStyle.IndentHelper.getIndent(org.jetbrains.kotlin.com.intellij.psi.PsiFile, org.jetbrains.kotlin.com.intellij.lang.ASTNode)" because the return value of "org.jetbrains.kotlin.com.intellij.psi.impl.source.codeStyle.IndentHelper.getInstance()" is null
    at org.jetbrains.kotlin.com.intellij.psi.impl.source.codeStyle.CodeEditUtil.saveWhitespacesInfo(CodeEditUtil.java:108)
    at org.jetbrains.kotlin.com.intellij.psi.impl.source.codeStyle.CodeEditUtil.removeChildren(CodeEditUtil.java:120)
    at org.jetbrains.kotlin.com.intellij.psi.impl.source.codeStyle.CodeEditUtil.removeChild(CodeEditUtil.java:35)
    at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement.deleteChildInternal(CompositeElement.java:451)
    at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement.delete(LeafPsiElement.java:182)
    at com.pinterest.ktlint.ruleset.standard.rules.ParameterListWrappingRule.wrapParameterInList(ParameterListWrappingRule.kt:241)
    at com.pinterest.ktlint.ruleset.standard.rules.ParameterListWrappingRule.wrapParameterList(ParameterListWrappingRule.kt:192)
    at com.pinterest.ktlint.ruleset.standard.rules.ParameterListWrappingRule.beforeVisitChildNodes(ParameterListWrappingRule.kt:79)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$1.invoke(RuleExecutionContext.kt:125)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$1.invoke(RuleExecutionContext.kt:124)
    at com.pinterest.ktlint.rule.engine.internal.SuppressHandler.handle(SuppressHandler.kt:28)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:124)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:93)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.access$executeRuleOnNodeRecursively(RuleExecutionContext.kt:30)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:132)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.SuppressHandler.handle(SuppressHandler.kt:28)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:93)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.access$executeRuleOnNodeRecursively(RuleExecutionContext.kt:30)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:132)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.SuppressHandler.handle(SuppressHandler.kt:28)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:93)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.access$executeRuleOnNodeRecursively(RuleExecutionContext.kt:30)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:132)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext$executeRuleOnNodeRecursively$2$1.invoke(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.SuppressHandler.handle(SuppressHandler.kt:28)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:131)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRuleOnNodeRecursively(RuleExecutionContext.kt:93)
    at com.pinterest.ktlint.rule.engine.internal.RuleExecutionContext.executeRule(RuleExecutionContext.kt:62)
    ... 13 more
 ()

Summary error count (descending) by rule:
  An internal error occurred in the Ktlint Rule Engine: 1

Steps to Reproduce

  1. Create this file.
    class ClassA
    /**
    * some comment
    */(paramA: String)
  2. Execute format command.
    $ ktlint --format ClassA.kt

Additional information

If there is a space between the slash '/' at the end of the multi-line comment and the parentheses '(' at the beginning of the property definition, no internal error will occur and the format will succeed. The execution result will be as follows.

The file to format( a space between '/' and '(' ):

class ClassA
/**
 * some comment
 */ (paramA: String)

Result:

class ClassA
/**
 * some comment
 */
(paramA: String)

Your Environment

paul-dingemans commented 5 months ago

What is your reasoning to put the KDoc between the class name and the parameter list? Usually it is on top of the class.

Of course it is a bug that Ktlint throws an exception, but I do not think that Ktlint should facilitate formatting code above to something like:

class ClassA
    /**
     * some comment
     */
    (paramA: String)
moritasoshi commented 5 months ago

I separated the class description from the property description to improve clarity. However, as you point out, this doesn't seem to be the usual way of writing. I agree with your view that there's no necessity to accommodate this scenario.