diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.kt index 6e6c63d562d..3b284d77a93 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.kt @@ -34,12 +34,12 @@ open class DelegatingBindingTrace( allowSliceRewrite: Boolean = false ) : BindingTrace { - private val map = if (BindingTraceContext.TRACK_REWRITES && !allowSliceRewrite) + protected val map = if (BindingTraceContext.TRACK_REWRITES && !allowSliceRewrite) TrackingSlicedMap(BindingTraceContext.TRACK_WITH_STACK_TRACES) else SlicedMapImpl(allowSliceRewrite) - private val mutableDiagnostics: MutableDiagnosticsWithSuppression? + protected val mutableDiagnostics: MutableDiagnosticsWithSuppression? private inner class MyBindingContext : BindingContext { override fun getDiagnostics(): Diagnostics = mutableDiagnostics ?: Diagnostics.EMPTY @@ -99,16 +99,15 @@ open class DelegatingBindingTrace( record(slice, key, true) } - override fun get(slice: ReadOnlySlice, key: K): V? { - val value = map.get(slice, key) - if (slice is SetSlice<*>) { - assert(value != null) - if (value != SetSlice.DEFAULT) return value - } else if (value != null) { - return value - } + override fun get(slice: ReadOnlySlice, key: K): V? = + selfGet(slice, key) ?: parentContext.get(slice, key) - return parentContext.get(slice, key) + protected fun selfGet(slice: ReadOnlySlice, key: K): V? { + val value = map.get(slice, key) + return if (slice is SetSlice<*>) { + assert(value != null) + if (value != SetSlice.DEFAULT) value else null + } else value } override fun getKeys(slice: WritableSlice): Collection { @@ -145,7 +144,7 @@ open class DelegatingBindingTrace( BindingContextUtils.addOwnDataTo(trace, filter, commitDiagnostics, map, mutableDiagnostics) } - fun clear() { + open fun clear() { map.clear() mutableDiagnostics?.clear() } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt index 96b64ebb200..28875926ccc 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt @@ -246,24 +246,15 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService { val specialModuleInfo = files.map(KtFile::getModuleInfo).toSet().single() val settings = specialModuleInfo.platformSettings(specialModuleInfo.platform ?: targetPlatform) - // File copies are created during completion and receive correct modification events through POM. // Dummy files created e.g. by J2K do not receive events. - val filesModificationTracker = if (files.all { it.originalFile != it }) { - ModificationTracker { - files.sumByLong { it.outOfBlockModificationCount } - } + val dependenciesForSyntheticFileCache = if (files.all { it.originalFile != it }) { + emptyList() } else { - ModificationTracker { - files.sumByLong { it.outOfBlockModificationCount + it.modificationStamp } - } + listOf(ModificationTracker { + files.sumByLong { it.modificationStamp } + }) } - val dependenciesForSyntheticFileCache = - listOf( - KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker, - filesModificationTracker - ) - val resolverDebugName = "$resolverForSpecialInfoName $specialModuleInfo for files ${files.joinToString { it.name }} for platform $targetPlatform" diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt index dbeacd938e9..b689b70b145 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/PerFileAnalysisCache.kt @@ -16,12 +16,15 @@ package org.jetbrains.kotlin.idea.caches.resolve +import com.google.common.collect.ImmutableMap import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.progress.ProgressIndicatorProvider import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.IndexNotReadyException import com.intellij.openapi.project.Project +import com.intellij.openapi.util.ModificationTracker import com.intellij.psi.PsiElement -import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.* import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.get @@ -29,18 +32,25 @@ import org.jetbrains.kotlin.context.GlobalContext import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve import org.jetbrains.kotlin.idea.caches.project.getModuleInfo +import org.jetbrains.kotlin.idea.caches.trackers.clearInBlockModifications +import org.jetbrains.kotlin.idea.caches.trackers.inBlockModifications import org.jetbrains.kotlin.idea.project.IdeaModuleStructureOracle -import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.findAnalyzerServices import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.resolve.diagnostics.DiagnosticsElementsCache import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice +import org.jetbrains.kotlin.util.slicedMap.WritableSlice import java.util.* internal class PerFileAnalysisCache(val file: KtFile, componentProvider: ComponentProvider) { @@ -51,6 +61,89 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone private val bodyResolveCache = componentProvider.get() private val cache = HashMap() + private var fileResult: AnalysisResult? = null + + fun getAnalysisResults(element: KtElement): AnalysisResult { + assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" } + + val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element) + + return synchronized(this) { + ProgressIndicatorProvider.checkCanceled() + + // step 1: perform incremental analysis IF it is applicable + getIncrementalAnalysisResult()?.let { return it } + + // cache does not contain AnalysisResult per each kt/psi element + // instead it looks up analysis for its parents - see lookUp(analyzableElement) + + // step 2: return result if it is cached + lookUp(analyzableParent)?.let { + return@synchronized it + } + + // step 3: perform analyze of analyzableParent as nothing has been cached yet + val result = analyze(analyzableParent) + cache[analyzableParent] = result + + return@synchronized result + } + } + + private fun getIncrementalAnalysisResult(): AnalysisResult? { + // move fileResult from cache if it is stored there + if (fileResult == null && cache.containsKey(file)) { + fileResult = cache[file] + + // drop existed results for entire cache: + // if incremental analysis is applicable it will produce a single value for file + // otherwise those results are potentially stale + cache.clear() + } + + val inBlockModifications = file.inBlockModifications + if (inBlockModifications.isNotEmpty()) { + try { + // IF there is a cached result for ktFile and there are inBlockModifications + fileResult = fileResult?.let { result -> + var analysisResult = result + for (inBlockModification in inBlockModifications) { + val resultCtx = analysisResult.bindingContext + + val stackedCtx = + if (resultCtx is StackedCompositeBindingContextTrace.StackedCompositeBindingContext) resultCtx else null + + // no incremental analysis IF it is not applicable + if (stackedCtx?.isIncrementalAnalysisApplicable() == false) return@let null + + val trace: StackedCompositeBindingContextTrace = + if (stackedCtx != null && stackedCtx.element() == inBlockModification) { + val trace = stackedCtx.bindingTrace() + trace.clear() + trace + } else { + // to reflect a depth of stacked binding context + val depth = (stackedCtx?.depth() ?: 0) + 1 + + StackedCompositeBindingContextTrace( + depth, + element = inBlockModification, + resolveContext = resolveSession.bindingContext, + parentContext = resultCtx + ) + } + + val newResult = analyze(inBlockModification, trace) + analysisResult = wrapResult(result, newResult, trace) + } + analysisResult + } + } finally { + file.clearInBlockModifications() + } + } + return fileResult + } private fun lookUp(analyzableElement: KtElement): AnalysisResult? { // Looking for parent elements that are already analyzed @@ -75,25 +168,26 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone return result } - fun getAnalysisResults(element: KtElement): AnalysisResult { - assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" } - - val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element) - - return synchronized(this) { - - val cached = lookUp(analyzableParent) - if (cached != null) return@synchronized cached - - val result = analyze(analyzableParent) - - cache[analyzableParent] = result - - return@synchronized result + private fun wrapResult( + oldResult: AnalysisResult, + newResult: AnalysisResult, + elementBindingTrace: StackedCompositeBindingContextTrace + ): AnalysisResult { + val newBindingCtx = elementBindingTrace.stackedContext + return when { + oldResult.isError() -> AnalysisResult.internalError(newBindingCtx, oldResult.error) + newResult.isError() -> AnalysisResult.internalError(newBindingCtx, newResult.error) + else -> AnalysisResult.success( + newBindingCtx, + oldResult.moduleDescriptor, + oldResult.shouldGenerateCode + ) } } - private fun analyze(analyzableElement: KtElement): AnalysisResult { + private fun analyze(analyzableElement: KtElement, bindingTrace: BindingTrace? = null): AnalysisResult { + ProgressIndicatorProvider.checkCanceled() + val project = analyzableElement.project if (DumbService.isDumb(project)) { return AnalysisResult.EMPTY @@ -107,7 +201,8 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone resolveSession, codeFragmentAnalyzer, bodyResolveCache, - analyzableElement + analyzableElement, + bindingTrace ) } catch (e: ProcessCanceledException) { throw e @@ -122,6 +217,99 @@ internal class PerFileAnalysisCache(val file: KtFile, componentProvider: Compone } } +private class MergedDiagnostics(val diagnostics: Collection, override val modificationTracker: ModificationTracker) : Diagnostics { + @Suppress("UNCHECKED_CAST") + private val elementsCache = DiagnosticsElementsCache(this) { true } + + override fun all() = diagnostics + + override fun forElement(psiElement: PsiElement): MutableCollection = elementsCache.getDiagnostics(psiElement) + + override fun noSuppression() = this +} + +private class StackedCompositeBindingContextTrace( + val depth: Int, // depth of stack over original ktFile bindingContext + val element: KtElement, + val resolveContext: BindingContext, + val parentContext: BindingContext +) : DelegatingBindingTrace( + resolveContext, + "Stacked trace for resolution of $element", + allowSliceRewrite = true +) { + /** + * Effectively StackedCompositeBindingContext holds up-to-date and partially outdated contexts (parentContext) + * + * The most up-to-date results for element are stored here (in a DelegatingBindingTrace#map) + * + * Note: It does not delete outdated results rather hide it therefore there is some extra memory footprint. + * + * Note: stackedContext differs from DelegatingBindingTrace#bindingContext: + * if result is not present in this context it goes to parentContext rather to resolveContext + * diagnostics are aggregated from this context and parentContext + */ + val stackedContext = StackedCompositeBindingContext() + + /** + * All diagnostics from parentContext apart those diagnostics those belongs to the element or its descendants + */ + val parentDiagnosticsApartElement: List = parentContext.diagnostics.all().filter { d -> + d.psiElement.parentsWithSelf.none { it == element } + }.toList() + + inner class StackedCompositeBindingContext : BindingContext { + var cachedDiagnostics: Diagnostics? = null + + fun bindingTrace(): StackedCompositeBindingContextTrace = this@StackedCompositeBindingContextTrace + + fun element(): KtElement = this@StackedCompositeBindingContextTrace.element + + fun depth(): Int = this@StackedCompositeBindingContextTrace.depth + + // to prevent too deep stacked binding context + fun isIncrementalAnalysisApplicable(): Boolean = this@StackedCompositeBindingContextTrace.depth < 16 + + override fun getDiagnostics(): Diagnostics { + if (cachedDiagnostics == null) { + val diagnosticList = + parentDiagnosticsApartElement + (this@StackedCompositeBindingContextTrace.mutableDiagnostics?.all() ?: emptyList()) + cachedDiagnostics = MergedDiagnostics(diagnosticList, parentContext.diagnostics.modificationTracker) + } + return cachedDiagnostics!! + } + + override fun get(slice: ReadOnlySlice, key: K): V? { + return selfGet(slice, key) ?: parentContext.get(slice, key) + } + + override fun getType(expression: KtExpression): KotlinType? { + val typeInfo = get(BindingContext.EXPRESSION_TYPE_INFO, expression) + return typeInfo?.type + } + + override fun getKeys(slice: WritableSlice): Collection { + val keys = map.getKeys(slice) + val fromParent = parentContext.getKeys(slice) + if (keys.isEmpty()) return fromParent + if (fromParent.isEmpty()) return keys + + return keys + fromParent + } + + override fun getSliceContents(slice: ReadOnlySlice): ImmutableMap { + return ImmutableMap.copyOf(parentContext.getSliceContents(slice) + map.getSliceContents(slice)) + } + + override fun addOwnDataTo(trace: BindingTrace, commitDiagnostics: Boolean) = throw UnsupportedOperationException() + } + + override fun clear() { + super.clear() + stackedContext.cachedDiagnostics = null + } +} + private object KotlinResolveDataProvider { private val topmostElementTypes = arrayOf?>( KtNamedFunction::class.java, @@ -159,9 +347,9 @@ private object KotlinResolveDataProvider { if (analyzableElement is KtClassInitializer) return analyzableElement.containingDeclaration return analyzableElement // if none of the above worked, take the outermost declaration - ?: PsiTreeUtil.getTopmostParentOfType(element, KtDeclaration::class.java) - // if even that didn't work, take the whole file - ?: element.containingKtFile + ?: PsiTreeUtil.getTopmostParentOfType(element, KtDeclaration::class.java) + // if even that didn't work, take the whole file + ?: element.containingKtFile } fun analyze( @@ -171,7 +359,8 @@ private object KotlinResolveDataProvider { resolveSession: ResolveSession, codeFragmentAnalyzer: CodeFragmentAnalyzer, bodyResolveCache: BodyResolveCache, - analyzableElement: KtElement + analyzableElement: KtElement, + bindingTrace: BindingTrace? ): AnalysisResult { try { if (analyzableElement is KtCodeFragment) { @@ -180,6 +369,16 @@ private object KotlinResolveDataProvider { return AnalysisResult.success(bindingContext, moduleDescriptor) } + val trace = bindingTrace ?: DelegatingBindingTrace( + resolveSession.bindingContext, + "Trace for resolution of $analyzableElement", + allowSliceRewrite = true + ) + + val moduleInfo = analyzableElement.containingKtFile.getModuleInfo() + + val targetPlatform = moduleInfo.platform + /* Note that currently we *have* to re-create LazyTopDownAnalyzer with custom trace in order to disallow resolution of bodies in top-level trace (trace from DI-container). @@ -190,17 +389,6 @@ private object KotlinResolveDataProvider { (see 'functionAdditionalResolve'). However, this trace is still needed, because we have other codepaths for other KtDeclarationWithBodies (like property accessors/secondary constructors/class initializers) */ - val trace = DelegatingBindingTrace( - resolveSession.bindingContext, - "Trace for resolution of " + analyzableElement, - allowSliceRewrite = true - ) - - val moduleInfo = analyzableElement.containingKtFile.getModuleInfo() - - // TODO: should return proper platform! - val targetPlatform = moduleInfo.platform ?: TargetPlatformDetector.getPlatform(analyzableElement.containingKtFile) - val lazyTopDownAnalyzer = createContainerForLazyBodyResolve( //TODO: should get ModuleContext globalContext.withProject(project).withModule(moduleDescriptor), diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt index 1911924dbae..e348f05c0de 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager -import com.intellij.psi.util.PsiModificationTracker import com.intellij.util.containers.SLRUCache import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.context.GlobalContextImpl @@ -28,6 +27,7 @@ import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo +import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.CompositeBindingContext @@ -69,7 +69,8 @@ internal class ProjectResolutionFacade( } } - val allDependencies = resolverForProjectDependencies + listOf(PsiModificationTracker.MODIFICATION_COUNT) + val allDependencies = + resolverForProjectDependencies + listOf(KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker) CachedValueProvider.Result.create(results, allDependencies) }, false ) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinCodeBlockModificationListener.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinCodeBlockModificationListener.kt index 8314b2d7107..4d0c927f32a 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinCodeBlockModificationListener.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/trackers/KotlinCodeBlockModificationListener.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.caches.trackers +import com.intellij.lang.ASTNode import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.openapi.util.ModificationTracker @@ -16,10 +17,7 @@ import com.intellij.pom.event.PomModelEvent import com.intellij.pom.event.PomModelListener import com.intellij.pom.tree.TreeAspect import com.intellij.pom.tree.events.TreeChangeEvent -import com.intellij.psi.PsiDirectory -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiManager -import com.intellij.psi.PsiTreeChangeEvent +import com.intellij.psi.* import com.intellij.psi.impl.PsiManagerImpl import com.intellij.psi.impl.PsiModificationTrackerImpl import com.intellij.psi.impl.PsiTreeChangeEventImpl @@ -89,9 +87,17 @@ class KotlinCodeBlockModificationListener( incFileModificationCount(ktFile) val changedElements = changeSet.changedElements - // When a code fragment is reparsed, Intellij doesn't do an AST diff and considers the entire - // contents to be replaced, which is represented in a POM event as an empty list of changed elements - if (changedElements.any { getInsideCodeBlockModificationScope(it.psi) == null } || changedElements.isEmpty()) { + + // skip change if it contains only virtual/fake change + if (changedElements.isNotEmpty() && + // ignore formatting (whitespaces etc) + (isFormattingChange(changeSet) || + changedElements.all { !it.psi.isPhysical }) + ) return + + val inBlockChange = inBlockModifications(changedElements) + + if (!inBlockChange) { messageBusConnection.deliverImmediately() if (ktFile.isPhysical && !isReplLine(ktFile.virtualFile)) { @@ -155,6 +161,8 @@ class KotlinCodeBlockModificationListener( } private fun incOutOfBlockModificationCount(file: KtFile) { + file.clearInBlockModifications() + val count = file.getUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT) ?: 0 file.putUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT, count + 1) } @@ -165,32 +173,72 @@ class KotlinCodeBlockModificationListener( tracker.incModificationCount() } - fun getInsideCodeBlockModificationScope(element: PsiElement): KtElement? { + private fun inBlockModifications(elements: Array): Boolean { + // When a code fragment is reparsed, Intellij doesn't do an AST diff and considers the entire + // contents to be replaced, which is represented in a POM event as an empty list of changed elements + if (elements.isEmpty()) return false + + val inBlockElements = mutableSetOf() + for (element in elements) { + // skip fake PSI elements like `IntellijIdeaRulezzz$` + val psi = element.psi + if (!psi.isPhysical) continue + + val modificationScope = getInsideCodeBlockModificationScope(psi) ?: return false + + inBlockElements.add(modificationScope.blockDeclaration) + } + + inBlockElements.forEach { it.containingKtFile.addInBlockModifiedItem(it) } + return inBlockElements.isNotEmpty() + } + + fun isFormattingChange(changeSet: TreeChangeEvent): Boolean = + changeSet.changedElements.all { + changeSet.getChangesByElement(it).affectedChildren.all { c -> (c is PsiWhiteSpace || c is PsiComment) } + } + + fun getInsideCodeBlockModificationScope(element: PsiElement): BlockModificationScopeElement? { val lambda = element.getTopmostParentOfType() if (lambda is KtLambdaExpression) { lambda.getTopmostParentOfType()?.let { - return it + return BlockModificationScopeElement(it, it) } } - val blockDeclaration = KtPsiUtil.getTopmostParentOfTypes(element, *BLOCK_DECLARATION_TYPES) as? KtDeclaration ?: return null - if (KtPsiUtil.isLocal(blockDeclaration)) return null // should not be local declaration + val blockDeclaration = + KtPsiUtil.getTopmostParentOfTypes(element, *BLOCK_DECLARATION_TYPES) as? KtDeclaration ?: return null + + // should not be local declaration + if (KtPsiUtil.isLocal(blockDeclaration)) + return null when (blockDeclaration) { is KtNamedFunction -> { if (blockDeclaration.hasBlockBody()) { - return blockDeclaration.bodyExpression?.takeIf { it.isAncestor(element) } + // case like `fun foo(): String {......}` + return blockDeclaration.bodyExpression + ?.takeIf { it.isAncestor(element) } + ?.let { BlockModificationScopeElement(blockDeclaration, it) } } else if (blockDeclaration.hasDeclaredReturnType()) { - return blockDeclaration.initializer?.takeIf { it.isAncestor(element) } + // case like `fun foo(): String = blabla` + return blockDeclaration.initializer + ?.takeIf { it.isAncestor(element) } + ?.let { BlockModificationScopeElement(blockDeclaration, it) } } } is KtProperty -> { if (blockDeclaration.typeReference != null) { - for (accessor in blockDeclaration.accessors) { - (accessor.initializer ?: accessor.bodyExpression) - ?.takeIf { it.isAncestor(element) } - ?.let { return it } + val accessors = + blockDeclaration.accessors.map { it.initializer ?: it.bodyExpression } + blockDeclaration.initializer + for (accessor in accessors) { + accessor?.takeIf { + it.isAncestor(element) && + // adding annotations to accessor is the same as change contract of property + (element !is KtAnnotated || element.annotationEntries.isEmpty()) + } + ?.let { return BlockModificationScopeElement(blockDeclaration, it) } } } } @@ -201,14 +249,37 @@ class KotlinCodeBlockModificationListener( ?.lastOrNull() ?.getLambdaExpression() ?.takeIf { it.isAncestor(element) } + ?.let { BlockModificationScopeElement(blockDeclaration, it) } } + is KtClassInitializer -> { + blockDeclaration + .takeIf { it.isAncestor(element) } + ?.let { ktClassInitializer -> + (KtPsiUtil.getTopmostParentOfTypes(blockDeclaration, KtClass::class.java) as? KtElement)?.let { + return BlockModificationScopeElement(it, ktClassInitializer) + } + } + } + + // TODO: still under consideration - is it worth to track changes of private properties / methods + // problem could be in diagnostics - it is worth to manage it with modTracker +// is KtClass -> { +// return when (element) { +// is KtProperty -> if (element.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE) blockDeclaration else null +// is KtNamedFunction -> if (element.visibilityModifierType()?.toVisibility() == Visibilities.PRIVATE) blockDeclaration else null +// else -> null +// } +// } + else -> throw IllegalStateException() } return null } + data class BlockModificationScopeElement(val blockDeclaration: KtElement, val element: KtElement) + fun isBlockDeclaration(declaration: KtDeclaration): Boolean { return BLOCK_DECLARATION_TYPES.any { it.isInstance(declaration) } } @@ -216,6 +287,7 @@ class KotlinCodeBlockModificationListener( private val BLOCK_DECLARATION_TYPES = arrayOf>( KtProperty::class.java, KtNamedFunction::class.java, + KtClassInitializer::class.java, KtScriptInitializer::class.java ) @@ -231,6 +303,32 @@ val KtFile.perFileModificationTracker: ModificationTracker private val FILE_OUT_OF_BLOCK_MODIFICATION_COUNT = Key("FILE_OUT_OF_BLOCK_MODIFICATION_COUNT") -val KtFile.outOfBlockModificationCount: Long - get() = getUserData(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT) ?: 0 +val KtFile.outOfBlockModificationCount: Long by NotNullableUserDataProperty(FILE_OUT_OF_BLOCK_MODIFICATION_COUNT, 0) + +/** + * inBlockModifications is a collection of block elements those have in-block modifications + */ +private val IN_BLOCK_MODIFICATIONS = Key>("IN_BLOCK_MODIFICATIONS") + +val KtFile.inBlockModifications: Collection + get() { + val collection = getUserData(IN_BLOCK_MODIFICATIONS) + return collection ?: emptySet() + } + +private fun KtFile.addInBlockModifiedItem(element: KtElement) { + val collection = putUserDataIfAbsent(IN_BLOCK_MODIFICATIONS, mutableSetOf()) + synchronized(collection) { + collection.add(element) + } +} + +fun KtFile.clearInBlockModifications() { + val collection = getUserData(IN_BLOCK_MODIFICATIONS) + collection?.let { + synchronized(it) { + it.clear() + } + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinChangeLocalityDetector.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinChangeLocalityDetector.kt index ab1090272c2..00bb262e31d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinChangeLocalityDetector.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/KotlinChangeLocalityDetector.kt @@ -18,18 +18,13 @@ package org.jetbrains.kotlin.idea.highlighter import com.intellij.codeInsight.daemon.ChangeLocalityDetector import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener.Companion.getInsideCodeBlockModificationScope class KotlinChangeLocalityDetector : ChangeLocalityDetector { override fun getChangeHighlightingDirtyScopeFor(element: PsiElement): PsiElement? { - val parent = element.parent - if (element is KtBlockExpression && parent is KtNamedFunction && parent.name != null) { - if (parent.parents.all { it is KtClassBody || it is KtClassOrObject || it is KtFile || it is KtScript }) { - return parent - } - } + val modificationScope = + getInsideCodeBlockModificationScope(element) ?: return null - return null + return modificationScope.blockDeclaration } } \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionBindingContextProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionBindingContextProvider.kt index 77da0511364..ce86cf909bb 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionBindingContextProvider.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionBindingContextProvider.kt @@ -107,7 +107,7 @@ class CompletionBindingContextProvider(project: Project) { val inStatement = position.findStatementInBlock() val block = inStatement?.parent as KtBlockExpression? val prevStatement = inStatement?.siblings(forward = false, withItself = false)?.firstIsInstanceOrNull() - val modificationScope = inStatement?.let { KotlinCodeBlockModificationListener.getInsideCodeBlockModificationScope(it) } + val modificationScope = inStatement?.let { KotlinCodeBlockModificationListener.getInsideCodeBlockModificationScope(it)?.element } val psiElementsBeforeAndAfter = modificationScope?.let { collectPsiElementsBeforeAndAfter(modificationScope, inStatement) } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/AbstractCompletionHandlerTests.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/AbstractCompletionHandlerTests.kt index 0484e9d9ac3..8c91a257ec6 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/AbstractCompletionHandlerTests.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/AbstractCompletionHandlerTests.kt @@ -19,13 +19,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.indexOfOrNull import java.io.File abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: CompletionType) : CompletionHandlerTestBase() { - private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:" - private val LOOKUP_STRING_PREFIX = "ELEMENT:" - private val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:" - private val TAIL_TEXT_PREFIX = "TAIL_TEXT:" - private val COMPLETION_CHAR_PREFIX = "CHAR:" - private val COMPLETION_CHARS_PREFIX = "CHARS:" - private val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:" + + companion object { + val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:" + val LOOKUP_STRING_PREFIX = "ELEMENT:" + val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:" + val TAIL_TEXT_PREFIX = "TAIL_TEXT:" + val COMPLETION_CHAR_PREFIX = "CHAR:" + val COMPLETION_CHARS_PREFIX = "CHARS:" + val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:" + } protected open fun doTest(testPath: String) { setUpFixture(fileName()) @@ -42,11 +45,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX) val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_PREFIX) val tailText = InTextDirectivesUtils.findStringWithPrefixes(fileText, TAIL_TEXT_PREFIX) - - val completionChars = completionChars( - char = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHAR_PREFIX), - chars = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHARS_PREFIX) - ) + val completionChars = completionChars(fileText) val completionType = ExpectedCompletionUtils.getCompletionType(fileText) ?: defaultCompletionType @@ -58,8 +57,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: val settingValue = line.substring(index + 1).trim() val (field, settings) = try { kotlinStyleSettings::class.java.getField(settingName) to kotlinStyleSettings - } - catch (e: NoSuchFieldException) { + } catch (e: NoSuchFieldException) { commonStyleSettings::class.java.getField(settingName) to commonStyleSettings } when (field.type.name) { @@ -70,6 +68,7 @@ abstract class AbstractCompletionHandlerTest(private val defaultCompletionType: } doTestWithTextLoaded( + myFixture, completionType, invocationCount, lookupString, diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTest.kt index c567777e0dd..1057fb5314d 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTest.kt @@ -31,7 +31,16 @@ class BasicCompletionHandlerTest : CompletionHandlerTestBase(){ private fun doTest(time: Int, lookupString: String?, itemText: String?, tailText: String?, completionChar: Char) { fixture.configureByFile(fileName()) - doTestWithTextLoaded(CompletionType.BASIC, time, lookupString, itemText, tailText, completionChar.toString(), fileName() + ".after") + doTestWithTextLoaded( + myFixture, + CompletionType.BASIC, + time, + lookupString, + itemText, + tailText, + completionChar.toString(), + fileName() + ".after" + ) } fun testClassCompletionImport() = doTest(2, "SortedSet", null, '\n') diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionHandlerTestBase.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionHandlerTestBase.kt index 1fc1c5d24b2..59059c75889 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionHandlerTestBase.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionHandlerTestBase.kt @@ -11,114 +11,133 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.LookupEvent import com.intellij.codeInsight.lookup.LookupManager import com.intellij.codeInsight.lookup.impl.LookupImpl +import com.intellij.openapi.project.Project import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.test.InTextDirectivesUtils abstract class CompletionHandlerTestBase : KotlinLightCodeInsightFixtureTestCase() { protected val fixture: JavaCodeInsightTestFixture get() = myFixture - protected fun doTestWithTextLoaded( - completionType: CompletionType, - time: Int, - lookupString: String?, - itemText: String?, - tailText: String?, - completionChars: String, - afterFilePath: String - ) { - for (idx in 0 until completionChars.length - 1) { - fixture.type(completionChars[idx]) + companion object { + fun doTestWithTextLoaded( + fixture: JavaCodeInsightTestFixture, + completionType: CompletionType, + time: Int, + lookupString: String?, + itemText: String?, + tailText: String?, + completionChars: String, + afterFilePath: String, + actions: List? = emptyList(), + afterTypingBlock: () -> Unit = {} + ) { + for (idx in 0 until completionChars.length - 1) { + fixture.type(completionChars[idx]) + afterTypingBlock() + } + + if (actions != null && actions.isNotEmpty()) { + for (action in actions) { + fixture.performEditorAction(action) + } + } + + fixture.complete(completionType, time) + + if (lookupString != null || itemText != null || tailText != null) { + val item = getExistentLookupElement(fixture.project, lookupString, itemText, tailText) + if (item != null) { + selectItem(fixture, item, completionChars.last()) + } + } + afterTypingBlock() + + fixture.checkResultByFile(afterFilePath) } - fixture.complete(completionType, time) - - if (lookupString != null || itemText != null || tailText != null) { - val item = getExistentLookupElement(lookupString, itemText, tailText) - if (item != null) { - selectItem(item, completionChars.last()) + fun completionChars(text: String): String { + val char: String? = InTextDirectivesUtils.findStringWithPrefixes(text, AbstractCompletionHandlerTest.COMPLETION_CHAR_PREFIX) + val chars: String? = InTextDirectivesUtils.findStringWithPrefixes(text, AbstractCompletionHandlerTest.COMPLETION_CHARS_PREFIX) + return when (char) { + null -> when (chars) { + null -> "\n" + else -> chars.replace("\\n", "\n").replace("\\t", "\t") + } + "\\n" -> "\n" + "\\t" -> "\t" + else -> char.single().toString() ?: error("Incorrect completion char: \"$char\"") } } - fixture.checkResultByFile(afterFilePath) - } - protected fun completionChars(char: String?, chars: String?): String = - when (char) { - null -> when (chars) { - null -> "\n" - else -> chars.replace("\\n", "\n").replace("\\t", "\t") + fun getExistentLookupElement(project: Project, lookupString: String?, itemText: String?, tailText: String?): LookupElement? { + val lookup = LookupManager.getInstance(project)?.activeLookup as LookupImpl? ?: return null + val items = lookup.items + + if (lookupString == "*") { + assert(itemText == null) + assert(tailText == null) + return items.firstOrNull() } - "\\n" -> "\n" - "\\t" -> "\t" - else -> char.single().toString() ?: error("Incorrect completion char: \"$char\"") - } - protected fun getExistentLookupElement(lookupString: String?, itemText: String?, tailText: String?): LookupElement? { - val lookup = LookupManager.getInstance(project)?.activeLookup as LookupImpl? ?: return null - val items = lookup.items + var foundElement : LookupElement? = null + val presentation = LookupElementPresentation() + for (lookupElement in items) { + val lookupOk = if (lookupString != null) lookupElement.lookupString == lookupString else true - if (lookupString == "*") { - assert(itemText == null) - assert(tailText == null) - return items.firstOrNull() - } + if (lookupOk) { + lookupElement.renderElement(presentation) - var foundElement : LookupElement? = null - val presentation = LookupElementPresentation() - for (lookupElement in items) { - val lookupOk = if (lookupString != null) lookupElement.lookupString == lookupString else true - - if (lookupOk) { - lookupElement.renderElement(presentation) - - val textOk = if (itemText != null) { - val itemItemText = presentation.itemText - itemItemText != null && itemItemText == itemText - } - else { - true - } - - if (textOk) { - val tailOk = if (tailText != null) { - val itemTailText = presentation.tailText - itemTailText != null && itemTailText == tailText + val textOk = if (itemText != null) { + val itemItemText = presentation.itemText + itemItemText != null && itemItemText == itemText } else { true } - if (tailOk) { - if (foundElement != null) { - val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(arrayOf(foundElement, lookupElement))) - fail("Several elements satisfy to completion restrictions:\n$dump") + if (textOk) { + val tailOk = if (tailText != null) { + val itemTailText = presentation.tailText + itemTailText != null && itemTailText == tailText + } + else { + true } - foundElement = lookupElement + if (tailOk) { + if (foundElement != null) { + val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(arrayOf(foundElement, lookupElement))) + fail("Several elements satisfy to completion restrictions:\n$dump") + } + + foundElement = lookupElement + } } } } + + if (foundElement == null) { + val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items.toTypedArray())) + error("No element satisfy completion restrictions in:\n$dump") + } + return foundElement } - if (foundElement == null) { - val dump = ExpectedCompletionUtils.listToString(ExpectedCompletionUtils.getItemsInformation(items.toTypedArray())) - error("No element satisfy completion restrictions in:\n$dump") - } - return foundElement - } - - protected fun selectItem(item: LookupElement?, completionChar: Char) { - val lookup = (fixture.lookup as LookupImpl) - if (lookup.currentItem != item) { // do not touch selection if not changed - important for char filter tests - lookup.currentItem = item - } - lookup.focusDegree = LookupImpl.FocusDegree.FOCUSED - if (LookupEvent.isSpecialCompletionChar(completionChar)) { - lookup.finishLookup(completionChar) - } - else { - fixture.type(completionChar) + fun selectItem(fixture: JavaCodeInsightTestFixture, item: LookupElement?, completionChar: Char) { + val lookup = (fixture.lookup as LookupImpl) + if (lookup.currentItem != item) { // do not touch selection if not changed - important for char filter tests + lookup.currentItem = item + } + lookup.focusDegree = LookupImpl.FocusDegree.FOCUSED + if (LookupEvent.isSpecialCompletionChar(completionChar)) { + lookup.finishLookup(completionChar) + } + else { + fixture.type(completionChar) + } } } } diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt index 9a35a62cf02..8c2fc0fd9cb 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt @@ -12,6 +12,13 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils import org.jetbrains.kotlin.idea.completion.test.configureWithExtraFile +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.CODE_STYLE_SETTING_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.COMPLETION_CHARS_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.COMPLETION_CHAR_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.ELEMENT_TEXT_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.INVOCATION_COUNT_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.LOOKUP_STRING_PREFIX +import org.jetbrains.kotlin.idea.completion.test.handlers.AbstractCompletionHandlerTest.Companion.TAIL_TEXT_PREFIX import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.test.configureCompilerOptions @@ -29,14 +36,6 @@ abstract class AbstractPerformanceCompletionHandlerTests( private val note: String = "" ) : CompletionHandlerTestBase() { - private val INVOCATION_COUNT_PREFIX = "INVOCATION_COUNT:" - private val LOOKUP_STRING_PREFIX = "ELEMENT:" - private val ELEMENT_TEXT_PREFIX = "ELEMENT_TEXT:" - private val TAIL_TEXT_PREFIX = "TAIL_TEXT:" - private val COMPLETION_CHAR_PREFIX = "CHAR:" - private val COMPLETION_CHARS_PREFIX = "CHARS:" - private val CODE_STYLE_SETTING_PREFIX = "CODE_STYLE_SETTING:" - companion object { @JvmStatic val statsMap: MutableMap = mutableMapOf() @@ -74,11 +73,7 @@ abstract class AbstractPerformanceCompletionHandlerTests( val lookupString = InTextDirectivesUtils.findStringWithPrefixes(fileText, LOOKUP_STRING_PREFIX) val itemText = InTextDirectivesUtils.findStringWithPrefixes(fileText, ELEMENT_TEXT_PREFIX) val tailText = InTextDirectivesUtils.findStringWithPrefixes(fileText, TAIL_TEXT_PREFIX) - - val completionChars = completionChars( - char = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHAR_PREFIX), - chars = InTextDirectivesUtils.findStringWithPrefixes(fileText, COMPLETION_CHARS_PREFIX) - ) + val completionChars = completionChars(fileText) val completionType = ExpectedCompletionUtils.getCompletionType(fileText) ?: defaultCompletionType @@ -157,9 +152,9 @@ abstract class AbstractPerformanceCompletionHandlerTests( fixture.complete(completionType, time) if (lookupString != null || itemText != null || tailText != null) { - val item = getExistentLookupElement(lookupString, itemText, tailText) + val item = getExistentLookupElement(project, lookupString, itemText, tailText) if (item != null) { - selectItem(item, completionChars.last()) + selectItem(fixture, item, completionChars.last()) } } } diff --git a/idea/testData/codeInsight/outOfBlock/FunBlock.kt b/idea/testData/codeInsight/outOfBlock/FunBlock.kt new file mode 100644 index 00000000000..745785e378f --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/FunBlock.kt @@ -0,0 +1,4 @@ +// OUT_OF_CODE_BLOCK: FALSE +fun some(): Int = run { 12 + } + +// TYPE: 1 \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/FunInFun.kt b/idea/testData/codeInsight/outOfBlock/FunInFun.kt index 0b3c23504f1..73b292e48ff 100644 --- a/idea/testData/codeInsight/outOfBlock/FunInFun.kt +++ b/idea/testData/codeInsight/outOfBlock/FunInFun.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun main() { fun some = 12 } \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/FunInInitBlock.kt b/idea/testData/codeInsight/outOfBlock/FunInInitBlock.kt index 2eda898028b..516d4366943 100644 --- a/idea/testData/codeInsight/outOfBlock/FunInInitBlock.kt +++ b/idea/testData/codeInsight/outOfBlock/FunInInitBlock.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE class A { init { @@ -6,7 +6,4 @@ class A { } } -} - -// TODO -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/FunNoBody.kt b/idea/testData/codeInsight/outOfBlock/FunNoBody.kt index 458f568b695..663d4b06899 100644 --- a/idea/testData/codeInsight/outOfBlock/FunNoBody.kt +++ b/idea/testData/codeInsight/outOfBlock/FunNoBody.kt @@ -1,2 +1,2 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE fun some() = 12 \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/FunNoType_Block.kt b/idea/testData/codeInsight/outOfBlock/FunNoType_Block.kt index fb591ee520d..b03b36904e7 100644 --- a/idea/testData/codeInsight/outOfBlock/FunNoType_Block.kt +++ b/idea/testData/codeInsight/outOfBlock/FunNoType_Block.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE fun test() { } diff --git a/idea/testData/codeInsight/outOfBlock/FunNoType_Block_Class.kt b/idea/testData/codeInsight/outOfBlock/FunNoType_Block_Class.kt index 993740c1f3a..f54a412bc72 100644 --- a/idea/testData/codeInsight/outOfBlock/FunNoType_Block_Class.kt +++ b/idea/testData/codeInsight/outOfBlock/FunNoType_Block_Class.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun test() { class Test { diff --git a/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer.kt b/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer.kt index 0550032dda5..2779088aabd 100644 --- a/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer.kt +++ b/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE // (Investigation starts from parent) fun test() : Int = 12 diff --git a/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer_Expression.kt b/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer_Expression.kt index 5b46c5502b3..3c36f684642 100644 --- a/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer_Expression.kt +++ b/idea/testData/codeInsight/outOfBlock/FunWithType_Initializer_Expression.kt @@ -1,3 +1,3 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun test() : Int = 12 + 12 \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InAntonymsObjectDeclaration.kt b/idea/testData/codeInsight/outOfBlock/InAntonymsObjectDeclaration.kt index 2fa2ca10eb4..d2cf3f01df6 100644 --- a/idea/testData/codeInsight/outOfBlock/InAntonymsObjectDeclaration.kt +++ b/idea/testData/codeInsight/outOfBlock/InAntonymsObjectDeclaration.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE val o = object { fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/InClass.kt b/idea/testData/codeInsight/outOfBlock/InClass.kt index 6a6e0731fd1..a67d257f0a0 100644 --- a/idea/testData/codeInsight/outOfBlock/InClass.kt +++ b/idea/testData/codeInsight/outOfBlock/InClass.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE class Some { fun } diff --git a/idea/testData/codeInsight/outOfBlock/InClassFunctionWithoutInference.kt b/idea/testData/codeInsight/outOfBlock/InClassFunctionWithoutInference.kt new file mode 100644 index 00000000000..922cef9e8da --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InClassFunctionWithoutInference.kt @@ -0,0 +1,11 @@ +// OUT_OF_CODE_BLOCK: TRUE + +class A { + fun foo(): Int = 12 + + fun bar(): Int = foo() + +} + +// TYPE: 1 +// TODO +// SKIP_ANALYZE_CHECK \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InClassPropertyAccessor.kt b/idea/testData/codeInsight/outOfBlock/InClassPropertyAccessor.kt index 41d43fc007f..e0b700fd4a4 100644 --- a/idea/testData/codeInsight/outOfBlock/InClassPropertyAccessor.kt +++ b/idea/testData/codeInsight/outOfBlock/InClassPropertyAccessor.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE class Test { val more : Int = 0 val test : Int diff --git a/idea/testData/codeInsight/outOfBlock/InExtensionFunction.kt b/idea/testData/codeInsight/outOfBlock/InExtensionFunction.kt new file mode 100644 index 00000000000..805c5acc814 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InExtensionFunction.kt @@ -0,0 +1,11 @@ +// OUT_OF_CODE_BLOCK: FALSE + +class A { + fun foo(): Int = 12 +} + +fun A.bar(): Int { + return foo() + +} + +// TYPE: 1 diff --git a/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithInference.kt b/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithInference.kt new file mode 100644 index 00000000000..ff8c4f31755 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithInference.kt @@ -0,0 +1,9 @@ +// OUT_OF_CODE_BLOCK: TRUE + +class A { + fun foo(): Int = 12 +} + +fun A.bar() = foo() + + +// TYPE: 1 diff --git a/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithoutInference.kt b/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithoutInference.kt new file mode 100644 index 00000000000..04d5301cbd9 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithoutInference.kt @@ -0,0 +1,11 @@ +// OUT_OF_CODE_BLOCK: TRUE + +class A { + fun foo(): Int = 12 +} + +fun A.bar(): Int = foo() + + +// TYPE: 1 +// TODO +// SKIP_ANALYZE_CHECK \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InFunInFunctionInitializerInFun.kt b/idea/testData/codeInsight/outOfBlock/InFunInFunctionInitializerInFun.kt index 9e48681bfc9..f4e680fde96 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunInFunctionInitializerInFun.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunInFunctionInitializerInFun.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE val a = 1 fun test() = if (a) { diff --git a/idea/testData/codeInsight/outOfBlock/InFunInMultiDeclaration.kt b/idea/testData/codeInsight/outOfBlock/InFunInMultiDeclaration.kt index 216da706503..0e90e70afb1 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunInMultiDeclaration.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunInMultiDeclaration.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE data class Test(val a : Int, val b : Int) diff --git a/idea/testData/codeInsight/outOfBlock/InFunInProperty.kt b/idea/testData/codeInsight/outOfBlock/InFunInProperty.kt index bd55a45a5d5..d32e9bf8f95 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunInProperty.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunInProperty.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun test() { val some = if () { fun other() { diff --git a/idea/testData/codeInsight/outOfBlock/InFunInPropertyInObjectLiteral.kt b/idea/testData/codeInsight/outOfBlock/InFunInPropertyInObjectLiteral.kt index 2f1f5bbded9..4a89d2994cc 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunInPropertyInObjectLiteral.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunInPropertyInObjectLiteral.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE interface Some fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/InFunObjectLiteral.kt b/idea/testData/codeInsight/outOfBlock/InFunObjectLiteral.kt index a321fed787a..14a8c588b5c 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunObjectLiteral.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunObjectLiteral.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE interface Some fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/InFunWithInference.kt b/idea/testData/codeInsight/outOfBlock/InFunWithInference.kt deleted file mode 100644 index 9db1fbd928f..00000000000 --- a/idea/testData/codeInsight/outOfBlock/InFunWithInference.kt +++ /dev/null @@ -1,2 +0,0 @@ -// TRUE -fun more() = { println } \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InFunctionLiteral.kt b/idea/testData/codeInsight/outOfBlock/InFunctionLiteral.kt index 0a22e760487..1261a146a7d 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunctionLiteral.kt +++ b/idea/testData/codeInsight/outOfBlock/InFunctionLiteral.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun main() { jq() { pri } diff --git a/idea/testData/codeInsight/outOfBlock/InGlobalPropertyWithGetter.kt b/idea/testData/codeInsight/outOfBlock/InGlobalPropertyWithGetter.kt index f5038062b8d..68bd74b8610 100644 --- a/idea/testData/codeInsight/outOfBlock/InGlobalPropertyWithGetter.kt +++ b/idea/testData/codeInsight/outOfBlock/InGlobalPropertyWithGetter.kt @@ -1,5 +1,4 @@ -// FALSE -// Important for 173 branch! OOCB is TRUE in this test because of IDEA-185462 +// OUT_OF_CODE_BLOCK: FALSE class B(val a: A) val B.foo: Int diff --git a/idea/testData/codeInsight/outOfBlock/InLambdaFunction.kt b/idea/testData/codeInsight/outOfBlock/InLambdaFunction.kt new file mode 100644 index 00000000000..4a5c977b7a5 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InLambdaFunction.kt @@ -0,0 +1,9 @@ +// OUT_OF_CODE_BLOCK: FALSE + +fun twice(s: String): String { + val repeatFun: String.(Int) -> String = { t -> this.repeat() } + + return repeatFun(s, 2) +} + +// TYPE: t diff --git a/idea/testData/codeInsight/outOfBlock/InMethod.kt b/idea/testData/codeInsight/outOfBlock/InMethod.kt index 5760a37a861..0e29dfaa457 100644 --- a/idea/testData/codeInsight/outOfBlock/InMethod.kt +++ b/idea/testData/codeInsight/outOfBlock/InMethod.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun test() { val a = 1 diff --git a/idea/testData/codeInsight/outOfBlock/InClassInClass.kt b/idea/testData/codeInsight/outOfBlock/InNestedClass.kt similarity index 66% rename from idea/testData/codeInsight/outOfBlock/InClassInClass.kt rename to idea/testData/codeInsight/outOfBlock/InNestedClass.kt index 8365185537f..a3f942a8ec0 100644 --- a/idea/testData/codeInsight/outOfBlock/InClassInClass.kt +++ b/idea/testData/codeInsight/outOfBlock/InNestedClass.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE class Test { class Other { diff --git a/idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block.kt b/idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlock.kt similarity index 76% rename from idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block.kt rename to idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlock.kt index b9848899881..adc38c1680c 100644 --- a/idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block.kt +++ b/idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlock.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE class Test { class Other { diff --git a/idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block_Expression.kt b/idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlockExpression.kt similarity index 77% rename from idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block_Expression.kt rename to idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlockExpression.kt index 14b4dbed0b5..84656258237 100644 --- a/idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block_Expression.kt +++ b/idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlockExpression.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE class Test { class Other { diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorSpecifyType.kt b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorSpecifyType.kt new file mode 100644 index 00000000000..22438436bbb --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorSpecifyType.kt @@ -0,0 +1,7 @@ +// OUT_OF_CODE_BLOCK: TRUE +// TYPE: 'Int` + +class InPropertyAccessorSpecifyType { + val prop1: Int + get(): = 42 +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithAnnotation.kt b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithAnnotation.kt new file mode 100644 index 00000000000..609b4b43901 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithAnnotation.kt @@ -0,0 +1,8 @@ +// OUT_OF_CODE_BLOCK: TRUE +// TYPE: '@Throws(Exception::class)` + +class InPropertyAccessorWithAnnotation { + val prop1: Int + + get() = 42 +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInference.kt b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInference.kt index d2577d4575c..73b1f50f8a0 100644 --- a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInference.kt +++ b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInference.kt @@ -1,6 +1,6 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE val test : Int get() = 12 -// TODO -// SKIP_ANALYZE_CHECK \ No newline at end of file +// TODO: Investigate +// SKIP_ANALYZE_CHECK diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInferenceInClass.kt b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInferenceInClass.kt index 5b91116857b..aa7f35d37db 100644 --- a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInferenceInClass.kt +++ b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInferenceInClass.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE class A { fun foo(): Int = 12 diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithoutInferenceInClass.kt b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithoutInferenceInClass.kt new file mode 100644 index 00000000000..a0698d9c3f9 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithoutInferenceInClass.kt @@ -0,0 +1,10 @@ +// OUT_OF_CODE_BLOCK: FALSE + +class A { + fun foo(): Int = 12 +} + +class B(val a: A) { + val prop1: Int + get() = a.foo() +} diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyWithFunctionLiteral.kt b/idea/testData/codeInsight/outOfBlock/InPropertyWithFunctionLiteral.kt index cdac03deab1..06765a8799b 100644 --- a/idea/testData/codeInsight/outOfBlock/InPropertyWithFunctionLiteral.kt +++ b/idea/testData/codeInsight/outOfBlock/InPropertyWithFunctionLiteral.kt @@ -1,6 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE class Test { val a : () -> Int = { pri } -} - -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InPropertyWithInference.kt b/idea/testData/codeInsight/outOfBlock/InPropertyWithInference.kt index 5cde4f2a5c2..b0a0a541f27 100644 --- a/idea/testData/codeInsight/outOfBlock/InPropertyWithInference.kt +++ b/idea/testData/codeInsight/outOfBlock/InPropertyWithInference.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE class Test { val a = "aasdf" } \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt index 4f7ddd1068b..c6d27d681d6 100644 --- a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallCallInLambdaInCall.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE // Navigation from "class B: A()" should move to valid constructor even after changing type in lambda diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt index 695e6ed7ec8..e18e9964dc1 100644 --- a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambaInBody.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE open class A(a: () -> Unit) diff --git a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt index 63e2fe74e9c..b00ca495c6c 100644 --- a/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt +++ b/idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE open class A(a: () -> Unit) { constructor(f: (String) -> Unit) : this({ -> f("") }) diff --git a/idea/testData/codeInsight/outOfBlock/InTopFun.kt b/idea/testData/codeInsight/outOfBlock/InTopFun.kt new file mode 100644 index 00000000000..6356c1a1853 --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/InTopFun.kt @@ -0,0 +1,2 @@ +// OUT_OF_CODE_BLOCK: FALSE +fun more() { println } \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InitBlock.kt b/idea/testData/codeInsight/outOfBlock/InitBlock.kt index ba410cebc14..457a2335618 100644 --- a/idea/testData/codeInsight/outOfBlock/InitBlock.kt +++ b/idea/testData/codeInsight/outOfBlock/InitBlock.kt @@ -1,10 +1,7 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE class A { init { call() } -} - -// TODO: -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/InFunInFunWithBody.kt b/idea/testData/codeInsight/outOfBlock/LocalFunWithBody.kt similarity index 62% rename from idea/testData/codeInsight/outOfBlock/InFunInFunWithBody.kt rename to idea/testData/codeInsight/outOfBlock/LocalFunWithBody.kt index d936f0e3748..7fbc5e3ce17 100644 --- a/idea/testData/codeInsight/outOfBlock/InFunInFunWithBody.kt +++ b/idea/testData/codeInsight/outOfBlock/LocalFunWithBody.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE fun test() { fun hello() { diff --git a/idea/testData/codeInsight/outOfBlock/LocalFunWithBodyInClass.kt b/idea/testData/codeInsight/outOfBlock/LocalFunWithBodyInClass.kt new file mode 100644 index 00000000000..f339634bd5f --- /dev/null +++ b/idea/testData/codeInsight/outOfBlock/LocalFunWithBodyInClass.kt @@ -0,0 +1,8 @@ +// OUT_OF_CODE_BLOCK: FALSE +class LocalFunWithBodyInClass { + fun test() { + fun hello() { + + } + } +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block.kt b/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block.kt index 5a72266cb3c..1cc62d23bea 100644 --- a/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block.kt +++ b/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE object Some { fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block_Expression.kt b/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block_Expression.kt index af583024d26..6d0facc7b69 100644 --- a/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block_Expression.kt +++ b/idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block_Expression.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE object Some { fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/PropNotNullType_Initializer_ObjectLiteral_Fun.kt b/idea/testData/codeInsight/outOfBlock/PropNotNullType_Initializer_ObjectLiteral_Fun.kt index 0308ee2d73e..f11e0089c99 100644 --- a/idea/testData/codeInsight/outOfBlock/PropNotNullType_Initializer_ObjectLiteral_Fun.kt +++ b/idea/testData/codeInsight/outOfBlock/PropNotNullType_Initializer_ObjectLiteral_Fun.kt @@ -1,12 +1,9 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE -// Problem with lazy initialization of nullable properties interface Some val test: Some = object: Some { fun test() { } -} - -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_If_Fun.kt b/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_If_Fun.kt index 89583bc74ad..c971827b222 100644 --- a/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_If_Fun.kt +++ b/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_If_Fun.kt @@ -1,6 +1,4 @@ -// TRUE - -// Problem with lazy initialization of nullable properties +// OUT_OF_CODE_BLOCK: FALSE val test: Int? = if (true) { fun test() { @@ -9,6 +7,4 @@ val test: Int? = if (true) { } else { -} - -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_ObjectLiteral_Fun.kt b/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_ObjectLiteral_Fun.kt index a2124069cbe..0a3f8b0b933 100644 --- a/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_ObjectLiteral_Fun.kt +++ b/idea/testData/codeInsight/outOfBlock/PropNullType_Initializer_ObjectLiteral_Fun.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE // Problem with lazy initialization of nullable properties interface Some @@ -7,6 +7,4 @@ val test: Some? = object: Some { fun test() { } -} - -// SKIP_ANALYZE_CHECK \ No newline at end of file +} \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/PropertyNoType_Initializer_String.kt b/idea/testData/codeInsight/outOfBlock/PropertyNoType_Initializer_String.kt index aa541ff3b65..4559e6b74cb 100644 --- a/idea/testData/codeInsight/outOfBlock/PropertyNoType_Initializer_String.kt +++ b/idea/testData/codeInsight/outOfBlock/PropertyNoType_Initializer_String.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE // TODO: Investigate diff --git a/idea/testData/codeInsight/outOfBlock/PropertyWithType_Initializer_String.kt b/idea/testData/codeInsight/outOfBlock/PropertyWithType_Initializer_String.kt index 025dfc44176..900189965eb 100644 --- a/idea/testData/codeInsight/outOfBlock/PropertyWithType_Initializer_String.kt +++ b/idea/testData/codeInsight/outOfBlock/PropertyWithType_Initializer_String.kt @@ -1,5 +1,3 @@ -// TRUE +// OUT_OF_CODE_BLOCK: FALSE -val test: String = "" - -// SKIP_ANALYZE_CHECK \ No newline at end of file +val test: String = "" \ No newline at end of file diff --git a/idea/testData/codeInsight/outOfBlock/StringInSuperConstroctorCall.kt b/idea/testData/codeInsight/outOfBlock/StringInSuperConstroctorCall.kt index 679ec8f1fba..c8ea84f37ce 100644 --- a/idea/testData/codeInsight/outOfBlock/StringInSuperConstroctorCall.kt +++ b/idea/testData/codeInsight/outOfBlock/StringInSuperConstroctorCall.kt @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE abstract class S(val f: () -> Unit) diff --git a/idea/testData/codeInsight/outOfBlock/WholeFileChanged.kt b/idea/testData/codeInsight/outOfBlock/WholeFileChanged.kt index cc4664f431c..48e487bca5e 100644 --- a/idea/testData/codeInsight/outOfBlock/WholeFileChanged.kt +++ b/idea/testData/codeInsight/outOfBlock/WholeFileChanged.kt @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE // SKIP_ANALYZE_CHECK fun test() { diff --git a/idea/testData/codeInsight/outOfBlock/scriptInLambdaExpression.kts b/idea/testData/codeInsight/outOfBlock/scriptInLambdaExpression.kts index 1fe8a82535f..9d02de66b74 100644 --- a/idea/testData/codeInsight/outOfBlock/scriptInLambdaExpression.kts +++ b/idea/testData/codeInsight/outOfBlock/scriptInLambdaExpression.kts @@ -1,4 +1,4 @@ -// FALSE +// OUT_OF_CODE_BLOCK: FALSE plugins { } diff --git a/idea/testData/codeInsight/outOfBlock/scriptTopLevelCallExpression.kts b/idea/testData/codeInsight/outOfBlock/scriptTopLevelCallExpression.kts index 510f3b10235..b6356b5e9e0 100644 --- a/idea/testData/codeInsight/outOfBlock/scriptTopLevelCallExpression.kts +++ b/idea/testData/codeInsight/outOfBlock/scriptTopLevelCallExpression.kts @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE fun foo() = 1 diff --git a/idea/testData/codeInsight/outOfBlock/scriptTopLevelExpression.kts b/idea/testData/codeInsight/outOfBlock/scriptTopLevelExpression.kts index 238f826bc3e..e4d0e47834d 100644 --- a/idea/testData/codeInsight/outOfBlock/scriptTopLevelExpression.kts +++ b/idea/testData/codeInsight/outOfBlock/scriptTopLevelExpression.kts @@ -1,4 +1,4 @@ -// TRUE +// OUT_OF_CODE_BLOCK: TRUE 1 // SKIP_ANALYZE_CHECK diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java index 13e7408802e..422d51952e6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java @@ -26,11 +26,15 @@ import java.io.IOException; public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCodeInsightFixtureTestCase { + public static final String OUT_OF_CODE_BLOCK_DIRECTIVE = "OUT_OF_CODE_BLOCK:"; + public static final String SKIP_ANALYZE_CHECK_DIRECTIVE = "SKIP_ANALYZE_CHECK"; + public static final String TYPE_DIRECTIVE = "TYPE:"; + protected void doTest(String unused) throws IOException { myFixture.configureByFile(fileName()); boolean expectedOutOfBlock = getExpectedOutOfBlockResult(); - boolean isSkipCheckDefined = InTextDirectivesUtils.isDirectiveDefined(myFixture.getFile().getText(), "SKIP_ANALYZE_CHECK"); + boolean isSkipCheckDefined = InTextDirectivesUtils.isDirectiveDefined(myFixture.getFile().getText(), SKIP_ANALYZE_CHECK_DIRECTIVE); assertTrue("It's allowed to skip check with analyze only for tests where out-of-block is expected", !isSkipCheckDefined || expectedOutOfBlock); @@ -57,7 +61,7 @@ public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCode assertEquals("Result for out of block test is differs from expected on element in file:\n" + FileUtil.loadFile(testDataFile()), - expectedOutOfBlock, oobBeforeType != oobAfterCount); + expectedOutOfBlock, oobBeforeType != oobAfterCount); if (!isSkipCheckDefined) { checkOOBWithDescriptorsResolve(expectedOutOfBlock); @@ -65,13 +69,9 @@ public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCode } private void checkOOBWithDescriptorsResolve(boolean expectedOutOfBlock) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - ((PsiModificationTrackerImpl) PsiManager.getInstance(myFixture.getProject()).getModificationTracker()) - .incOutOfCodeBlockModificationCounter(); - } - }); + ApplicationManager.getApplication().runReadAction( + () -> ((PsiModificationTrackerImpl) PsiManager.getInstance(myFixture.getProject()).getModificationTracker()) + .incOutOfCodeBlockModificationCounter()); PsiElement updateElement = myFixture.getFile().findElementAt(myFixture.getCaretOffset() - 1); KtExpression ktExpression = PsiTreeUtil.getParentOfType(updateElement, KtExpression.class, false); @@ -104,7 +104,7 @@ public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCode private String getStringToType() { String text = myFixture.getDocument(myFixture.getFile()).getText(); - String typeDirectives = InTextDirectivesUtils.findStringWithPrefixes(text, "TYPE:"); + String typeDirectives = InTextDirectivesUtils.findStringWithPrefixes(text, TYPE_DIRECTIVE); return typeDirectives != null ? StringUtil.unescapeStringCharacters(typeDirectives) : "a"; } @@ -112,17 +112,12 @@ public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCode private boolean getExpectedOutOfBlockResult() { String text = myFixture.getDocument(myFixture.getFile()).getText(); - boolean expectedOutOfBlock = false; - if (text.startsWith("// TRUE")) { - expectedOutOfBlock = true; - } - else if (text.startsWith("// FALSE")) { - expectedOutOfBlock = false; - } - else { - fail("Expectation of code block result test should be configured with " + - "\"// TRUE\" or \"// FALSE\" directive in the beginning of the file"); - } - return expectedOutOfBlock; + String outOfCodeBlockDirective = InTextDirectivesUtils.findStringWithPrefixes(text, OUT_OF_CODE_BLOCK_DIRECTIVE); + assertNotNull(fileName() + + ": Expectation of code block result test should be configured with " + + "\"// " + OUT_OF_CODE_BLOCK_DIRECTIVE + " TRUE\" or " + + "\"// " + OUT_OF_CODE_BLOCK_DIRECTIVE + " FALSE\" directive in the file", + outOfCodeBlockDirective); + return Boolean.parseBoolean(outOfCodeBlockDirective); } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java index cdaac212872..6c98680ba96 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/OutOfBlockModificationTestGenerated.java @@ -28,14 +28,9 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/outOfBlock"), Pattern.compile("^(.+)\\.(kt|kts)$"), true); } - @TestMetadata("Class_Class_FunNoType_Block.kt") - public void testClass_Class_FunNoType_Block() throws Exception { - runTest("idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block.kt"); - } - - @TestMetadata("Class_Class_FunNoType_Block_Expression.kt") - public void testClass_Class_FunNoType_Block_Expression() throws Exception { - runTest("idea/testData/codeInsight/outOfBlock/Class_Class_FunNoType_Block_Expression.kt"); + @TestMetadata("FunBlock.kt") + public void testFunBlock() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/FunBlock.kt"); } @TestMetadata("FunInFun.kt") @@ -83,9 +78,9 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InClass.kt"); } - @TestMetadata("InClassInClass.kt") - public void testInClassInClass() throws Exception { - runTest("idea/testData/codeInsight/outOfBlock/InClassInClass.kt"); + @TestMetadata("InClassFunctionWithoutInference.kt") + public void testInClassFunctionWithoutInference() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InClassFunctionWithoutInference.kt"); } @TestMetadata("InClassPropertyAccessor.kt") @@ -93,9 +88,19 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InClassPropertyAccessor.kt"); } - @TestMetadata("InFunInFunWithBody.kt") - public void testInFunInFunWithBody() throws Exception { - runTest("idea/testData/codeInsight/outOfBlock/InFunInFunWithBody.kt"); + @TestMetadata("InExtensionFunction.kt") + public void testInExtensionFunction() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InExtensionFunction.kt"); + } + + @TestMetadata("InExtensionFunctionWithInference.kt") + public void testInExtensionFunctionWithInference() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithInference.kt"); + } + + @TestMetadata("InExtensionFunctionWithoutInference.kt") + public void testInExtensionFunctionWithoutInference() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InExtensionFunctionWithoutInference.kt"); } @TestMetadata("InFunInFunctionInitializerInFun.kt") @@ -123,11 +128,6 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InFunObjectLiteral.kt"); } - @TestMetadata("InFunWithInference.kt") - public void testInFunWithInference() throws Exception { - runTest("idea/testData/codeInsight/outOfBlock/InFunWithInference.kt"); - } - @TestMetadata("InFunctionLiteral.kt") public void testInFunctionLiteral() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/InFunctionLiteral.kt"); @@ -138,11 +138,41 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InGlobalPropertyWithGetter.kt"); } + @TestMetadata("InLambdaFunction.kt") + public void testInLambdaFunction() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InLambdaFunction.kt"); + } + @TestMetadata("InMethod.kt") public void testInMethod() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/InMethod.kt"); } + @TestMetadata("InNestedClass.kt") + public void testInNestedClass() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InNestedClass.kt"); + } + + @TestMetadata("InNestedClassFunNoTypeBlock.kt") + public void testInNestedClassFunNoTypeBlock() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlock.kt"); + } + + @TestMetadata("InNestedClassFunNoTypeBlockExpression.kt") + public void testInNestedClassFunNoTypeBlockExpression() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InNestedClassFunNoTypeBlockExpression.kt"); + } + + @TestMetadata("InPropertyAccessorSpecifyType.kt") + public void testInPropertyAccessorSpecifyType() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InPropertyAccessorSpecifyType.kt"); + } + + @TestMetadata("InPropertyAccessorWithAnnotation.kt") + public void testInPropertyAccessorWithAnnotation() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithAnnotation.kt"); + } + @TestMetadata("InPropertyAccessorWithInference.kt") public void testInPropertyAccessorWithInference() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInference.kt"); @@ -153,6 +183,11 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithInferenceInClass.kt"); } + @TestMetadata("InPropertyAccessorWithoutInferenceInClass.kt") + public void testInPropertyAccessorWithoutInferenceInClass() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InPropertyAccessorWithoutInferenceInClass.kt"); + } + @TestMetadata("InPropertyWithFunctionLiteral.kt") public void testInPropertyWithFunctionLiteral() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/InPropertyWithFunctionLiteral.kt"); @@ -178,11 +213,26 @@ public class OutOfBlockModificationTestGenerated extends AbstractOutOfBlockModif runTest("idea/testData/codeInsight/outOfBlock/InSuperTypeCallInLambdaParameters.kt"); } + @TestMetadata("InTopFun.kt") + public void testInTopFun() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/InTopFun.kt"); + } + @TestMetadata("InitBlock.kt") public void testInitBlock() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/InitBlock.kt"); } + @TestMetadata("LocalFunWithBody.kt") + public void testLocalFunWithBody() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/LocalFunWithBody.kt"); + } + + @TestMetadata("LocalFunWithBodyInClass.kt") + public void testLocalFunWithBodyInClass() throws Exception { + runTest("idea/testData/codeInsight/outOfBlock/LocalFunWithBodyInClass.kt"); + } + @TestMetadata("Object_FunNoType_Block.kt") public void testObject_FunNoType_Block() throws Exception { runTest("idea/testData/codeInsight/outOfBlock/Object_FunNoType_Block.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightingTest.java b/idea/tests/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightingTest.java index 0a53045a0d8..c984dfac1d2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightingTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightingTest.java @@ -18,12 +18,17 @@ import java.util.List; public abstract class AbstractHighlightingTest extends KotlinLightCodeInsightFixtureTestCase { + public static final String NO_CHECK_INFOS_PREFIX = "// NO_CHECK_INFOS"; + public static final String NO_CHECK_WEAK_WARNINGS_PREFIX = "// NO_CHECK_WEAK_WARNINGS"; + public static final String NO_CHECK_WARNINGS_PREFIX = "// NO_CHECK_WARNINGS"; + public static final String EXPECTED_DUPLICATED_HIGHLIGHTING_PREFIX = "// EXPECTED_DUPLICATED_HIGHLIGHTING"; + protected void doTest(String unused) throws Exception { String fileText = FileUtil.loadFile(new File(testPath()), true); - boolean checkInfos = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// NO_CHECK_INFOS"); - boolean checkWeakWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// NO_CHECK_WEAK_WARNINGS"); - boolean checkWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, "// NO_CHECK_WARNINGS"); - boolean expectedDuplicatedHighlighting = InTextDirectivesUtils.isDirectiveDefined(fileText, "// EXPECTED_DUPLICATED_HIGHLIGHTING"); + boolean checkInfos = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_INFOS_PREFIX); + boolean checkWeakWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_WEAK_WARNINGS_PREFIX); + boolean checkWarnings = !InTextDirectivesUtils.isDirectiveDefined(fileText, NO_CHECK_WARNINGS_PREFIX); + boolean expectedDuplicatedHighlighting = InTextDirectivesUtils.isDirectiveDefined(fileText, EXPECTED_DUPLICATED_HIGHLIGHTING_PREFIX); myFixture.configureByFile(fileName());