[Analysis API FIR] do not consider unresolved imports as unused in import optimizer
Otherwise, the import optimizer breaks incomplete code ^KTIJ-25034 fixed
This commit is contained in:
committed by
Space Team
parent
d9ca77f716
commit
15f19f324e
+55
-5
@@ -21,7 +21,12 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
|||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.realPsi
|
import org.jetbrains.kotlin.fir.realPsi
|
||||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedError
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedReferenceError
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedSymbolError
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedTypeQualifierError
|
||||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||||
@@ -62,7 +67,9 @@ internal class KtFirImportOptimizer(
|
|||||||
.map { it.fqName }
|
.map { it.fqName }
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
val referencesEntities = collectReferencedEntities(firFile)
|
val (usedImports, unresolvedNames) = collectReferencedEntities(firFile)
|
||||||
|
|
||||||
|
val referencesEntities = usedImports
|
||||||
.filterNot { (fqName, referencedByNames) ->
|
.filterNot { (fqName, referencedByNames) ->
|
||||||
// when referenced by more than one name, we need to keep the imports with same package
|
// when referenced by more than one name, we need to keep the imports with same package
|
||||||
fqName.parentOrNull() == file.packageFqName && referencedByNames.size == 1
|
fqName.parentOrNull() == file.packageFqName && referencedByNames.size == 1
|
||||||
@@ -82,8 +89,9 @@ internal class KtFirImportOptimizer(
|
|||||||
val importPath = import.importPath ?: continue
|
val importPath = import.importPath ?: continue
|
||||||
|
|
||||||
val isUsed = when {
|
val isUsed = when {
|
||||||
|
importPath.importedName in unresolvedNames -> true
|
||||||
!alreadySeenImports.add(importPath) -> false
|
!alreadySeenImports.add(importPath) -> false
|
||||||
importPath.isAllUnder -> importPath.fqName in requiredStarImports
|
importPath.isAllUnder -> unresolvedNames.isNotEmpty() || importPath.fqName in requiredStarImports
|
||||||
importPath.fqName in referencesEntities -> importPath.importedName in referencesEntities.getValue(importPath.fqName)
|
importPath.fqName in referencesEntities -> importPath.importedName in referencesEntities.getValue(importPath.fqName)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@@ -96,8 +104,14 @@ internal class KtFirImportOptimizer(
|
|||||||
return KtImportOptimizerResult(unusedImports)
|
return KtImportOptimizerResult(unusedImports)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectReferencedEntities(firFile: FirFile): Map<FqName, Set<Name>> {
|
private data class ReferencedEntitiesResult(
|
||||||
|
val usedImports: Map<FqName, Set<Name>>,
|
||||||
|
val unresolvedNames: Set<Name>,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun collectReferencedEntities(firFile: FirFile): ReferencedEntitiesResult {
|
||||||
val usedImports = mutableMapOf<FqName, MutableSet<Name>>()
|
val usedImports = mutableMapOf<FqName, MutableSet<Name>>()
|
||||||
|
val unresolvedNames = mutableSetOf<Name>()
|
||||||
|
|
||||||
firFile.accept(object : FirVisitorVoid() {
|
firFile.accept(object : FirVisitorVoid() {
|
||||||
override fun visitElement(element: FirElement) {
|
override fun visitElement(element: FirElement) {
|
||||||
@@ -128,6 +142,7 @@ internal class KtFirImportOptimizer(
|
|||||||
|
|
||||||
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {
|
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {
|
||||||
processTypeRef(errorTypeRef)
|
processTypeRef(errorTypeRef)
|
||||||
|
processErrorTypeRef(errorTypeRef)
|
||||||
|
|
||||||
errorTypeRef.delegatedTypeRef?.accept(this)
|
errorTypeRef.delegatedTypeRef?.accept(this)
|
||||||
super.visitErrorTypeRef(errorTypeRef)
|
super.visitErrorTypeRef(errorTypeRef)
|
||||||
@@ -148,8 +163,21 @@ internal class KtFirImportOptimizer(
|
|||||||
super.visitErrorResolvedQualifier(errorResolvedQualifier)
|
super.visitErrorResolvedQualifier(errorResolvedQualifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processErrorNameReference(resolvable: FirResolvable) {
|
||||||
|
val nameReference = resolvable.calleeReference as? FirErrorNamedReference ?: return
|
||||||
|
val name = nameReference.unresolvedName ?: return
|
||||||
|
unresolvedNames += name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processErrorTypeRef(typeRef: FirErrorTypeRef) {
|
||||||
|
val diagnostic = typeRef.diagnostic as? ConeUnresolvedError ?: return
|
||||||
|
val name = diagnostic.unresolvedName ?: return
|
||||||
|
unresolvedNames += name
|
||||||
|
}
|
||||||
|
|
||||||
private fun processFunctionCall(functionCall: FirFunctionCall) {
|
private fun processFunctionCall(functionCall: FirFunctionCall) {
|
||||||
if (functionCall.isFullyQualified) return
|
if (functionCall.isFullyQualified) return
|
||||||
|
processErrorNameReference(functionCall)
|
||||||
|
|
||||||
val referencesByName = functionCall.functionReferenceName ?: return
|
val referencesByName = functionCall.functionReferenceName ?: return
|
||||||
val functionSymbol = functionCall.referencedCallableSymbol ?: return
|
val functionSymbol = functionCall.referencedCallableSymbol ?: return
|
||||||
@@ -158,6 +186,8 @@ internal class KtFirImportOptimizer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processImplicitFunctionCall(implicitInvokeCall: FirImplicitInvokeCall) {
|
private fun processImplicitFunctionCall(implicitInvokeCall: FirImplicitInvokeCall) {
|
||||||
|
processErrorNameReference(implicitInvokeCall)
|
||||||
|
|
||||||
val functionSymbol = implicitInvokeCall.referencedCallableSymbol ?: return
|
val functionSymbol = implicitInvokeCall.referencedCallableSymbol ?: return
|
||||||
|
|
||||||
saveCallable(functionSymbol, OperatorNameConventions.INVOKE)
|
saveCallable(functionSymbol, OperatorNameConventions.INVOKE)
|
||||||
@@ -165,6 +195,7 @@ internal class KtFirImportOptimizer(
|
|||||||
|
|
||||||
private fun processPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) {
|
private fun processPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) {
|
||||||
if (propertyAccessExpression.isFullyQualified) return
|
if (propertyAccessExpression.isFullyQualified) return
|
||||||
|
processErrorNameReference(propertyAccessExpression)
|
||||||
|
|
||||||
val referencedByName = propertyAccessExpression.propertyReferenceName ?: return
|
val referencedByName = propertyAccessExpression.propertyReferenceName ?: return
|
||||||
val propertySymbol = propertyAccessExpression.referencedCallableSymbol ?: return
|
val propertySymbol = propertyAccessExpression.referencedCallableSymbol ?: return
|
||||||
@@ -180,6 +211,7 @@ internal class KtFirImportOptimizer(
|
|||||||
|
|
||||||
private fun processCallableReferenceAccess(callableReferenceAccess: FirCallableReferenceAccess) {
|
private fun processCallableReferenceAccess(callableReferenceAccess: FirCallableReferenceAccess) {
|
||||||
if (callableReferenceAccess.isFullyQualified) return
|
if (callableReferenceAccess.isFullyQualified) return
|
||||||
|
processErrorNameReference(callableReferenceAccess)
|
||||||
|
|
||||||
val referencedByName = callableReferenceAccess.callableReferenceName ?: return
|
val referencedByName = callableReferenceAccess.callableReferenceName ?: return
|
||||||
val resolvedSymbol = callableReferenceAccess.referencedCallableSymbol ?: return
|
val resolvedSymbol = callableReferenceAccess.referencedCallableSymbol ?: return
|
||||||
@@ -218,10 +250,28 @@ internal class KtFirImportOptimizer(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return usedImports
|
return ReferencedEntitiesResult(usedImports, unresolvedNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val FirErrorNamedReference.unresolvedName: Name?
|
||||||
|
get() {
|
||||||
|
val diagnostic = diagnostic as? ConeUnresolvedError ?: return null
|
||||||
|
return diagnostic.unresolvedName
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ConeUnresolvedError.unresolvedName: Name?
|
||||||
|
get() = when (this) {
|
||||||
|
is ConeUnresolvedNameError -> name
|
||||||
|
is ConeUnresolvedReferenceError -> name
|
||||||
|
is ConeUnresolvedSymbolError -> classId.shortClassName
|
||||||
|
is ConeUnresolvedTypeQualifierError -> {
|
||||||
|
// we take the first qualifier part because only the first one can be imported
|
||||||
|
qualifiers.firstOrNull()?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An actual name by which this callable reference were used.
|
* An actual name by which this callable reference were used.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+54
@@ -133,12 +133,66 @@ public class FirIdeNormalAnalysisSourceModuleAnalysisApiImportOptimizerTestGener
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedFunction.kt")
|
||||||
|
public void testUnresolvedFunction() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedFunctionStarImport.kt")
|
||||||
|
public void testUnresolvedFunctionStarImport() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedFunctionStarImport.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedProperty.kt")
|
||||||
|
public void testUnresolvedProperty() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedProperty.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedType.kt")
|
||||||
|
public void testUnresolvedType() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeArgument.kt")
|
||||||
|
public void testUnresolvedTypeArgument() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeQualifier.kt")
|
||||||
|
public void testUnresolvedTypeQualifier() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeQualifier.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeQualifierConstructor.kt")
|
||||||
|
public void testUnresolvedTypeQualifierConstructor() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeQualifierConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedViaImportAlias.kt")
|
||||||
|
public void testUnresolvedViaImportAlias() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedViaImportAlias.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unusedTypeHiddenByTypeParameter_invalidAsArgument.kt")
|
@TestMetadata("unusedTypeHiddenByTypeParameter_invalidAsArgument.kt")
|
||||||
public void testUnusedTypeHiddenByTypeParameter_invalidAsArgument() throws Exception {
|
public void testUnusedTypeHiddenByTypeParameter_invalidAsArgument() throws Exception {
|
||||||
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedTypeHiddenByTypeParameter_invalidAsArgument.kt");
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedTypeHiddenByTypeParameter_invalidAsArgument.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unusedUnresolvedImport.kt")
|
||||||
|
public void testUnusedUnresolvedImport() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedUnresolvedImport.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("usedConstructor_invalidArguments.kt")
|
@TestMetadata("usedConstructor_invalidArguments.kt")
|
||||||
public void testUsedConstructor_invalidArguments() throws Exception {
|
public void testUsedConstructor_invalidArguments() throws Exception {
|
||||||
|
|||||||
+54
@@ -133,12 +133,66 @@ public class FirStandaloneNormalAnalysisSourceModuleAnalysisApiImportOptimizerTe
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedFunction.kt")
|
||||||
|
public void testUnresolvedFunction() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedFunctionStarImport.kt")
|
||||||
|
public void testUnresolvedFunctionStarImport() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedFunctionStarImport.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedProperty.kt")
|
||||||
|
public void testUnresolvedProperty() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedProperty.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedType.kt")
|
||||||
|
public void testUnresolvedType() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeArgument.kt")
|
||||||
|
public void testUnresolvedTypeArgument() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeQualifier.kt")
|
||||||
|
public void testUnresolvedTypeQualifier() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeQualifier.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedTypeQualifierConstructor.kt")
|
||||||
|
public void testUnresolvedTypeQualifierConstructor() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedTypeQualifierConstructor.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unresolvedViaImportAlias.kt")
|
||||||
|
public void testUnresolvedViaImportAlias() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unresolvedViaImportAlias.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("unusedTypeHiddenByTypeParameter_invalidAsArgument.kt")
|
@TestMetadata("unusedTypeHiddenByTypeParameter_invalidAsArgument.kt")
|
||||||
public void testUnusedTypeHiddenByTypeParameter_invalidAsArgument() throws Exception {
|
public void testUnusedTypeHiddenByTypeParameter_invalidAsArgument() throws Exception {
|
||||||
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedTypeHiddenByTypeParameter_invalidAsArgument.kt");
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedTypeHiddenByTypeParameter_invalidAsArgument.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unusedUnresolvedImport.kt")
|
||||||
|
public void testUnusedUnresolvedImport() throws Exception {
|
||||||
|
runTest("analysis/analysis-api/testData/components/importOptimizer/analyseImports/referencesWithErrors/unusedUnresolvedImport.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("usedConstructor_invalidArguments.kt")
|
@TestMetadata("usedConstructor_invalidArguments.kt")
|
||||||
public void testUsedConstructor_invalidArguments() throws Exception {
|
public void testUsedConstructor_invalidArguments() throws Exception {
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.readText
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
readText()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.*
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
readText()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.prop
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
prop
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.X
|
||||||
|
|
||||||
|
fun t(): X {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.X
|
||||||
|
|
||||||
|
fun t(): List<X> {
|
||||||
|
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.X
|
||||||
|
import a.b.c.C
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
X.C
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.X
|
||||||
|
import a.b.c.C
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
X.C()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package m2.second
|
||||||
|
|
||||||
|
import a.b.c.prop as prp
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
prp
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
a.a.Foo
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package one
|
||||||
|
|
||||||
|
import a.c.c.D
|
||||||
|
import a.a.Foo
|
||||||
|
import a.star.*
|
||||||
|
|
||||||
|
fun t() {
|
||||||
|
D()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user