Formatter: support trailing comma in lambda parameters

#KT-34744
This commit is contained in:
Dmitry Gridin
2019-12-27 16:19:11 +07:00
parent 07bda5a759
commit 4adfaab3ec
12 changed files with 711 additions and 37 deletions
@@ -681,5 +681,6 @@ fun getTrailingCommaByClosingElement(closingElement: PsiElement?): PsiElement? {
} }
fun getTrailingCommaByElementsList(elementList: PsiElement?): PsiElement? { fun getTrailingCommaByElementsList(elementList: PsiElement?): PsiElement? {
return elementList?.lastChild?.run { if (node.elementType == KtTokens.COMMA) this else null } val lastChild = elementList?.lastChild?.let { if (it !is PsiComment) it else it.getPrevSiblingIgnoringWhitespaceAndComments() }
return lastChild?.run { if (node.elementType == KtTokens.COMMA) this else null }
} }
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.idea.formatter
import com.intellij.formatting.* import com.intellij.formatting.*
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.TokenType import com.intellij.psi.TokenType
@@ -20,13 +19,16 @@ import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.Companion.strategy import org.jetbrains.kotlin.idea.formatter.NodeIndentStrategy.Companion.strategy
import org.jetbrains.kotlin.idea.util.containsLineBreakInThis
import org.jetbrains.kotlin.idea.util.isMultiline import org.jetbrains.kotlin.idea.util.isMultiline
import org.jetbrains.kotlin.idea.util.needTrailingComma
import org.jetbrains.kotlin.idea.util.requireNode import org.jetbrains.kotlin.idea.util.requireNode
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes
import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS) private val QUALIFIED_OPERATION = TokenSet.create(DOT, SAFE_ACCESS)
@@ -600,15 +602,31 @@ abstract class KotlinCommonBlock(
} }
elementType === VALUE_PARAMETER_LIST -> { elementType === VALUE_PARAMETER_LIST -> {
if (parentElementType === FUN || when (parentElementType) {
parentElementType === PRIMARY_CONSTRUCTOR || FUN, PRIMARY_CONSTRUCTOR, SECONDARY_CONSTRUCTOR -> return getWrappingStrategyForItemList(
parentElementType === SECONDARY_CONSTRUCTOR commonSettings.METHOD_PARAMETERS_WRAP,
) return getWrappingStrategyForItemList( VALUE_PARAMETER,
commonSettings.METHOD_PARAMETERS_WRAP, node.withTrailingComma,
VALUE_PARAMETER, additionalWrap = trailingCommaWrappingStrategyWithMultiLineCheck(LPAR, RPAR)
node.withTrailingComma, )
additionalWrap = trailingCommaWrappingStrategyWithMultiLineCheck(LPAR, RPAR) FUNCTION_LITERAL -> {
) if (nodePsi.parent?.safeAs<KtFunctionLiteral>()?.needTrailingComma(settings) == true) {
val check = thisOrPrevIsMultiLineElement(COMMA, LBRACE /* not necessary */, ARROW /* not necessary */)
return { childElement ->
createWrapAlwaysIf(getPrevWithoutWhitespaceAndComments(childElement) == null || check(childElement))
}
}
}
}
}
elementType === FUNCTION_LITERAL -> {
val withTrailingComma = nodePsi.cast<KtFunctionLiteral>().needTrailingComma(settings)
return { childElement ->
createWrapAlwaysIf(
withTrailingComma && (childElement.elementType === ARROW || getPrevWithoutWhitespaceAndComments(childElement)?.elementType === LBRACE)
)
}
} }
elementType === INDICES -> return defaultTrailingCommaWrappingStrategy(LBRACKET, RBRACKET) elementType === INDICES -> return defaultTrailingCommaWrappingStrategy(LBRACKET, RBRACKET)
@@ -676,8 +694,7 @@ abstract class KotlinCommonBlock(
nodePsi is KtProperty -> nodePsi is KtProperty ->
return wrap@{ childElement -> return wrap@{ childElement ->
val wrapSetting = val wrapSetting = if (nodePsi.isLocal) commonSettings.VARIABLE_ANNOTATION_WRAP else commonSettings.FIELD_ANNOTATION_WRAP
if (nodePsi.isLocal) commonSettings.VARIABLE_ANNOTATION_WRAP else commonSettings.FIELD_ANNOTATION_WRAP
getWrapAfterAnnotation(childElement, wrapSetting)?.let { getWrapAfterAnnotation(childElement, wrapSetting)?.let {
return@wrap it return@wrap it
} }
@@ -763,9 +780,7 @@ abstract class KotlinCommonBlock(
val startOffset = childElement.notDelimiterSiblingNodeInSequence(false, delimiterType, leftBarrier)?.startOffset ?: psi.startOffset val startOffset = childElement.notDelimiterSiblingNodeInSequence(false, delimiterType, leftBarrier)?.startOffset ?: psi.startOffset
val endOffset = childElement.notDelimiterSiblingNodeInSequence(true, delimiterType, rightBarrier)?.psi?.endOffset ?: psi.endOffset val endOffset = childElement.notDelimiterSiblingNodeInSequence(true, delimiterType, rightBarrier)?.psi?.endOffset ?: psi.endOffset
val treeParent = childElement.treeParent return psi.parent.containsLineBreakInThis(startOffset, endOffset)
val textRange = TextRange.create(startOffset, endOffset).shiftLeft(treeParent.startOffset)
return StringUtil.containsLineBreak(textRange.subSequence(treeParent.text))
} }
private fun trailingCommaWrappingStrategyWithMultiLineCheck( private fun trailingCommaWrappingStrategyWithMultiLineCheck(
@@ -779,14 +794,12 @@ abstract class KotlinCommonBlock(
additionalCheck: (ASTNode) -> Boolean additionalCheck: (ASTNode) -> Boolean
): WrappingStrategy = { childElement -> ): WrappingStrategy = { childElement ->
val childElementType = childElement.elementType val childElementType = childElement.elementType
if (childElement.treeParent.withTrailingComma && (childElementType === rightAnchor || createWrapAlwaysIf(
childElement.treeParent.withTrailingComma && (childElementType === rightAnchor ||
getPrevWithoutWhitespaceAndComments(childElement)?.elementType === leftAnchor || getPrevWithoutWhitespaceAndComments(childElement)?.elementType === leftAnchor ||
additionalCheck(childElement) additionalCheck(childElement)
) )
) )
Wrap.createWrap(WrapType.ALWAYS, true)
else
null
} }
} }
@@ -1139,3 +1152,5 @@ private fun extractIndent(node: ASTNode): String {
return "" return ""
return prevNode.text.substringAfterLast("\n", prevNode.text) return prevNode.text.substringAfterLast("\n", prevNode.text)
} }
private fun createWrapAlwaysIf(option: Boolean): Wrap? = if (option) Wrap.createWrap(WrapType.ALWAYS, true) else null
@@ -10,7 +10,12 @@ import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettings
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.formatter.kotlinCustomSettings
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
/* /*
* ASTBlock.node is nullable, this extension was introduced to minimize changes * ASTBlock.node is nullable, this extension was introduced to minimize changes
@@ -39,3 +44,16 @@ fun PsiElement.getLineCount(): Int {
} }
fun PsiElement.isMultiline() = getLineCount() > 1 fun PsiElement.isMultiline() = getLineCount() > 1
fun KtFunctionLiteral.needTrailingComma(settings: CodeStyleSettings): Boolean = valueParameterList?.trailingComma != null ||
settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA &&
run(fun(): Boolean {
val startOffset = valueParameterList?.startOffset ?: return false
val endOffset = arrow?.endOffset ?: return false
return containsLineBreakInThis(startOffset, endOffset)
})
fun PsiElement.containsLineBreakInThis(globalStartOffset: Int, globalEndOffset: Int): Boolean {
val textRange = TextRange.create(globalStartOffset, globalEndOffset).shiftLeft(startOffset)
return StringUtil.containsLineBreak(textRange.subSequence(text))
}
@@ -15,6 +15,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettings
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor import com.intellij.psi.impl.source.codeStyle.PostFormatProcessor
import com.intellij.psi.impl.source.codeStyle.PostFormatProcessorHelper import com.intellij.psi.impl.source.codeStyle.PostFormatProcessorHelper
import org.jetbrains.kotlin.idea.util.isMultiline import org.jetbrains.kotlin.idea.util.isMultiline
import org.jetbrains.kotlin.idea.util.needTrailingComma
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
@@ -66,13 +67,13 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
} }
private fun processCommaOwner(parent: KtElement) { private fun processCommaOwner(parent: KtElement) {
val previous = parent.lastChild?.getPrevSiblingIgnoringWhitespaceAndComments() ?: return val lastElement = parent.lastCommaOwnerOrComma ?: return
val elementType = previous.safeAs<ASTNode>()?.elementType val elementType = lastElement.safeAs<ASTNode>()?.elementType
if (elementType === KtTokens.COMMA || settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA && parent.isMultiline()) { if (elementType === KtTokens.COMMA || parent.needComma) {
// add a missing comma // add a missing comma
if (elementType !== KtTokens.COMMA) { if (elementType !== KtTokens.COMMA) {
changePsi(parent) { element, factory -> changePsi(parent) { element, factory ->
element.addAfter(factory.createComma(), previous) element.addAfter(factory.createComma(), lastElement)
} }
} }
@@ -80,6 +81,12 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
} }
} }
private val KtElement.needComma: Boolean
get() = when (val parent = parent) {
is KtFunctionLiteral -> parent.needTrailingComma(settings)
else -> settings.kotlinCustomSettings.ALLOW_TRAILING_COMMA && isMultiline()
}
private fun changePsi(element: KtElement, update: (KtElement, KtPsiFactory) -> Unit) { private fun changePsi(element: KtElement, update: (KtElement, KtPsiFactory) -> Unit) {
val oldLength = element.textLength val oldLength = element.textLength
update(element, KtPsiFactory(element)) update(element, KtPsiFactory(element))
@@ -88,9 +95,7 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
} }
private fun correctCommaPosition(parent: KtElement) { private fun correctCommaPosition(parent: KtElement) {
val lPar = parent.children.firstOrNull() ?: return val invalidElements = parent.firstChild?.siblings(withItself = false)?.mapNotNull {
val rPar = parent.children.lastOrNull() ?: return
val invalidElements = lPar.siblings(withItself = false).takeWhile { it != rPar }.mapNotNull {
if (it !is ASTNode || it.elementType != KtTokens.COMMA) return@mapNotNull null if (it !is ASTNode || it.elementType != KtTokens.COMMA) return@mapNotNull null
val prevWithComment = it.getPrevSiblingIgnoringWhitespace(false) val prevWithComment = it.getPrevSiblingIgnoringWhitespace(false)
@@ -99,7 +104,7 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
it.createSmartPointer() to prevWithoutComment.createSmartPointer() it.createSmartPointer() to prevWithoutComment.createSmartPointer()
} else } else
null null
}.toList() }?.toList() ?: return
if (invalidElements.isNotEmpty()) { if (invalidElements.isNotEmpty()) {
changePsi(parent) { element, factory -> changePsi(parent) { element, factory ->
@@ -129,4 +134,16 @@ private class TrailingCommaVisitor(val settings: CodeStyleSettings) : KtTreeVisi
companion object { companion object {
private val LOG = Logger.getInstance(TrailingCommaVisitor::class.java) private val LOG = Logger.getInstance(TrailingCommaVisitor::class.java)
} }
} }
private val PsiElement.lastCommaOwnerOrComma: PsiElement?
get() {
val lastChild = lastChild ?: return null
val withSelf = when (lastChild.safeAs<ASTNode>()?.elementType) {
KtTokens.COMMA -> return lastChild
KtTokens.RBRACKET, KtTokens.RPAR, KtTokens.RBRACE, KtTokens.GT -> false
else -> true
}
return lastChild.getPrevSiblingIgnoringWhitespaceAndComments(withSelf)
}
@@ -97,8 +97,8 @@ fun a() = Unit
@Anno([ @Anno([
1, 1,
2/* 2,/*
*/,/* *//*
*/ */
]) ])
fun a() = Unit fun a() = Unit
@@ -148,8 +148,8 @@ fun a() = Unit
@Anno( @Anno(
[ [
1, 1,
2/* 2,/*
*/,/* *//*
*/ */
], ],
) )
@@ -0,0 +1,190 @@
val x = {
x: Comparable<Comparable<Number>>,
y: String,
->
println("1")
}
val x = { x: Comparable<Comparable<Number>>, y: String ->
println("1")
}
val x = { x: String, y
: String ->
println("1")
}
val x = { x: String,
y: String
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>,
y: String,
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
z: String,
->
println("1")
}
val x = { x: String,
y: String,
z: String
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = { x: String
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
y: String,
->
println("1")
}
val x = { x: String,
z: String
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
z: String,
->
println("1")
}
val x = {
x, y: String,
z: String,
->
println("1")
}
val x = { x: String, y: String, z: String
->
println("1")
}
val x = {
z: String, v: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
->
println("1")
}
val x = { x: Int
->
println("1")
}
val x = { x: String, y: String, /* */ z: String
->
println("1")
}
val x = { x: String, y: String
/* */, /* */ z: String
->
println("1")
}()
val x = {
x: String, /*
*/
y: String,
z: String,
->
println("1")
}
val x = {
x: String, y: String,
z: String, /*
*/
->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>, y: String,
z: String,
->
println("1")
}
val x = { x: String, y: String, z: String
->
println("1")
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,205 @@
val x = {
x: Comparable<Comparable<Number>>,
y: String,
->
println("1")
}
val x = { x: Comparable<Comparable<Number>>, y: String ->
println("1")
}
val x = {
x: String,
y
: String,
->
println("1")
}
val x = {
x: String,
y: String,
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>,
y: String,
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String,
y: String,
->
println("1")
}
val x = {
x: String,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String,
->
println("1")
}
val x = {
x: String,
z: String,
->
println("1")
}
val x = {
x, y: String,
z: String,
->
println("1")
}
val x = {
x: String, y: String, z: String,
->
println("1")
}
val x = {
z: String, v: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: Int,
->
println("1")
}
val x = {
x: String, y: String, /* */
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
/* */ /* */
z: String,
->
println("1")
}()
val x = {
x: String, /*
*/
y: String,
z: String,
->
println("1")
}
val x = {
x: String, y: String,
z: String, /*
*/
->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>, y: String,
z: String,
->
println("1")
}
val x = {
x: String, y: String, z: String,
->
println("1")
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -0,0 +1,192 @@
val x = {
x: Comparable<Comparable<Number>>,
y: String,
->
println("1")
}
val x = {x: Comparable<Comparable<Number>>, y: String->
println("1")
}
val x = {x: String, y
: String->
println("1")
}
val x = {
x: String,
y: String
->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>,
y: String
,->
println("1")
}
val x = {
x: String,
y: Comparable<Comparable<Number>>,
z: String,
->
println("1")
}
val x = {
x: String,
y: String,
z: String
->
println("1")
}
val x = {
x: String,
y: String,
z: String,->
println("1")
}
val x = {
x: String,
y: String,
z: String
,->
println("1")
}
val x = {
x: String,
->
println("1")
}
val x = {
x: String
->
println("1")
}
val x = {
x: String,->
println("1")
}
val x = {
x: String
,->
println("1")
}
val x = {
x: String ,
y: String,
->
println("1")
}
val x = {
x: String,
z: String
->
println("1")
}
val x = {
x: String,
y: String,
z: String ,->
println("1")
}
val x = {
x: String,
z: String
, ->
println("1")
}
val x = {
x, y: String,
z: String
, ->
println("1")
}
val x = {
x: String, y: String, z: String
->
println("1")
}
val x = {
z: String, v: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {x: String,
y: Comparable<Comparable<Number>>,
->
println("1")
}
val x = {
x: Int
->
println("1")
}
val x = {
x: String, y: String
, /* */ z: String
->
println("1")
}
val x = {
x: String, y: String
/* */, /* */ z: String
->
println("1")
}()
val x = {
x: String, /*
*/ y: String,
z: String ,->
println("1")
}
val x = {
x: String, y: String , z: String /*
*/
, ->
println("1")
}
val x = {
x: Comparable<Comparable<Number>>, y: String,
z: String
, ->
println("1")
}
val x = {
x: String, y: String, z: String
->
println("1")
}
// SET_TRUE: ALLOW_TRAILING_COMMA
@@ -226,8 +226,8 @@ class H<
class J< class J<
x : String, y : String, x : String, y : String,
z : String /* z : String, /*
*/, */
> >
class K< class K<
@@ -234,8 +234,8 @@ class H<
class J< class J<
x : String, y : String, x : String, y : String,
z : String /* z : String, /*
*/, */
> >
class K< class K<
@@ -1200,6 +1200,24 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
} }
} }
@TestMetadata("idea/testData/formatter/trailingComma/lambdaParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LambdaParameters extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInLambdaParameters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/lambdaParameters"), Pattern.compile("^([^\\.]+)\\.after\\.kt.*$"), null, true);
}
@TestMetadata("LambdaParameterList.after.kt")
public void testLambdaParameterList() throws Exception {
runTest("idea/testData/formatter/trailingComma/lambdaParameters/LambdaParameterList.after.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma/typeArguments") @TestMetadata("idea/testData/formatter/trailingComma/typeArguments")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -1716,6 +1734,24 @@ public class FormatterTestGenerated extends AbstractFormatterTest {
} }
} }
@TestMetadata("idea/testData/formatter/trailingComma/lambdaParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class LambdaParameters extends AbstractFormatterTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestInverted, this, testDataFilePath);
}
public void testAllFilesPresentInLambdaParameters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/formatter/trailingComma/lambdaParameters"), Pattern.compile("^([^\\.]+)\\.after\\.inv\\.kt.*$"), null, true);
}
@TestMetadata("LambdaParameterList.after.inv.kt")
public void testLambdaParameterList() throws Exception {
runTest("idea/testData/formatter/trailingComma/lambdaParameters/LambdaParameterList.after.inv.kt");
}
}
@TestMetadata("idea/testData/formatter/trailingComma/typeArguments") @TestMetadata("idea/testData/formatter/trailingComma/typeArguments")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)