mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+5
@@ -96,7 +96,12 @@ public class DataFlowInspectionBase extends AbstractBaseJavaLocalInspectionTool
|
||||
DataFlowInstructionVisitor visitor =
|
||||
analyzeDfaWithNestedClosures(aClass, holder, runner, Collections.singletonList(runner.createMemoryState()));
|
||||
List<DfaMemoryState> states = visitor.getEndOfInitializerStates();
|
||||
boolean physical = aClass.isPhysical();
|
||||
for (PsiMethod method : aClass.getConstructors()) {
|
||||
if (physical && !method.isPhysical()) {
|
||||
// Constructor could be provided by, e.g. Lombok plugin: ignore it, we won't report any problems inside anyway
|
||||
continue;
|
||||
}
|
||||
List<DfaMemoryState> initialStates;
|
||||
PsiMethodCallExpression call = JavaPsiConstructorUtil.findThisOrSuperCallInConstructor(method);
|
||||
if (JavaPsiConstructorUtil.isChainedConstructorCall(call) || (call == null && DfaUtil.hasImplicitImpureSuperCall(aClass, method))) {
|
||||
|
||||
+6
-1
@@ -4,6 +4,8 @@ package com.intellij.codeInspection.dataFlow;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.codeInspection.util.OptionalUtil;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
@@ -183,7 +185,10 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
@Nullable TextRange range,
|
||||
@NotNull DfaMemoryState memState) {
|
||||
if (!expression.isPhysical()) {
|
||||
LOG.error("Non-physical expression is passed" + expression);
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application.isEAP() || application.isInternal() || application.isUnitTestMode()) {
|
||||
throw new IllegalStateException("Non-physical expression is passed");
|
||||
}
|
||||
}
|
||||
expression.accept(new ExpressionVisitor(value, memState));
|
||||
if (range == null) {
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package xxx;
|
||||
|
||||
public class InnerClasses {
|
||||
static class PackagePrivateInnerClass {
|
||||
}
|
||||
|
||||
static class PackagePrivateInnerClassWithConstructor {
|
||||
PackagePrivateInnerClassWithConstructor() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassWithPackagePrivateConstructor {
|
||||
ClassWithPackagePrivateConstructor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package xxx;
|
||||
|
||||
class PackagePrivateClass {
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package xxx;
|
||||
|
||||
public class ProtectedConstructors {
|
||||
protected ProtectedConstructors() {
|
||||
}
|
||||
|
||||
protected ProtectedConstructors(int i) {
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package xxx;
|
||||
|
||||
public class ProtectedMembers {
|
||||
protected void method() {
|
||||
}
|
||||
|
||||
static protected void staticMethod() {
|
||||
}
|
||||
|
||||
protected static class StaticInner {
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package xxx;
|
||||
|
||||
public class PublicClass {
|
||||
public String publicField;
|
||||
String packagePrivateField;
|
||||
static public String PUBLIC_STATIC_FIELD;
|
||||
static String PACKAGE_PRIVATE_STATIC_FIELD;
|
||||
|
||||
public void publicMethod() {}
|
||||
void packagePrivateMethod() {}
|
||||
|
||||
PublicClass() {}
|
||||
public PublicClass(int i) {}
|
||||
PublicClass(boolean b) {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package xxx;
|
||||
|
||||
public class PublicClassWithDefaultConstructor {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package xxx;
|
||||
|
||||
public class StaticMembers {
|
||||
static String IMPORTED_FIELD = "";
|
||||
static void importedMethod() {
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package xxx
|
||||
|
||||
import xxx.StaticMembers.*
|
||||
|
||||
/**
|
||||
* @see PackagePrivateClass
|
||||
* @see PublicClass.packagePrivateField
|
||||
*/
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
class AccessingPackagePrivateMembers {
|
||||
private val property = <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass()</warning>
|
||||
|
||||
fun main() {
|
||||
<warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass()</warning>
|
||||
var variable: <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning>
|
||||
|
||||
val aClass: PublicClass = PublicClass(1);
|
||||
val aClass2: PublicClassWithDefaultConstructor = PublicClassWithDefaultConstructor();
|
||||
<warning descr="Constructor PublicClass.PublicClass() is package-private, but declared in a different module 'dep'">PublicClass()</warning>
|
||||
<warning descr="Constructor PublicClass.PublicClass(boolean) is package-private, but declared in a different module 'dep'">PublicClass(true)</warning>
|
||||
|
||||
System.out.println(aClass.publicField)
|
||||
System.out.println(aClass.<warning descr="Field PublicClass.packagePrivateField is package-private, but declared in a different module 'dep'">packagePrivateField</warning>)
|
||||
System.out.println(PublicClass.PUBLIC_STATIC_FIELD)
|
||||
System.out.println(PublicClass.<warning descr="Field PublicClass.PACKAGE_PRIVATE_STATIC_FIELD is package-private, but declared in a different module 'dep'">PACKAGE_PRIVATE_STATIC_FIELD</warning>)
|
||||
|
||||
aClass.publicMethod()
|
||||
aClass.<warning descr="Method PublicClass.packagePrivateMethod() is package-private, but declared in a different module 'dep'">packagePrivateMethod()</warning>
|
||||
|
||||
System.out.println(<warning descr="Field StaticMembers.IMPORTED_FIELD is package-private, but declared in a different module 'dep'">IMPORTED_FIELD</warning>)
|
||||
<warning descr="Method StaticMembers.importedMethod() is package-private, but declared in a different module 'dep'">importedMethod</warning>()
|
||||
|
||||
InnerClasses.<warning descr="Class xxx.InnerClasses.PackagePrivateInnerClass is package-private, but declared in a different module 'dep'">PackagePrivateInnerClass()</warning>
|
||||
InnerClasses.<warning descr="Constructor PackagePrivateInnerClassWithConstructor.PackagePrivateInnerClassWithConstructor() is package-private, but declared in a different module 'dep'">PackagePrivateInnerClassWithConstructor()</warning>
|
||||
InnerClasses.<warning descr="Constructor ClassWithPackagePrivateConstructor.ClassWithPackagePrivateConstructor() is package-private, but declared in a different module 'dep'">ClassWithPackagePrivateConstructor()</warning>
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val staticProperty = <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass()</warning>
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package xxx
|
||||
|
||||
class AccessingProtectedMembersNotFromSubclass {
|
||||
fun foo() {
|
||||
val aClass: ProtectedMembers = ProtectedMembers()
|
||||
aClass.<warning descr="Method ProtectedMembers.method() is protected and used not through a subclass here, but declared in a different module 'dep'">method()</warning>
|
||||
ProtectedMembers.<warning descr="Method ProtectedMembers.staticMethod() is protected and used not through a subclass here, but declared in a different module 'dep'">staticMethod()</warning>
|
||||
<warning descr="Constructor ProtectedConstructors.ProtectedConstructors() is protected and used not through a subclass here, but declared in a different module 'dep'">ProtectedConstructors()</warning>
|
||||
<warning descr="Constructor ProtectedConstructors.ProtectedConstructors(int) is protected and used not through a subclass here, but declared in a different module 'dep'">ProtectedConstructors(1)</warning>
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
class AccessingProtectedMembersFromSubclass : ProtectedMembers() {
|
||||
fun foo() {
|
||||
method()
|
||||
staticMethod()
|
||||
ProtectedMembers.staticMethod()
|
||||
|
||||
val aClass = ProtectedMembers()
|
||||
aClass.<warning descr="Method ProtectedMembers.method() is protected and used not through a subclass here, but declared in a different module 'dep'">method()</warning>
|
||||
val myInstance = AccessingProtectedMembersFromSubclass()
|
||||
myInstance.method()
|
||||
|
||||
var inner1: ProtectedMembers.StaticInner
|
||||
var inner2: StaticInner
|
||||
}
|
||||
|
||||
private class StaticInnerImpl1 : ProtectedMembers.StaticInner()
|
||||
|
||||
private class StaticInnerImpl2 : StaticInner()
|
||||
}
|
||||
|
||||
class AccessingDefaultProtectedConstructorFromSubclass : ProtectedConstructors()
|
||||
|
||||
class AccessingProtectedConstructorFromSubclass : ProtectedConstructors(1)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.intellij.codeInspection.tests.kotlin
|
||||
|
||||
import com.intellij.jvm.analysis.JvmAnalysisKtTestsUtil
|
||||
import com.intellij.testFramework.TestDataPath
|
||||
import com.siyeh.ig.dependency.SuspiciousPackagePrivateAccessInspectionTestCase
|
||||
|
||||
@TestDataPath("/testData/codeInspection/suspiciousPackagePrivateAccess")
|
||||
class KtSuspiciousPackagePrivateAccessInspectionTest : SuspiciousPackagePrivateAccessInspectionTestCase("kt") {
|
||||
fun testAccessingPackagePrivateMembers() {
|
||||
doTestWithDependency()
|
||||
}
|
||||
|
||||
fun testAccessingProtectedMembers() {
|
||||
doTestWithDependency()
|
||||
}
|
||||
|
||||
override fun getBasePath() = "${JvmAnalysisKtTestsUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH}/codeInspection/suspiciousPackagePrivateAccess"
|
||||
}
|
||||
+11
-19
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.daemon;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
@@ -22,12 +7,19 @@ import com.intellij.openapi.util.TextRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface QuickFixActionRegistrar {
|
||||
|
||||
void register(@NotNull IntentionAction action);
|
||||
|
||||
void register(@NotNull TextRange fixRange, @NotNull IntentionAction action, HighlightDisplayKey key);
|
||||
|
||||
/**
|
||||
* Allows to replace some of the built-in quickfixes.
|
||||
* @param condition condition for quickfixes to remove
|
||||
* Allows to replace some of the built-in quick fixes.
|
||||
*
|
||||
* @param condition condition for quick fixes to remove
|
||||
* @deprecated if some fix may be inapplicable under certain circumstances
|
||||
* it should be fixed to provide its own EP, so it's possible to plug into the fix directly
|
||||
* instead of filtering it with this method
|
||||
*/
|
||||
void unregister(@NotNull Condition<IntentionAction> condition);
|
||||
@Deprecated
|
||||
default void unregister(@NotNull Condition<IntentionAction> condition) {}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.components.StoragePathMacros
|
||||
import com.intellij.openapi.components.stateStore
|
||||
import com.intellij.testFramework.ApplicationRule
|
||||
import com.intellij.testFramework.rules.InMemoryFsRule
|
||||
import com.intellij.util.io.createDirectories
|
||||
import com.intellij.util.io.directoryStreamIfExists
|
||||
import com.intellij.util.io.exists
|
||||
import com.intellij.util.io.write
|
||||
@@ -68,7 +69,11 @@ class ConfigImportHelperTest {
|
||||
writeStorageFile("2020.1", 100, isMacOs)
|
||||
writeStorageFile("2021.1", 200, isMacOs)
|
||||
writeStorageFile("2022.1", 300, isMacOs)
|
||||
val newConfigPath = fs.getPath("/data/${constructConfigPath("2022.1", isMacOs)}")
|
||||
|
||||
val newConfigPath = fs.getPath("/data/${constructConfigPath("2022.3", isMacOs)}")
|
||||
// create new config dir to test that it will be not suggested too (as on start of new version config dir can be created)
|
||||
newConfigPath.createDirectories()
|
||||
|
||||
assertThat(ConfigImportHelper.findRecentConfigDirectory(newConfigPath, isMacOs).joinToString("\n")).isEqualTo("""
|
||||
/data/${constructConfigPath("2022.1", isMacOs)}
|
||||
/data/${constructConfigPath("2021.1", isMacOs)}
|
||||
@@ -83,12 +88,35 @@ class ConfigImportHelperTest {
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private fun writeStorageFile(version: String, lastModified: Long, isMacOs: Boolean) {
|
||||
val path = fsRule.fs.getPath("/data/" + (constructConfigPath(version, isMacOs)),
|
||||
PathManager.OPTIONS_DIRECTORY + '/' + StoragePathMacros.NOT_ROAMABLE_FILE)
|
||||
Files.setLastModifiedTime(path.write(version), FileTime.fromMillis(lastModified))
|
||||
@Test
|
||||
fun `sort if no anchor files`() {
|
||||
val isMacOs = true
|
||||
fun writeStorageDir(version: String) {
|
||||
val dir = fsRule.fs.getPath("/data/" + (constructConfigPath(version, isMacOs)))
|
||||
dir.createDirectories()
|
||||
}
|
||||
|
||||
val fs = fsRule.fs
|
||||
writeStorageDir("2022.1")
|
||||
writeStorageDir("2021.1")
|
||||
writeStorageDir("2020.1")
|
||||
|
||||
val newConfigPath = fs.getPath("/data/${constructConfigPath("2022.3", isMacOs)}")
|
||||
// create new config dir to test that it will be not suggested too (as on start of new version config dir can be created)
|
||||
newConfigPath.createDirectories()
|
||||
|
||||
assertThat(ConfigImportHelper.findRecentConfigDirectory(newConfigPath, isMacOs).joinToString("\n")).isEqualTo("""
|
||||
/data/${constructConfigPath("2022.1", isMacOs)}
|
||||
/data/${constructConfigPath("2021.1", isMacOs)}
|
||||
/data/${constructConfigPath("2020.1", isMacOs)}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private fun writeStorageFile(version: String, lastModified: Long, isMacOs: Boolean) {
|
||||
val dir = fsRule.fs.getPath("/data/" + (constructConfigPath(version, isMacOs)))
|
||||
val file = dir.resolve(PathManager.OPTIONS_DIRECTORY + '/' + StoragePathMacros.NOT_ROAMABLE_FILE)
|
||||
Files.setLastModifiedTime(file.write(version), FileTime.fromMillis(lastModified))
|
||||
}
|
||||
}
|
||||
|
||||
private fun constructConfigPath(version: String, isMacOs: Boolean): String {
|
||||
|
||||
@@ -5,13 +5,13 @@ package com.intellij.execution;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.executors.DefaultRunExecutor;
|
||||
import com.intellij.execution.impl.ExecutionManagerImpl;
|
||||
import com.intellij.execution.process.OSProcessHandler;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.execution.ui.RunContentManager;
|
||||
import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
@@ -348,15 +348,6 @@ public class ExecutionHelper {
|
||||
if (indicator != null && title2 != null) {
|
||||
indicator.setText2(title2);
|
||||
}
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (application.isInternal() && !application.isHeadlessEnvironment()) {
|
||||
if (application.isDispatchThread()) {
|
||||
LOG.warn("Synchronous execution on EDT: " + processHandler, new Throwable());
|
||||
}
|
||||
else if (application.isReadAccessAllowed()) {
|
||||
LOG.warn("Synchronous execution under ReadAction: " + processHandler, new Throwable());
|
||||
}
|
||||
}
|
||||
process.run();
|
||||
}
|
||||
}
|
||||
@@ -419,7 +410,7 @@ public class ExecutionHelper {
|
||||
mySemaphore.down();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(myWaitThread);
|
||||
ApplicationManager.getApplication().executeOnPooledThread(myCancelListener);
|
||||
|
||||
OSProcessHandler.checkEdtAndReadAction(processHandler);
|
||||
mySemaphore.waitFor();
|
||||
}
|
||||
};
|
||||
@@ -448,7 +439,7 @@ public class ExecutionHelper {
|
||||
public void run() {
|
||||
mySemaphore.down();
|
||||
ApplicationManager.getApplication().executeOnPooledThread(myProcessThread);
|
||||
|
||||
OSProcessHandler.checkEdtAndReadAction(processHandler);
|
||||
mySemaphore.waitFor();
|
||||
}
|
||||
};
|
||||
|
||||
+19
-30
@@ -1057,6 +1057,7 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
|
||||
if (resultsExpired) {
|
||||
retainContributors(itemsMap.keySet());
|
||||
clearMoreItems();
|
||||
|
||||
itemsMap.forEach((contributor, list) -> {
|
||||
Object[] oldItems = ArrayUtil.toObjectArray(getFoundItems(contributor));
|
||||
@@ -1076,14 +1077,16 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
else {
|
||||
itemsMap.forEach((contributor, list) -> {
|
||||
int startIndex = contributors().indexOf(contributor);
|
||||
int insertionIndex = getInsertionPoint(contributor);
|
||||
int endIndex = insertionIndex + list.size() - 1;
|
||||
listElements.addAll(insertionIndex, list);
|
||||
fireIntervalAdded(this, insertionIndex, endIndex);
|
||||
|
||||
// there were items for this contributor before update
|
||||
if (startIndex >= 0) {
|
||||
addElementsWithPriority(contributor, startIndex, list);
|
||||
}
|
||||
else {
|
||||
startIndex = getInsertionPoint(contributor);
|
||||
int endIndex = startIndex + list.size() - 1;
|
||||
listElements.addAll(startIndex, list);
|
||||
fireIntervalAdded(this, startIndex, endIndex);
|
||||
listElements.subList(startIndex, endIndex + 1)
|
||||
.sort(Comparator.comparingInt(SESearcher.ElementInfo::getPriority).reversed());
|
||||
fireContentsChanged(this, startIndex, endIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1115,22 +1118,13 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
}
|
||||
}
|
||||
|
||||
private void addElementsWithPriority(SearchEverywhereContributor<?> contributor, int index, List<SESearcher.ElementInfo> newElements) {
|
||||
for (SESearcher.ElementInfo newElementInfo : newElements) {
|
||||
if (index < listElements.size()) {
|
||||
SESearcher.ElementInfo existingElementInfo = listElements.get(index);
|
||||
while (existingElementInfo.getContributor() == contributor
|
||||
&& existingElementInfo.getPriority() >= newElementInfo.getPriority()
|
||||
&& existingElementInfo.getElement() != MORE_ELEMENT) {
|
||||
index++;
|
||||
if (index >= listElements.size()) break;
|
||||
existingElementInfo = listElements.get(index);
|
||||
}
|
||||
listElements.add(index, newElementInfo);
|
||||
index++;
|
||||
}
|
||||
else {
|
||||
listElements.add(newElementInfo);
|
||||
private void clearMoreItems() {
|
||||
ListIterator<SESearcher.ElementInfo> iterator = listElements.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
int index = iterator.nextIndex();
|
||||
if (iterator.next().getElement() == MORE_ELEMENT) {
|
||||
iterator.remove();
|
||||
fireContentsChanged(this, index, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1266,13 +1260,8 @@ public class SearchEverywhereUI extends BigPopupUI implements DataProvider, Quic
|
||||
return isMoreElement(index) ? index : index + 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i).getSortWeight() > contributor.getSortWeight()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return listElements.size();
|
||||
index = Collections.binarySearch(list, contributor, Comparator.comparingInt(SearchEverywhereContributor::getSortWeight));
|
||||
return -index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-2
@@ -26,8 +26,8 @@ public class SearchModelTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
|
||||
// adding to empty -----------------------------------------------------------------------
|
||||
model.addElements(Arrays.asList(
|
||||
new SESearcher.ElementInfo("item_3_20", 340, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_2_20", 250, STUB_CONTRIBUTOR_2),
|
||||
new SESearcher.ElementInfo("item_3_20", 340, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_1_20", 160, STUB_CONTRIBUTOR_1),
|
||||
new SESearcher.ElementInfo("item_3_30", 330, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_2_10", 280, STUB_CONTRIBUTOR_2),
|
||||
@@ -72,8 +72,39 @@ public class SearchModelTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
Assert.assertEquals(expectedItems, actualItems);
|
||||
|
||||
// expiring results -----------------------------------------------------------------------
|
||||
// removing items -----------------------------------------------------------------------
|
||||
model.expireResults();
|
||||
model.addElements(Arrays.asList(
|
||||
new SESearcher.ElementInfo("item_3_50", 310, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_1_20", 160, STUB_CONTRIBUTOR_1),
|
||||
new SESearcher.ElementInfo("item_3_10", 350, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_2_23", 250, STUB_CONTRIBUTOR_2),
|
||||
new SESearcher.ElementInfo("item_3_30", 330, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_2_05", 290, STUB_CONTRIBUTOR_2),
|
||||
new SESearcher.ElementInfo("item_2_10", 280, STUB_CONTRIBUTOR_2),
|
||||
new SESearcher.ElementInfo("item_1_35", 130, STUB_CONTRIBUTOR_1),
|
||||
new SESearcher.ElementInfo("item_3_20", 340, STUB_CONTRIBUTOR_3),
|
||||
new SESearcher.ElementInfo("item_1_25", 150, STUB_CONTRIBUTOR_1)
|
||||
));
|
||||
model.setHasMore(STUB_CONTRIBUTOR_1, true);
|
||||
model.setHasMore(STUB_CONTRIBUTOR_2, true);
|
||||
|
||||
actualItems = model.getItems();
|
||||
expectedItems = Arrays.asList("item_1_20", "item_1_25", "item_1_35", SearchListModel.MORE_ELEMENT,
|
||||
"item_2_05", "item_2_10", "item_2_23", SearchListModel.MORE_ELEMENT,
|
||||
"item_3_10", "item_3_20", "item_3_30", "item_3_50");
|
||||
Assert.assertEquals(expectedItems, actualItems);
|
||||
|
||||
// removing items -----------------------------------------------------------------------
|
||||
model.removeElement("item_1_25", STUB_CONTRIBUTOR_1);
|
||||
model.removeElement("item_3_20", STUB_CONTRIBUTOR_3);
|
||||
model.removeElement("item_3_30", STUB_CONTRIBUTOR_3);
|
||||
model.setHasMore(STUB_CONTRIBUTOR_1, false);
|
||||
|
||||
actualItems = model.getItems();
|
||||
expectedItems = Arrays.asList("item_1_20", "item_1_35",
|
||||
"item_2_05", "item_2_10", "item_2_23", SearchListModel.MORE_ELEMENT,
|
||||
"item_3_10", "item_3_50");
|
||||
Assert.assertEquals(expectedItems, actualItems);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -15,11 +15,14 @@ package com.intellij.execution.process;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.encoding.EncodingManager;
|
||||
import com.intellij.util.ExceptionUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.BaseOutputReader;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -32,6 +35,8 @@ import java.util.concurrent.Future;
|
||||
|
||||
public class OSProcessHandler extends BaseOSProcessHandler {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.OSProcessHandler");
|
||||
private static final Set<String> REPORTED_EXECUTIONS = ContainerUtil.newConcurrentSet();
|
||||
private static final long ALLOWED_TIMEOUT_THRESHHOLD = 10;
|
||||
|
||||
public static final Key<Set<File>> DELETE_FILES_ON_TERMINATION = Key.create("OSProcessHandler.FileToDelete");
|
||||
|
||||
@@ -56,6 +61,42 @@ public class OSProcessHandler extends BaseOSProcessHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean waitFor() {
|
||||
checkEdtAndReadAction(this);
|
||||
return super.waitFor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean waitFor(long timeoutInMilliseconds) {
|
||||
if (timeoutInMilliseconds > ALLOWED_TIMEOUT_THRESHHOLD) {
|
||||
checkEdtAndReadAction(this);
|
||||
}
|
||||
return super.waitFor(timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we are going to wait for {@code processHandler} to finish on EDT or under ReadAction. Logs error if we do so.
|
||||
*
|
||||
* @apiNote works only in internal mode with UI. Reports once per running session per stacktrace per cause.
|
||||
*/
|
||||
public static void checkEdtAndReadAction(@NotNull ProcessHandler processHandler) {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
if (!application.isInternal() || application.isHeadlessEnvironment()) {
|
||||
return;
|
||||
}
|
||||
String message = null;
|
||||
if (application.isDispatchThread()) {
|
||||
message = "Synchronous execution on EDT: ";
|
||||
}
|
||||
else if (application.isReadAccessAllowed()) {
|
||||
message = "Synchronous execution under ReadAction: ";
|
||||
}
|
||||
if (message != null && REPORTED_EXECUTIONS.add(ExceptionUtil.currentStackTrace())) {
|
||||
LOG.error(message + processHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteTempFiles(Set<File> tempFiles) {
|
||||
if (tempFiles != null) {
|
||||
try {
|
||||
|
||||
@@ -1315,7 +1315,7 @@ public class JBTabsImpl extends JComponent
|
||||
|
||||
@NotNull
|
||||
public List<TabInfo> getTabs() {
|
||||
//ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
if (myAllTabs != null) return myAllTabs;
|
||||
|
||||
ArrayList<TabInfo> result = new ArrayList<>(myVisibleInfos);
|
||||
|
||||
@@ -554,39 +554,38 @@ public class SimpleTree extends Tree implements CellEditorListener {
|
||||
|
||||
myExpandedHandle = null;
|
||||
myCollapsedHandle = null;
|
||||
myExpandedHandle = null;
|
||||
myEmptyHandle = null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Icon getHandleIcon(DefaultMutableTreeNode node, TreePath path) {
|
||||
if (node.getChildCount() == 0) return getEmptyHandle();
|
||||
|
||||
|
||||
return isExpanded(path) ? getExpandedHandle() : getCollapsedHandle();
|
||||
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Icon getExpandedHandle() {
|
||||
if (myExpandedHandle == null) {
|
||||
myExpandedHandle = UIUtil.getTreeExpandedIcon();
|
||||
}
|
||||
|
||||
return myExpandedHandle;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Icon getCollapsedHandle() {
|
||||
if (myCollapsedHandle == null) {
|
||||
myCollapsedHandle = UIUtil.getTreeCollapsedIcon();
|
||||
}
|
||||
|
||||
return myCollapsedHandle;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Icon getEmptyHandle() {
|
||||
if (myEmptyHandle == null) {
|
||||
final Icon expand = getExpandedHandle();
|
||||
myEmptyHandle = expand != null ? EmptyIcon.create(expand) : EmptyIcon.create(0);
|
||||
}
|
||||
|
||||
return myEmptyHandle;
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -159,7 +159,10 @@ public class ConfigImportHelper {
|
||||
}
|
||||
|
||||
final List<Path> candidates;
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(configsHome, it -> StringUtil.startsWithIgnoreCase(it.getFileName().toString(), prefix))) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(configsHome, it -> {
|
||||
//noinspection CodeBlock2Expr
|
||||
return StringUtil.startsWithIgnoreCase(it.getFileName().toString(), prefix) && !it.equals(isMacOs ? newConfigDir : newConfigDir.getParent());
|
||||
})) {
|
||||
candidates = ContainerUtilRt.newArrayList(stream);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
@@ -194,7 +197,13 @@ public class ConfigImportHelper {
|
||||
for (Object key : fileToLastModified.keys()) {
|
||||
result.add((Path)key);
|
||||
}
|
||||
result.sort((o1, o2) -> (int)(fileToLastModified.get(o2) - fileToLastModified.get(o1)));
|
||||
result.sort((o1, o2) -> {
|
||||
int diff = (int)(fileToLastModified.get(o2) - fileToLastModified.get(o1));
|
||||
if (diff == 0) {
|
||||
return StringUtil.naturalCompare(o2.toString(), o1.toString());
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1041,6 +1041,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
|
||||
|
||||
@Override
|
||||
public void setSelectedEditor(@NotNull VirtualFile file, @NotNull String fileEditorProviderId) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
|
||||
if (composite == null) {
|
||||
final List<EditorWithProviderComposite> composites = getEditorComposites(file);
|
||||
@@ -1310,6 +1311,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
|
||||
@Override
|
||||
@Nullable
|
||||
public FileEditorWithProvider getSelectedEditorWithProvider(@NotNull VirtualFile file) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
if (file instanceof VirtualFileWindow) file = ((VirtualFileWindow)file).getDelegate();
|
||||
final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
|
||||
if (composite != null) {
|
||||
@@ -1323,8 +1325,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
|
||||
@Override
|
||||
@NotNull
|
||||
public Pair<FileEditor[], FileEditorProvider[]> getEditorsWithProviders(@NotNull final VirtualFile file) {
|
||||
assertReadAccess();
|
||||
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
final EditorWithProviderComposite composite = getCurrentEditorWithProviderComposite(file);
|
||||
if (composite != null) {
|
||||
return Pair.create(composite.getEditors(), composite.getProviders());
|
||||
|
||||
+48
-10
@@ -19,6 +19,8 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.components.GradientViewport;
|
||||
import com.intellij.ui.tree.ui.Control;
|
||||
import com.intellij.ui.tree.ui.DefaultControl;
|
||||
import com.intellij.ui.treeStructure.*;
|
||||
import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
|
||||
import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure;
|
||||
@@ -74,6 +76,8 @@ public class SettingsTreeView extends JComponent implements Accessible, Disposab
|
||||
private Configurable myQueuedConfigurable;
|
||||
private boolean myPaintInternalInfo;
|
||||
|
||||
private MyControl myControl;
|
||||
|
||||
public SettingsTreeView(SettingsFilter filter, ConfigurableGroup[] groups) {
|
||||
myFilter = filter;
|
||||
myRoot = new MyRoot(groups);
|
||||
@@ -123,7 +127,7 @@ public class SettingsTreeView extends JComponent implements Accessible, Disposab
|
||||
myHeader.setBorder(BorderFactory.createEmptyBorder(1, 10 + getLeftMargin(0), 0, 0));
|
||||
}
|
||||
myHeader.setFont(myTree.getFont());
|
||||
myHeader.setIcon(myTree.getEmptyHandle());
|
||||
myHeader.setIcon(getIcon(null));
|
||||
int height = myHeader.getPreferredSize().height;
|
||||
String group = findGroupNameAt(0, height + 3);
|
||||
if (group == null || !group.equals(findGroupNameAt(0, 0))) {
|
||||
@@ -181,6 +185,48 @@ public class SettingsTreeView extends JComponent implements Accessible, Disposab
|
||||
Disposer.register(this, myBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI() {
|
||||
super.updateUI();
|
||||
myControl = null;
|
||||
}
|
||||
|
||||
private Icon getIcon(@Nullable DefaultMutableTreeNode node) {
|
||||
if (myControl == null) myControl = new MyControl();
|
||||
if (node == null || 0 == node.getChildCount()) return myControl.empty;
|
||||
return myTree.isExpanded(new TreePath(node.getPath())) ? myControl.expanded : myControl.collapsed;
|
||||
}
|
||||
|
||||
private static final class MyControl {
|
||||
private final Control control = new DefaultControl();
|
||||
private final Icon collapsed = new MyIcon(false);
|
||||
private final Icon expanded = new MyIcon(true);
|
||||
private final Icon empty = new MyIcon(null);
|
||||
|
||||
private final class MyIcon implements Icon {
|
||||
private final Boolean expanded;
|
||||
|
||||
private MyIcon(@Nullable Boolean expanded) {
|
||||
this.expanded = expanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return control.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return control.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
if (expanded != null) control.paint(g, x, y, getIconWidth(), getIconHeight(), expanded, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setComponentPopupMenuTo(JTree tree) {
|
||||
tree.setComponentPopupMenu(new JPopupMenu() {
|
||||
private Transferable transferable;
|
||||
@@ -584,15 +630,7 @@ public class SettingsTreeView extends JComponent implements Accessible, Disposab
|
||||
// configure node icon
|
||||
Icon nodeIcon = null;
|
||||
if (value instanceof DefaultMutableTreeNode) {
|
||||
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
|
||||
if (0 == treeNode.getChildCount()) {
|
||||
nodeIcon = myTree.getEmptyHandle();
|
||||
}
|
||||
else {
|
||||
nodeIcon = myTree.isExpanded(new TreePath(treeNode.getPath()))
|
||||
? myTree.getExpandedHandle()
|
||||
: myTree.getCollapsedHandle();
|
||||
}
|
||||
nodeIcon = getIcon((DefaultMutableTreeNode)value);
|
||||
}
|
||||
myNodeIcon.setIcon(nodeIcon);
|
||||
if (node != null && myPaintInternalInfo) {
|
||||
|
||||
+118
-89
@@ -6,6 +6,7 @@ import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.io.FileAttributes;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VFileProperty;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -31,21 +32,21 @@ import java.nio.file.attribute.DosFileAttributes;
|
||||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static com.intellij.openapi.vfs.newvfs.persistent.VfsEventGenerationHelper.LOG;
|
||||
|
||||
class LocalFileSystemRefreshWorker {
|
||||
private final boolean myIsRecursive;
|
||||
private final Queue<NewVirtualFile> myRefreshQueue = new Queue<>(100);
|
||||
private final NewVirtualFile myRefreshRoot;
|
||||
private final VfsEventGenerationHelper myHelper = new VfsEventGenerationHelper();
|
||||
private volatile boolean myCancelled;
|
||||
|
||||
LocalFileSystemRefreshWorker(@NotNull NewVirtualFile refreshRoot, boolean isRecursive) {
|
||||
myIsRecursive = isRecursive;
|
||||
myRefreshQueue.addLast(refreshRoot);
|
||||
myRefreshRoot = refreshRoot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -58,7 +59,7 @@ class LocalFileSystemRefreshWorker {
|
||||
}
|
||||
|
||||
public void scan() {
|
||||
NewVirtualFile root = myRefreshQueue.pullFirst();
|
||||
NewVirtualFile root = myRefreshRoot;
|
||||
boolean rootDirty = root.isDirty();
|
||||
if (LOG.isDebugEnabled()) LOG.debug("root=" + root + " dirty=" + rootDirty);
|
||||
if (!rootDirty) return;
|
||||
@@ -74,72 +75,118 @@ class LocalFileSystemRefreshWorker {
|
||||
fs = PersistentFS.replaceWithNativeFS(fs);
|
||||
}
|
||||
|
||||
myRefreshQueue.addLast(root);
|
||||
|
||||
try {
|
||||
processQueue(fs, PersistentFS.getInstance());
|
||||
}
|
||||
catch (RefreshCancelledException e) {
|
||||
LOG.debug("refresh cancelled");
|
||||
}
|
||||
RefreshContext context = createRefreshContext(fs, PersistentFS.getInstance(), FilePathHashingStrategy.create(fs.isCaseSensitive()));
|
||||
context.submitRefreshRequest(() -> processFile(root, context));
|
||||
context.waitForRefreshToFinish();
|
||||
}
|
||||
|
||||
private void processQueue(@NotNull NewVirtualFileSystem fs, @NotNull PersistentFS persistence) throws RefreshCancelledException {
|
||||
TObjectHashingStrategy<String> strategy = FilePathHashingStrategy.create(fs.isCaseSensitive());
|
||||
private RefreshContext createRefreshContext(NewVirtualFileSystem fs, PersistentFS persistentFS, TObjectHashingStrategy<String> strategy) {
|
||||
int parallelism = Registry.intValue("vfs.use.nio-based.local.refresh.worker.parallelism", Runtime.getRuntime().availableProcessors() - 1);
|
||||
|
||||
if (myIsRecursive && parallelism > 0) {
|
||||
final ForkJoinPool pool = new ForkJoinPool(parallelism);
|
||||
|
||||
while (!myRefreshQueue.isEmpty()) {
|
||||
NewVirtualFile file = myRefreshQueue.pullFirst();
|
||||
if (!myHelper.checkDirty(file)) continue;
|
||||
|
||||
checkCancelled(file);
|
||||
|
||||
if (file.isDirectory()) {
|
||||
boolean fullSync = ((VirtualDirectoryImpl)file).allChildrenLoaded();
|
||||
if (fullSync) {
|
||||
fullDirRefresh(fs, persistence, strategy, (VirtualDirectoryImpl)file);
|
||||
return new RefreshContext(fs, persistentFS, strategy) {
|
||||
@Override
|
||||
void submitRefreshRequest(Runnable action) {
|
||||
pool.submit(action);
|
||||
}
|
||||
else {
|
||||
partialDirRefresh(fs, persistence, strategy, (VirtualDirectoryImpl)file);
|
||||
|
||||
@Override
|
||||
void doWaitForRefreshToFinish() {
|
||||
pool.awaitQuiescence(1, TimeUnit.DAYS);
|
||||
pool.shutdown();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new RefreshContext(fs, persistentFS, strategy) {
|
||||
final Queue<Runnable> myRefreshRequests = new Queue<>(100);
|
||||
|
||||
@Override
|
||||
void submitRefreshRequest(Runnable request) {
|
||||
myRefreshRequests.addLast(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
void doWaitForRefreshToFinish() {
|
||||
while(!myRefreshRequests.isEmpty()) {
|
||||
Runnable request = myRefreshRequests.pullFirst();
|
||||
request.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void processFile(NewVirtualFile file, RefreshContext refreshContext) {
|
||||
if (!myHelper.checkDirty(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(checkCancelled(file, refreshContext)) return;
|
||||
|
||||
if (file.isDirectory()) {
|
||||
boolean fullSync = ((VirtualDirectoryImpl)file).allChildrenLoaded();
|
||||
if (fullSync) {
|
||||
fullDirRefresh((VirtualDirectoryImpl)file, refreshContext);
|
||||
}
|
||||
else {
|
||||
refreshFile(fs, persistence, strategy, file);
|
||||
partialDirRefresh((VirtualDirectoryImpl)file, refreshContext);
|
||||
}
|
||||
}
|
||||
else {
|
||||
refreshFile(file, refreshContext);
|
||||
}
|
||||
|
||||
if (myIsRecursive || !file.isDirectory()) {
|
||||
file.markClean();
|
||||
if(checkCancelled(file, refreshContext)) return;
|
||||
|
||||
if (myIsRecursive || !file.isDirectory()) {
|
||||
file.markClean();
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class RefreshContext {
|
||||
final NewVirtualFileSystem fs;
|
||||
final PersistentFS persistence;
|
||||
final TObjectHashingStrategy<String> strategy;
|
||||
final LinkedBlockingQueue<NewVirtualFile> filesToBecomeDirty = new LinkedBlockingQueue<>();
|
||||
|
||||
RefreshContext(NewVirtualFileSystem fs, PersistentFS persistence, TObjectHashingStrategy<String> strategy) {
|
||||
this.fs = fs;
|
||||
this.persistence = persistence;
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
abstract void submitRefreshRequest(Runnable action);
|
||||
abstract void doWaitForRefreshToFinish();
|
||||
|
||||
final void waitForRefreshToFinish() {
|
||||
doWaitForRefreshToFinish();
|
||||
|
||||
for (NewVirtualFile file : filesToBecomeDirty) {
|
||||
forceMarkDirty(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshFile(@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull PersistentFS persistence,
|
||||
@NotNull TObjectHashingStrategy<String> strategy,
|
||||
@NotNull NewVirtualFile file) {
|
||||
RefreshingFileVisitor refreshingFileVisitor = new RefreshingFileVisitor(file, persistence, fs,
|
||||
null,
|
||||
Collections.singletonList(file), strategy);
|
||||
private void refreshFile(@NotNull NewVirtualFile file, RefreshContext refreshContext) {
|
||||
RefreshingFileVisitor refreshingFileVisitor = new RefreshingFileVisitor(file, refreshContext, null,
|
||||
Collections.singletonList(file));
|
||||
|
||||
refreshingFileVisitor.visit(file);
|
||||
myHelper.addAllEventsFrom(refreshingFileVisitor.getHelper());
|
||||
}
|
||||
|
||||
private static final AtomicInteger myRequests = new AtomicInteger();
|
||||
private static final AtomicLong myTime = new AtomicLong();
|
||||
|
||||
private void fullDirRefresh(@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull PersistentFS persistence,
|
||||
@NotNull TObjectHashingStrategy<String> strategy,
|
||||
@NotNull VirtualDirectoryImpl dir) {
|
||||
private void fullDirRefresh(@NotNull VirtualDirectoryImpl dir, RefreshContext refreshContext) {
|
||||
while (true) {
|
||||
// obtaining directory snapshot
|
||||
Pair<String[], VirtualFile[]> result = getDirectorySnapshot(persistence, dir);
|
||||
Pair<String[], VirtualFile[]> result = getDirectorySnapshot(refreshContext.persistence, dir);
|
||||
if (result == null) return;
|
||||
String[] currentNames = result.getFirst();
|
||||
VirtualFile[] children = result.getSecond();
|
||||
|
||||
RefreshingFileVisitor refreshingFileVisitor =
|
||||
new RefreshingFileVisitor(dir, persistence, fs, null, Arrays.asList(children), strategy);
|
||||
new RefreshingFileVisitor(dir, refreshContext, null, Arrays.asList(children));
|
||||
|
||||
refreshingFileVisitor.visit(dir);
|
||||
|
||||
@@ -148,7 +195,7 @@ class LocalFileSystemRefreshWorker {
|
||||
if (ApplicationManager.getApplication().isDisposed()) {
|
||||
return true;
|
||||
}
|
||||
if (!Arrays.equals(currentNames, persistence.list(dir)) || !Arrays.equals(children, dir.getChildren())) {
|
||||
if (!Arrays.equals(currentNames, refreshContext.persistence.list(dir)) || !Arrays.equals(children, dir.getChildren())) {
|
||||
if (LOG.isDebugEnabled()) LOG.debug("retry: " + dir);
|
||||
return false;
|
||||
}
|
||||
@@ -171,10 +218,7 @@ class LocalFileSystemRefreshWorker {
|
||||
});
|
||||
}
|
||||
|
||||
private void partialDirRefresh(@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull PersistentFS persistence,
|
||||
@NotNull TObjectHashingStrategy<String> strategy,
|
||||
@NotNull VirtualDirectoryImpl dir) {
|
||||
private void partialDirRefresh(@NotNull VirtualDirectoryImpl dir, RefreshContext refreshContext) {
|
||||
while (true) {
|
||||
// obtaining directory snapshot
|
||||
Pair<List<VirtualFile>, List<String>> result =
|
||||
@@ -184,7 +228,7 @@ class LocalFileSystemRefreshWorker {
|
||||
List<String> wanted = result.getSecond();
|
||||
|
||||
if (cached.isEmpty() && wanted.isEmpty()) return;
|
||||
RefreshingFileVisitor refreshingFileVisitor = new RefreshingFileVisitor(dir, persistence, fs, wanted, cached, strategy);
|
||||
RefreshingFileVisitor refreshingFileVisitor = new RefreshingFileVisitor(dir, refreshContext, wanted, cached);
|
||||
refreshingFileVisitor.visit(dir);
|
||||
|
||||
// generating events unless a directory was changed in between
|
||||
@@ -204,18 +248,14 @@ class LocalFileSystemRefreshWorker {
|
||||
}
|
||||
}
|
||||
|
||||
private static class RefreshCancelledException extends RuntimeException {
|
||||
}
|
||||
|
||||
private void checkCancelled(@NotNull NewVirtualFile stopAt) {
|
||||
if (myCancelled || ourCancellingCondition != null && ourCancellingCondition.fun(stopAt)) {
|
||||
forceMarkDirty(stopAt);
|
||||
while (!myRefreshQueue.isEmpty()) {
|
||||
NewVirtualFile next = myRefreshQueue.pullFirst();
|
||||
forceMarkDirty(next);
|
||||
}
|
||||
throw new RefreshCancelledException();
|
||||
private boolean checkCancelled(@NotNull NewVirtualFile stopAt, RefreshContext refreshContext) {
|
||||
boolean myRequestedCancel = false;
|
||||
if (myCancelled || (myRequestedCancel = ourCancellingCondition != null && ourCancellingCondition.fun(stopAt))) {
|
||||
if (myRequestedCancel) myCancelled = true;
|
||||
refreshContext.filesToBecomeDirty.offer(stopAt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void forceMarkDirty(@NotNull NewVirtualFile file) {
|
||||
@@ -237,20 +277,16 @@ class LocalFileSystemRefreshWorker {
|
||||
private final Set<String> myChildrenWeAreInterested; // null - no limit
|
||||
|
||||
private final VirtualFile myFileOrDir;
|
||||
private final PersistentFS myPersistence;
|
||||
private final NewVirtualFileSystem myFs;
|
||||
private final RefreshContext myRefreshContext;
|
||||
|
||||
RefreshingFileVisitor(@NotNull VirtualFile fileOrDir,
|
||||
@NotNull PersistentFS persistence,
|
||||
@NotNull NewVirtualFileSystem fs,
|
||||
@NotNull RefreshContext refreshContext,
|
||||
@Nullable("null means all") Collection<String> childrenToRefresh,
|
||||
@NotNull Collection<VirtualFile> existingPersistentChildren,
|
||||
@NotNull TObjectHashingStrategy<String> strategy) {
|
||||
@NotNull Collection<VirtualFile> existingPersistentChildren) {
|
||||
myFileOrDir = fileOrDir;
|
||||
myPersistence = persistence;
|
||||
myFs = fs;
|
||||
myPersistentChildren = new THashMap<>(existingPersistentChildren.size(), strategy);
|
||||
myChildrenWeAreInterested = childrenToRefresh != null ? new THashSet<>(childrenToRefresh, strategy) : null;
|
||||
myRefreshContext = refreshContext;
|
||||
myPersistentChildren = new THashMap<>(existingPersistentChildren.size(), refreshContext.strategy);
|
||||
myChildrenWeAreInterested = childrenToRefresh != null ? new THashSet<>(childrenToRefresh, refreshContext.strategy) : null;
|
||||
|
||||
for (VirtualFile child : existingPersistentChildren) {
|
||||
String name = child.getName();
|
||||
@@ -275,7 +311,9 @@ class LocalFileSystemRefreshWorker {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
checkCancelled(child);
|
||||
if(checkCancelled(child, myRefreshContext)) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
if (!child.isDirty()) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
@@ -312,16 +350,16 @@ class LocalFileSystemRefreshWorker {
|
||||
}
|
||||
|
||||
if (!directory) {
|
||||
myHelper.checkContentChanged(child, myPersistence.getTimeStamp(child), attrs.lastModifiedTime().toMillis(),
|
||||
myPersistence.getLastRecordedLength(child), attrs.size());
|
||||
myHelper.checkContentChanged(child, myRefreshContext.persistence.getTimeStamp(child), attrs.lastModifiedTime().toMillis(),
|
||||
myRefreshContext.persistence.getLastRecordedLength(child), attrs.size());
|
||||
}
|
||||
else {
|
||||
if (myIsRecursive) {
|
||||
myRefreshQueue.addLast(child);
|
||||
myRefreshContext.submitRefreshRequest(() -> processFile(child, myRefreshContext));
|
||||
}
|
||||
}
|
||||
|
||||
boolean currentWritable = myPersistence.isWritable(child);
|
||||
boolean currentWritable = myRefreshContext.persistence.isWritable(child);
|
||||
boolean isWritable;
|
||||
|
||||
if (attrs instanceof DosFileAttributes) {
|
||||
@@ -342,7 +380,7 @@ class LocalFileSystemRefreshWorker {
|
||||
}
|
||||
|
||||
if (isLink) {
|
||||
myHelper.checkSymbolicLinkChange(child, child.getCanonicalPath(), myFs.resolveSymLink(child));
|
||||
myHelper.checkSymbolicLinkChange(child, child.getCanonicalPath(), myRefreshContext.fs.resolveSymLink(child));
|
||||
}
|
||||
if (!child.isDirectory()) child.markClean();
|
||||
}
|
||||
@@ -353,9 +391,7 @@ class LocalFileSystemRefreshWorker {
|
||||
return !VfsUtil.isBadName(name);
|
||||
}
|
||||
|
||||
public void visit(@NotNull VirtualFile fileOrDir) {
|
||||
long started = System.nanoTime();
|
||||
|
||||
void visit(@NotNull VirtualFile fileOrDir) {
|
||||
try {
|
||||
Path path = Paths.get(fileOrDir.getPath());
|
||||
if (fileOrDir.isDirectory()) {
|
||||
@@ -384,13 +420,6 @@ class LocalFileSystemRefreshWorker {
|
||||
catch (IOException ex) {
|
||||
LOG.error(ex);
|
||||
}
|
||||
|
||||
int requests = myRequests.incrementAndGet();
|
||||
long l = myTime.addAndGet(System.nanoTime() - started);
|
||||
|
||||
if (requests % 1000 == 0) {
|
||||
System.out.println("refresh:" + myRequests + " for " + TimeUnit.NANOSECONDS.toMillis(l) + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.awt.Graphics;
|
||||
import javax.swing.Icon;
|
||||
|
||||
final class DefaultControl implements Control {
|
||||
public final class DefaultControl implements Control {
|
||||
private final CompoundIcon collapsedIcon = new CompoundIcon("treeCollapsed");
|
||||
private final CompoundIcon expandedIcon = new CompoundIcon("treeExpanded");
|
||||
|
||||
|
||||
@@ -470,7 +470,9 @@
|
||||
</group>
|
||||
|
||||
<group id="ProjectViewPopupMenuModifyGroup">
|
||||
<reference ref="TypeHierarchy"/>
|
||||
<reference ref="ReformatCode"/>
|
||||
<reference ref="OptimizeImports"/>
|
||||
<reference ref="$Delete"/>
|
||||
<reference ref="Scratch.ChangeLanguage"/>
|
||||
<group id="MarkFileAs" class="com.intellij.openapi.file.exclude.ui.MarkFileGroup">
|
||||
|
||||
+1
-1
@@ -655,7 +655,7 @@ public class LocalFileSystemTest extends BareTestFixtureTestCase {
|
||||
|
||||
RefreshWorker.setCancellingCondition(null);
|
||||
topDir.refresh(false, true);
|
||||
assertEquals(processed, files);
|
||||
assertEquals(files, processed);
|
||||
}
|
||||
finally {
|
||||
connection.disconnect();
|
||||
|
||||
@@ -51,10 +51,10 @@ class NestedSourceMap(private val childMap: SourceMap, private val parentMap: So
|
||||
|
||||
override fun findSourceIndex(sourceFile: VirtualFile, localFileUrlOnly: Boolean): Int = parentMap.findSourceIndex(sourceFile, localFileUrlOnly)
|
||||
|
||||
override fun findSourceIndex(sourceUrls: List<Url>,
|
||||
override fun findSourceIndex(sourceUrl: Url,
|
||||
sourceFile: VirtualFile?,
|
||||
resolver: Lazy<SourceFileResolver?>?,
|
||||
localFileUrlOnly: Boolean): Int = parentMap.findSourceIndex(sourceUrls, sourceFile, resolver, localFileUrlOnly)
|
||||
localFileUrlOnly: Boolean): Int = parentMap.findSourceIndex(sourceUrl, sourceFile, resolver, localFileUrlOnly)
|
||||
|
||||
override fun processSourceMappingsInLine(sourceIndex: Int, sourceLine: Int, mappingProcessor: MappingsProcessorInLine): Boolean {
|
||||
val childSourceMappings = childMap.findSourceMappings(sourceIndex)
|
||||
|
||||
@@ -34,10 +34,10 @@ interface SourceMap {
|
||||
|
||||
fun findSourceMappings(sourceIndex: Int): Mappings
|
||||
|
||||
fun findSourceIndex(sourceUrls: List<Url>, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Int
|
||||
fun findSourceIndex(sourceUrl: Url, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Int
|
||||
|
||||
fun findSourceMappings(sourceUrls: List<Url>, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Mappings? {
|
||||
val sourceIndex = findSourceIndex(sourceUrls, sourceFile, resolver, localFileUrlOnly)
|
||||
fun findSourceMappings(sourceUrl: Url, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Mappings? {
|
||||
val sourceIndex = findSourceIndex(sourceUrl, sourceFile, resolver, localFileUrlOnly)
|
||||
return if (sourceIndex >= 0) findSourceMappings(sourceIndex) else null
|
||||
}
|
||||
|
||||
@@ -48,8 +48,14 @@ interface SourceMap {
|
||||
fun processSourceMappingsInLine(sourceIndex: Int, sourceLine: Int, mappingProcessor: MappingsProcessorInLine): Boolean
|
||||
|
||||
fun processSourceMappingsInLine(sourceUrls: List<Url>, sourceLine: Int, mappingProcessor: MappingsProcessorInLine, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Boolean {
|
||||
val sourceIndex = findSourceIndex(sourceUrls, sourceFile, resolver, localFileUrlOnly)
|
||||
return sourceIndex >= 0 && processSourceMappingsInLine(sourceIndex, sourceLine, mappingProcessor)
|
||||
var result = false
|
||||
for (sourceUrl in sourceUrls) {
|
||||
val sourceIndex = findSourceIndex(sourceUrl, sourceFile, resolver, localFileUrlOnly)
|
||||
if (sourceIndex >= 0 && processSourceMappingsInLine(sourceIndex, sourceLine, mappingProcessor)) {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +68,8 @@ class OneLevelSourceMap(override val outFile: String?,
|
||||
override val sources: Array<Url>
|
||||
get() = sourceResolver.canonicalizedUrls
|
||||
|
||||
override fun findSourceIndex(sourceUrls: List<Url>, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Int {
|
||||
val index = sourceResolver.findSourceIndex(sourceUrls, sourceFile, localFileUrlOnly)
|
||||
override fun findSourceIndex(sourceUrl: Url, sourceFile: VirtualFile?, resolver: Lazy<SourceFileResolver?>?, localFileUrlOnly: Boolean): Int {
|
||||
val index = sourceResolver.findSourceIndex(sourceUrl, sourceFile, localFileUrlOnly)
|
||||
if (index == -1 && resolver != null) {
|
||||
return resolver.value?.let { sourceResolver.findSourceIndex(it) } ?: -1
|
||||
}
|
||||
|
||||
@@ -78,12 +78,10 @@ class SourceResolver(private val rawSources: List<String>,
|
||||
return if (resolveByCanonicalizedUrls != -1) resolveByCanonicalizedUrls else resolver.resolve(rawSources)
|
||||
}
|
||||
|
||||
fun findSourceIndex(sourceUrls: List<Url>, sourceFile: VirtualFile?, localFileUrlOnly: Boolean): Int {
|
||||
for (sourceUrl in sourceUrls) {
|
||||
val index = canonicalizedUrlToSourceIndex.get(sourceUrl)
|
||||
if (index != -1) {
|
||||
return index
|
||||
}
|
||||
fun findSourceIndex(sourceUrl: Url, sourceFile: VirtualFile?, localFileUrlOnly: Boolean): Int {
|
||||
val index = canonicalizedUrlToSourceIndex.get(sourceUrl)
|
||||
if (index != -1) {
|
||||
return index
|
||||
}
|
||||
|
||||
if (sourceFile != null) {
|
||||
|
||||
@@ -1657,6 +1657,8 @@ JavaScript.Language.Service.truncate.traced.messages=true
|
||||
JavaScript.Language.Service.truncate.traced.messages.description=Truncate traced JavaScript language Service messages in log
|
||||
|
||||
vfs.use.nio-based.local.refresh.worker=false
|
||||
vfs.use.nio-based.local.refresh.worker.parallelism=7
|
||||
vfs.use.nio-based.local.refresh.worker.parallelism.description=How many threads will be used to access file system for detecting changes. Positive value is best suited for SSD because it allows running many operations in parallel
|
||||
vfs.use.new.jar.handler=true
|
||||
vfs.filewatcher.works.in.async.way=true
|
||||
|
||||
|
||||
+83
-17
@@ -15,6 +15,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.uast.UastVisitorAdapter;
|
||||
import com.intellij.ui.ContextHelpLabel;
|
||||
@@ -65,20 +66,49 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
|
||||
}
|
||||
PsiElement resolved = node.resolve();
|
||||
if (resolved instanceof PsiMember) {
|
||||
checkAccess(node.getSelector(), (PsiMember)resolved, receiver);
|
||||
checkAccess(node.getSelector(), (PsiMember)resolved, getAccessObjectType(receiver));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitSimpleNameReferenceExpression(@NotNull USimpleNameReferenceExpression node) {
|
||||
PsiElement resolved = node.resolve();
|
||||
if (resolved instanceof PsiMember) {
|
||||
checkAccess(node, (PsiMember)resolved, null);
|
||||
UElement uastParent = node.getUastParent();
|
||||
//we should skip 'checkAccess' here if node is part of UQualifiedReferenceExpression or UCallExpression node,
|
||||
// otherwise the same problem will be reported twice
|
||||
if (!isSelectorOfQualifiedReference(node)
|
||||
&& !(uastParent instanceof UCallExpression && isMethodReferenceOfCallExpression(node, (UCallExpression)uastParent)
|
||||
&& (((UCallExpression)uastParent).getKind() == UastCallKind.CONSTRUCTOR_CALL || isSelectorOfQualifiedReference((UExpression)uastParent)))) {
|
||||
PsiElement resolved = node.resolve();
|
||||
if (resolved instanceof PsiMember) {
|
||||
checkAccess(node, (PsiMember)resolved, null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isSelectorOfQualifiedReference(@Nullable UExpression expression) {
|
||||
if (expression == null) return false;
|
||||
UElement parent = expression.getUastParent();
|
||||
return parent instanceof UQualifiedReferenceExpression
|
||||
&& referToSameSourceElement(expression, ((UQualifiedReferenceExpression)parent).getSelector());
|
||||
}
|
||||
|
||||
private boolean isMethodReferenceOfCallExpression(@NotNull USimpleNameReferenceExpression expression, @NotNull UCallExpression parent) {
|
||||
UElement methodIdentifier = parent.getMethodIdentifier();
|
||||
UReferenceExpression classReference = parent.getClassReference();
|
||||
if (methodIdentifier == null && classReference != null) {
|
||||
methodIdentifier = classReference.getReferenceNameElement();
|
||||
}
|
||||
return referToSameSourceElement(expression.getReferenceNameElement(), methodIdentifier);
|
||||
}
|
||||
|
||||
private boolean referToSameSourceElement(@Nullable UElement element1, @Nullable UElement element2) {
|
||||
if (element1 == null || element2 == null) return false;
|
||||
PsiElement sourcePsi1 = element1.getSourcePsi();
|
||||
return sourcePsi1 != null && sourcePsi1.equals(element2.getSourcePsi());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression node) {
|
||||
PsiElement resolve = node.resolve();
|
||||
@@ -86,18 +116,50 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
|
||||
PsiMember member = (PsiMember)resolve;
|
||||
UElement sourceNode = getReferenceNameElement(node);
|
||||
if (sourceNode != null) {
|
||||
checkAccess(sourceNode, member, node.getQualifierExpression());
|
||||
checkAccess(sourceNode, member, getAccessObjectType(node.getQualifierExpression()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkAccess(@NotNull UElement sourceNode, @NotNull PsiMember target, @Nullable UExpression receiver) {
|
||||
@Override
|
||||
public boolean visitTypeReferenceExpression(@NotNull UTypeReferenceExpression node) {
|
||||
//in Kotlin implementation of UAST USimpleNameReferenceExpression::resolve returns null for reference to type in local variable declaration,
|
||||
// so we need to have this case specifically
|
||||
if (!(node.getSourcePsi() instanceof PsiTypeElement)) {
|
||||
PsiClass resolved = PsiTypesUtil.getPsiClass(node.getType());
|
||||
if (resolved != null) {
|
||||
checkAccess(node, resolved, null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitCallExpression(@NotNull UCallExpression node) {
|
||||
//regular method calls are handled by visitSimpleNameReferenceExpression or visitQualifiedReferenceExpression, but we need to handle
|
||||
// constructor calls in a special way because they may refer to classes
|
||||
if (!isSelectorOfQualifiedReference(node) && node.getKind() == UastCallKind.CONSTRUCTOR_CALL) {
|
||||
PsiMethod resolved = node.resolve();
|
||||
if (resolved != null) {
|
||||
checkAccess(node, resolved, null);
|
||||
}
|
||||
else {
|
||||
UReferenceExpression classReference = node.getClassReference();
|
||||
PsiElement resolvedClass = classReference != null ? classReference.resolve() : null;
|
||||
if (resolvedClass instanceof PsiClass) {
|
||||
checkAccess(node, (PsiClass)resolvedClass, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkAccess(@NotNull UElement sourceNode, @NotNull PsiMember target, @Nullable PsiClass accessObjectType) {
|
||||
if (target.hasModifier(JvmModifier.PACKAGE_LOCAL)) {
|
||||
checkPackageLocalAccess(sourceNode, target, "package-private");
|
||||
}
|
||||
else if (target.hasModifier(JvmModifier.PROTECTED) && receiver != null
|
||||
&& !(receiver instanceof UThisExpression) && !(receiver instanceof USuperExpression) && !canAccessProtectedMember(receiver, sourceNode, target)) {
|
||||
else if (target.hasModifier(JvmModifier.PROTECTED) && !canAccessProtectedMember(sourceNode, target, accessObjectType)) {
|
||||
checkPackageLocalAccess(sourceNode, target, "protected and used not through a subclass here");
|
||||
}
|
||||
}
|
||||
@@ -132,22 +194,26 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
|
||||
return node;
|
||||
}
|
||||
|
||||
private static boolean canAccessProtectedMember(UExpression receiver, UElement sourceNode, PsiMember member) {
|
||||
PsiClass memberClass = member.getContainingClass();
|
||||
if (memberClass == null) return false;
|
||||
@Nullable
|
||||
private static PsiClass getAccessObjectType(@Nullable UExpression receiver) {
|
||||
if (receiver == null || receiver instanceof UThisExpression || receiver instanceof USuperExpression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass accessObjectType;
|
||||
PsiType type = receiver.getExpressionType();
|
||||
if (type != null) {
|
||||
if (!(type instanceof PsiClassType)) return false;
|
||||
accessObjectType = ((PsiClassType)type).resolve();
|
||||
if (accessObjectType == null) return false;
|
||||
if (!(type instanceof PsiClassType)) return null;
|
||||
return ((PsiClassType)type).resolve();
|
||||
}
|
||||
else {
|
||||
PsiElement element = ((UReferenceExpression)receiver).resolve();
|
||||
if (!(element instanceof PsiClass)) return false;
|
||||
accessObjectType = (PsiClass)element;
|
||||
return element instanceof PsiClass ? (PsiClass)element : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canAccessProtectedMember(UElement sourceNode, PsiMember member, PsiClass accessObjectType) {
|
||||
PsiClass memberClass = member.getContainingClass();
|
||||
if (memberClass == null) return false;
|
||||
|
||||
PsiElement sourcePsi = sourceNode.getSourcePsi();
|
||||
UClass sourceClass = UastUtils.findContaining(sourcePsi, UClass.class);
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package xxx;
|
||||
|
||||
public class InnerClasses {
|
||||
static class PackagePrivateInnerClass {
|
||||
}
|
||||
|
||||
static class PackagePrivateInnerClassWithConstructor {
|
||||
PackagePrivateInnerClassWithConstructor() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassWithPackagePrivateConstructor {
|
||||
ClassWithPackagePrivateConstructor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package xxx;
|
||||
|
||||
public class ProtectedConstructors {
|
||||
protected ProtectedConstructors() {
|
||||
}
|
||||
|
||||
protected ProtectedConstructors(int i) {
|
||||
}
|
||||
}
|
||||
+4
@@ -9,4 +9,8 @@ public class PublicClass {
|
||||
|
||||
public void publicMethod() {}
|
||||
void packagePrivateMethod() {}
|
||||
|
||||
PublicClass() {}
|
||||
public PublicClass(int i) {}
|
||||
PublicClass(boolean b) {}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package xxx;
|
||||
|
||||
public class PublicClassWithDefaultConstructor {
|
||||
}
|
||||
+13
-5
@@ -7,19 +7,23 @@ import static xxx.StaticMembers.*;
|
||||
* @see PublicClass#packagePrivateField
|
||||
*/
|
||||
public class AccessingPackagePrivateMembers {
|
||||
Object field = new <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning>();
|
||||
static Object staticField = <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">new PackagePrivateClass()</warning>;
|
||||
Object field = <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">new PackagePrivateClass()</warning>;
|
||||
{
|
||||
new <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning>();
|
||||
<warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">new PackagePrivateClass()</warning>;
|
||||
}
|
||||
static {
|
||||
new <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning>();
|
||||
<warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">new PackagePrivateClass()</warning>;
|
||||
}
|
||||
|
||||
public void main() {
|
||||
new <warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning>();
|
||||
<warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">new PackagePrivateClass()</warning>;
|
||||
<warning descr="Class xxx.PackagePrivateClass is package-private, but declared in a different module 'dep'">PackagePrivateClass</warning> variable;
|
||||
|
||||
PublicClass aClass = new PublicClass();
|
||||
PublicClass aClass = new PublicClass(1);
|
||||
PublicClassWithDefaultConstructor aClass2 = new PublicClassWithDefaultConstructor();
|
||||
<warning descr="Constructor PublicClass.PublicClass() is package-private, but declared in a different module 'dep'">new PublicClass()</warning>;
|
||||
<warning descr="Constructor PublicClass.PublicClass(boolean) is package-private, but declared in a different module 'dep'">new PublicClass(true)</warning>;
|
||||
|
||||
System.out.println(aClass.publicField);
|
||||
System.out.println(aClass.<warning descr="Field PublicClass.packagePrivateField is package-private, but declared in a different module 'dep'">packagePrivateField</warning>);
|
||||
@@ -32,5 +36,9 @@ public class AccessingPackagePrivateMembers {
|
||||
|
||||
System.out.println(<warning descr="Field StaticMembers.IMPORTED_FIELD is package-private, but declared in a different module 'dep'">IMPORTED_FIELD</warning>);
|
||||
<warning descr="Method StaticMembers.importedMethod() is package-private, but declared in a different module 'dep'">importedMethod</warning>();
|
||||
|
||||
<warning descr="Class xxx.InnerClasses.PackagePrivateInnerClass is package-private, but declared in a different module 'dep'">new InnerClasses.<warning descr="Class xxx.InnerClasses.PackagePrivateInnerClass is package-private, but declared in a different module 'dep'">PackagePrivateInnerClass</warning>()</warning>;
|
||||
<warning descr="Constructor PackagePrivateInnerClassWithConstructor.PackagePrivateInnerClassWithConstructor() is package-private, but declared in a different module 'dep'">new InnerClasses.<warning descr="Class xxx.InnerClasses.PackagePrivateInnerClassWithConstructor is package-private, but declared in a different module 'dep'">PackagePrivateInnerClassWithConstructor</warning>()</warning>;
|
||||
<warning descr="Constructor ClassWithPackagePrivateConstructor.ClassWithPackagePrivateConstructor() is package-private, but declared in a different module 'dep'">new InnerClasses.ClassWithPackagePrivateConstructor()</warning>;
|
||||
}
|
||||
}
|
||||
+33
@@ -5,6 +5,10 @@ class AccessingProtectedMembersNotFromSubclass {
|
||||
ProtectedMembers aClass = new ProtectedMembers();
|
||||
aClass.<warning descr="Method ProtectedMembers.method() is protected and used not through a subclass here, but declared in a different module 'dep'">method</warning>();
|
||||
ProtectedMembers.<warning descr="Method ProtectedMembers.staticMethod() is protected and used not through a subclass here, but declared in a different module 'dep'">staticMethod</warning>();
|
||||
<warning descr="Constructor ProtectedConstructors.ProtectedConstructors() is protected and used not through a subclass here, but declared in a different module 'dep'">new ProtectedConstructors()</warning>;
|
||||
<warning descr="Constructor ProtectedConstructors.ProtectedConstructors(int) is protected and used not through a subclass here, but declared in a different module 'dep'">new ProtectedConstructors(1)</warning>;
|
||||
new ProtectedConstructors() {};
|
||||
new ProtectedConstructors(1) {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +25,13 @@ class AccessingProtectedMembersFromSubclass extends ProtectedMembers {
|
||||
|
||||
ProtectedMembers.StaticInner inner1;
|
||||
StaticInner inner2;
|
||||
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
method();
|
||||
staticMethod();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class StaticInnerImpl1 extends ProtectedMembers.StaticInner {
|
||||
@@ -28,4 +39,26 @@ class AccessingProtectedMembersFromSubclass extends ProtectedMembers {
|
||||
|
||||
public static class StaticInnerImpl2 extends StaticInner {
|
||||
}
|
||||
|
||||
public class OwnInner {
|
||||
void bar() {
|
||||
method();
|
||||
staticMethod();
|
||||
}
|
||||
}
|
||||
|
||||
public static class OwnStaticInner {
|
||||
void bar() {
|
||||
staticMethod();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AccessingDefaultProtectedConstructorFromSubclass extends ProtectedConstructors {
|
||||
}
|
||||
|
||||
class AccessingProtectedConstructorFromSubclass extends ProtectedConstructors {
|
||||
AccessingProtectedConstructorFromSubclass() {
|
||||
super(1);
|
||||
}
|
||||
}
|
||||
+2
-94
@@ -1,27 +1,8 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.dependency;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.siyeh.ig.LightInspectionTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SuspiciousPackagePrivateAccessInspectionTest extends LightInspectionTestCase {
|
||||
private final ProjectWithDepModuleDescriptor myProjectDescriptor = new ProjectWithDepModuleDescriptor(LanguageLevel.HIGHEST);
|
||||
public class SuspiciousPackagePrivateAccessInspectionTest extends SuspiciousPackagePrivateAccessInspectionTestCase {
|
||||
public SuspiciousPackagePrivateAccessInspectionTest() {super("java");}
|
||||
|
||||
public void testAccessingPackagePrivateMembers() {
|
||||
doTestWithDependency();
|
||||
@@ -30,77 +11,4 @@ public class SuspiciousPackagePrivateAccessInspectionTest extends LightInspectio
|
||||
public void testAccessingProtectedMembers() {
|
||||
doTestWithDependency();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.copyDirectoryToProject("dep", ProjectWithDepModuleDescriptor.getDepModuleSourceRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
myProjectDescriptor.cleanUpSources();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
addSuppressedException(e);
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestWithDependency() {
|
||||
myFixture.configureByFile("src/" + getTestName(false) + ".java");
|
||||
myFixture.testHighlighting(true, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return myProjectDescriptor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new SuspiciousPackagePrivateAccessInspection();
|
||||
}
|
||||
|
||||
private static class ProjectWithDepModuleDescriptor extends ProjectDescriptor {
|
||||
private static final String DEP_MODULE_SOURCE_ROOT = "dep-module-src";
|
||||
private VirtualFile mySourceRoot;
|
||||
|
||||
ProjectWithDepModuleDescriptor(@NotNull LanguageLevel languageLevel) {
|
||||
super(languageLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpProject(@NotNull Project project, @NotNull SetupHandler handler) throws Exception {
|
||||
super.setUpProject(project, handler);
|
||||
WriteAction.run(() -> {
|
||||
Module mainModule = ModuleManager.getInstance(project).findModuleByName(TEST_MODULE_NAME);
|
||||
File depModuleDir = FileUtil.createTempDirectory("dep-module-", null);
|
||||
Module depModule = createModule(project, depModuleDir + "/dep.iml");
|
||||
ModuleRootModificationUtil.updateModel(depModule, model -> {
|
||||
model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(myLanguageLevel);
|
||||
model.setSdk(getSdk());
|
||||
mySourceRoot = createSourceRoot(depModule, DEP_MODULE_SOURCE_ROOT);
|
||||
model.addContentEntry(mySourceRoot).addSourceFolder(mySourceRoot, JavaSourceRootType.SOURCE);
|
||||
});
|
||||
ModuleRootModificationUtil.addDependency(mainModule, depModule);
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanUpSources() throws IOException {
|
||||
if (mySourceRoot != null) {
|
||||
WriteAction.run(() -> mySourceRoot.delete(this));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getDepModuleSourceRoot() {
|
||||
return "../" + DEP_MODULE_SOURCE_ROOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.dependency;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.siyeh.ig.LightInspectionTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SuspiciousPackagePrivateAccessInspectionTestCase extends LightInspectionTestCase {
|
||||
private final ProjectWithDepModuleDescriptor myProjectDescriptor = new ProjectWithDepModuleDescriptor(LanguageLevel.HIGHEST);
|
||||
private final String myExtension;
|
||||
|
||||
public SuspiciousPackagePrivateAccessInspectionTestCase(String extension) {
|
||||
myExtension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.copyDirectoryToProject("dep", ProjectWithDepModuleDescriptor.getDepModuleSourceRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
myProjectDescriptor.cleanUpSources();
|
||||
}
|
||||
catch (Throwable e) {
|
||||
addSuppressedException(e);
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
protected void doTestWithDependency() {
|
||||
myFixture.configureByFile("src/" + getTestName(false) + "." + myExtension);
|
||||
myFixture.testHighlighting(true, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return myProjectDescriptor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new SuspiciousPackagePrivateAccessInspection();
|
||||
}
|
||||
|
||||
private static class ProjectWithDepModuleDescriptor extends ProjectDescriptor {
|
||||
private static final String DEP_MODULE_SOURCE_ROOT = "dep-module-src";
|
||||
private VirtualFile mySourceRoot;
|
||||
|
||||
ProjectWithDepModuleDescriptor(@NotNull LanguageLevel languageLevel) {
|
||||
super(languageLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpProject(@NotNull Project project, @NotNull SetupHandler handler) throws Exception {
|
||||
super.setUpProject(project, handler);
|
||||
WriteAction.run(() -> {
|
||||
Module mainModule = ModuleManager.getInstance(project).findModuleByName(TEST_MODULE_NAME);
|
||||
File depModuleDir = FileUtil.createTempDirectory("dep-module-", null);
|
||||
Module depModule = createModule(project, depModuleDir + "/dep.iml");
|
||||
ModuleRootModificationUtil.updateModel(depModule, model -> {
|
||||
model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(myLanguageLevel);
|
||||
model.setSdk(getSdk());
|
||||
mySourceRoot = createSourceRoot(depModule, DEP_MODULE_SOURCE_ROOT);
|
||||
model.addContentEntry(mySourceRoot).addSourceFolder(mySourceRoot, JavaSourceRootType.SOURCE);
|
||||
});
|
||||
ModuleRootModificationUtil.addDependency(mainModule, depModule);
|
||||
});
|
||||
}
|
||||
|
||||
public void cleanUpSources() throws IOException {
|
||||
if (mySourceRoot != null) {
|
||||
WriteAction.run(() -> mySourceRoot.delete(this));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getDepModuleSourceRoot() {
|
||||
return "../" + DEP_MODULE_SOURCE_ROOT;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-79
@@ -1,6 +1,4 @@
|
||||
// Copyright 2000-2017 JetBrains s.r.o.
|
||||
// Use of this source code is governed by the Apache 2.0 license that can be
|
||||
// found in the LICENSE file.
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess;
|
||||
|
||||
import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
@@ -9,6 +7,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
|
||||
import com.intellij.codeInsight.intention.EmptyIntentionAction;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory;
|
||||
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -32,20 +31,14 @@ import org.jetbrains.plugins.groovy.codeInspection.GrInspectionUtil;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyQuickFixFactory;
|
||||
import org.jetbrains.plugins.groovy.extensions.GroovyUnresolvedHighlightFilter;
|
||||
import org.jetbrains.plugins.groovy.findUsages.MissingMethodAndPropertyUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.GrCreateClassKind;
|
||||
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GroovyDocPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.EmptyGroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
|
||||
@@ -68,6 +61,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.intellij.psi.util.PsiUtil.isInnerClass;
|
||||
import static org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.ReferenceFixesKt.generateAddImportActions;
|
||||
import static org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.ReferenceFixesKt.generateCreateClassActions;
|
||||
import static org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil.hasArguments;
|
||||
import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.hasEnclosingInstanceInScope;
|
||||
|
||||
@@ -453,81 +448,17 @@ public class GrUnresolvedAccessChecker {
|
||||
}
|
||||
|
||||
private static void registerAddImportFixes(GrReferenceElement refElement, @Nullable HighlightInfo info, final HighlightDisplayKey key) {
|
||||
final String referenceName = refElement.getReferenceName();
|
||||
if (StringUtil.isEmpty(referenceName)) return;
|
||||
if (!(refElement instanceof GrCodeReferenceElement) && Character.isLowerCase(referenceName.charAt(0))) return;
|
||||
if (refElement.getQualifier() != null) return;
|
||||
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createGroovyAddImportAction(refElement), key);
|
||||
for (IntentionAction action : generateAddImportActions(refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, action, key);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerCreateClassByTypeFix(@NotNull GrReferenceElement refElement,
|
||||
@Nullable HighlightInfo info,
|
||||
final HighlightDisplayKey key) {
|
||||
GrPackageDefinition packageDefinition = PsiTreeUtil.getParentOfType(refElement, GrPackageDefinition.class);
|
||||
if (packageDefinition != null) return;
|
||||
|
||||
PsiElement parent = refElement.getParent();
|
||||
if (parent instanceof GrNewExpression &&
|
||||
refElement.getManager().areElementsEquivalent(((GrNewExpression)parent).getReferenceElement(), refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFromNewAction((GrNewExpression)parent), key);
|
||||
HighlightDisplayKey key) {
|
||||
for (IntentionAction fix : generateCreateClassActions(refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, fix, key);
|
||||
}
|
||||
else if (canBeClassOrPackage(refElement)) {
|
||||
if (shouldBeInterface(refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.INTERFACE), key);
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.TRAIT), key);
|
||||
}
|
||||
else if (shouldBeClass(refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.CLASS), key);
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.ENUM), key);
|
||||
}
|
||||
else if (shouldBeAnnotation(refElement)) {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.ANNOTATION), key);
|
||||
}
|
||||
else {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.CLASS), key);
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.INTERFACE), key);
|
||||
|
||||
if (!refElement.isQualified() || resolvesToGroovy(refElement.getQualifier())) {
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.TRAIT), key);
|
||||
}
|
||||
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.ENUM), key);
|
||||
QuickFixAction.registerQuickFixAction(info, GroovyQuickFixFactory.getInstance().createClassFixAction(refElement, GrCreateClassKind.ANNOTATION), key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean resolvesToGroovy(PsiElement qualifier) {
|
||||
if (qualifier instanceof GrReferenceElement) {
|
||||
return ((GrReferenceElement)qualifier).resolve() instanceof GroovyPsiElement;
|
||||
}
|
||||
if (qualifier instanceof GrExpression) {
|
||||
PsiType type = ((GrExpression)qualifier).getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
PsiClass resolved = ((PsiClassType)type).resolve();
|
||||
return resolved instanceof GroovyPsiElement;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canBeClassOrPackage(@NotNull GrReferenceElement refElement) {
|
||||
return !(refElement instanceof GrReferenceExpression) || ResolveUtil.canBeClassOrPackage((GrReferenceExpression)refElement);
|
||||
}
|
||||
|
||||
private static boolean shouldBeAnnotation(GrReferenceElement element) {
|
||||
return element.getParent() instanceof GrAnnotation;
|
||||
}
|
||||
|
||||
private static boolean shouldBeInterface(GrReferenceElement myRefElement) {
|
||||
PsiElement parent = myRefElement.getParent();
|
||||
return parent instanceof GrImplementsClause || parent instanceof GrExtendsClause && parent.getParent() instanceof GrInterfaceDefinition;
|
||||
}
|
||||
|
||||
private static boolean shouldBeClass(GrReferenceElement myRefElement) {
|
||||
PsiElement parent = myRefElement.getParent();
|
||||
return parent instanceof GrExtendsClause && !(parent.getParent() instanceof GrInterfaceDefinition);
|
||||
}
|
||||
|
||||
private static boolean shouldHighlightAsUnresolved(@NotNull GrReferenceExpression referenceExpression) {
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.plugins.groovy.codeInspection.GroovyQuickFixFactory
|
||||
import org.jetbrains.plugins.groovy.lang.GrCreateClassKind
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement
|
||||
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil.canBeClassOrPackage
|
||||
|
||||
fun generateCreateClassActions(ref: GrReferenceElement<*>): Collection<IntentionAction> {
|
||||
if (ref.parentOfType<GrPackageDefinition>() != null) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val factory = GroovyQuickFixFactory.getInstance()
|
||||
|
||||
val parent = ref.parent
|
||||
if (parent is GrNewExpression && parent.referenceElement === ref) {
|
||||
return listOf(factory.createClassFromNewAction(parent))
|
||||
}
|
||||
|
||||
if (ref is GrReferenceExpression && !canBeClassOrPackage(ref)) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return when {
|
||||
classExpected(parent) -> listOf(
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.CLASS),
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.ENUM)
|
||||
)
|
||||
interfaceExpected(parent) -> listOf(
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.INTERFACE),
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.TRAIT)
|
||||
)
|
||||
annotationExpected(parent) -> listOf(
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.ANNOTATION)
|
||||
)
|
||||
else -> {
|
||||
val result = mutableListOf(
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.CLASS),
|
||||
factory.createClassFixAction(ref, GrCreateClassKind.INTERFACE)
|
||||
)
|
||||
if (!ref.isQualified || resolvesToGroovy(ref.qualifier)) {
|
||||
result += factory.createClassFixAction(ref, GrCreateClassKind.TRAIT)
|
||||
}
|
||||
result += factory.createClassFixAction(ref, GrCreateClassKind.ENUM)
|
||||
result += factory.createClassFixAction(ref, GrCreateClassKind.ANNOTATION)
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun classExpected(parent: PsiElement?): Boolean {
|
||||
return parent is GrExtendsClause && parent.parent !is GrInterfaceDefinition
|
||||
}
|
||||
|
||||
private fun interfaceExpected(parent: PsiElement?): Boolean {
|
||||
return parent is GrImplementsClause || parent is GrExtendsClause && parent.parent is GrInterfaceDefinition
|
||||
}
|
||||
|
||||
private fun annotationExpected(parent: PsiElement?): Boolean {
|
||||
return parent is GrAnnotation
|
||||
}
|
||||
|
||||
private fun resolvesToGroovy(qualifier: PsiElement?): Boolean {
|
||||
return when (qualifier) {
|
||||
is GrReferenceElement<*> -> qualifier.resolve() is GroovyPsiElement
|
||||
is GrExpression -> {
|
||||
val type = qualifier.type as? PsiClassType
|
||||
type?.resolve() is GroovyPsiElement
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun generateAddImportActions(ref: GrReferenceElement<*>): Collection<IntentionAction> {
|
||||
return generateAddImportAction(ref)?.let(::listOf) ?: emptyList()
|
||||
}
|
||||
|
||||
private fun generateAddImportAction(ref: GrReferenceElement<*>): IntentionAction? {
|
||||
if (ref.isQualified) return null
|
||||
val referenceName = ref.referenceName ?: return null
|
||||
if (referenceName.isEmpty()) return null
|
||||
if (ref !is GrCodeReferenceElement && Character.isLowerCase(referenceName[0])) return null
|
||||
return GroovyQuickFixFactory.getInstance().createGroovyAddImportAction(ref)
|
||||
}
|
||||
+6
-2
@@ -19,8 +19,12 @@ internal object ImplicitClosureCallPredicate : PsiElementPredicate {
|
||||
return false
|
||||
}
|
||||
val result = element.advancedResolve()
|
||||
return result.isInvokedOnProperty && element.invokedExpression.type.isClosureType()
|
||||
|| result.element.isClosureCallMethod()
|
||||
if (element.implicitCallReference == null) {
|
||||
return result.isInvokedOnProperty && element.invokedExpression.type.isClosureType()
|
||||
}
|
||||
else {
|
||||
return result.element.isClosureCallMethod()
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiType?.isClosureType(): Boolean {
|
||||
|
||||
+5
@@ -54,4 +54,9 @@ class MakeClosureCallExplicitIntentionTest extends GroovyLatestTest implements B
|
||||
void 'closure method'() {
|
||||
doTest 'Closure foo() {}; <caret>foo()', null
|
||||
}
|
||||
|
||||
@Test
|
||||
void 'closure method call'() {
|
||||
doTest 'Closure foo() {}; foo().<caret>call()', null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,9 +192,6 @@
|
||||
</reference>
|
||||
|
||||
<group id="ProjectViewAnalysisGroup">
|
||||
<reference ref="TypeHierarchy"/>
|
||||
<reference ref="OptimizeImports"/>
|
||||
|
||||
<add-to-group group-id="ProjectViewPopupMenuModifyGroup" anchor="first"/>
|
||||
</group>
|
||||
|
||||
|
||||
@@ -28,13 +28,13 @@ import java.util.Locale;
|
||||
*/
|
||||
public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
protected static final int BASE_STATE_MASK = 0x3F;
|
||||
private static final int SEEN_STYLE = 0x40;
|
||||
private static final int SEEN_TAG = 0x80;
|
||||
private static final int SEEN_SCRIPT = 0x100;
|
||||
private static final int SEEN_ATTRIBUTE = 0x200;
|
||||
private static final int SEEN_CONTENT_TYPE = 0x400;
|
||||
private static final int SEEN_STYLESHEET_TYPE = 0x800;
|
||||
protected static final int BASE_STATE_SHIFT = 11;
|
||||
private static final int SEEN_TAG = 0x40;
|
||||
private static final int SEEN_ATTRIBUTE = 0x80;
|
||||
private static final int SEEN_CONTENT_TYPE = 0x100;
|
||||
private static final int SEEN_STYLESHEET_TYPE = 0x200;
|
||||
private static final int SEEN_STYLE_SCRIPT_MASK = 0xE00;
|
||||
private static final int SEEN_STYLE_SCRIPT_SHIFT = 10;
|
||||
protected static final int BASE_STATE_SHIFT = 12;
|
||||
@Nullable
|
||||
protected static final Language ourDefaultLanguage = Language.findLanguageByID("JavaScript");
|
||||
@Nullable
|
||||
@@ -45,6 +45,10 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
protected boolean seenStyle;
|
||||
protected boolean seenScript;
|
||||
|
||||
private static final char SCRIPT = 1;
|
||||
private static final char STYLE = 2;
|
||||
private final int[] scriptStyleStack = new int[] {0, 0};
|
||||
|
||||
@Nullable
|
||||
protected String scriptType = null;
|
||||
@Nullable
|
||||
@@ -119,8 +123,7 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
return;
|
||||
}
|
||||
|
||||
seenStyle = style;
|
||||
seenScript = script;
|
||||
pushScriptStyle(script, style);
|
||||
|
||||
if (!isHtmlTagState(state)) {
|
||||
seenAttribute=true;
|
||||
@@ -133,8 +136,7 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
@Override
|
||||
public void handleElement(Lexer lexer) {
|
||||
if (seenAttribute) {
|
||||
seenStyle = false;
|
||||
seenScript = false;
|
||||
popScriptStyle();
|
||||
seenAttribute = false;
|
||||
}
|
||||
seenContentType = false;
|
||||
@@ -142,6 +144,22 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
}
|
||||
}
|
||||
|
||||
private void pushScriptStyle(boolean script, boolean style) {
|
||||
int position = scriptStyleStack[0] == 0 ? 0 : 1;
|
||||
scriptStyleStack[position] = script ? SCRIPT :
|
||||
style ? STYLE :
|
||||
0;
|
||||
seenStyle = style;
|
||||
seenScript = script;
|
||||
}
|
||||
|
||||
protected void popScriptStyle() {
|
||||
int position = scriptStyleStack[1] == 0 ? 0 : 1;
|
||||
scriptStyleStack[position] = 0;
|
||||
seenStyle = scriptStyleStack[0] == STYLE;
|
||||
seenScript = scriptStyleStack[0] == SCRIPT;
|
||||
}
|
||||
|
||||
class XmlAttributeValueHandler implements TokenHandler {
|
||||
@Override
|
||||
public void handleElement(Lexer lexer) {
|
||||
@@ -218,9 +236,7 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
@Override
|
||||
public void handleElement(Lexer lexer) {
|
||||
if (seenAttribute) {
|
||||
seenScript=false;
|
||||
seenStyle=false;
|
||||
|
||||
popScriptStyle();
|
||||
seenAttribute=false;
|
||||
} else {
|
||||
if (seenStyle || seenScript) {
|
||||
@@ -233,8 +249,7 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
class XmlTagEndHandler implements TokenHandler {
|
||||
@Override
|
||||
public void handleElement(Lexer lexer) {
|
||||
seenStyle=false;
|
||||
seenScript=false;
|
||||
popScriptStyle();
|
||||
seenAttribute=false;
|
||||
seenContentType=false;
|
||||
seenStylesheetType=false;
|
||||
@@ -283,12 +298,18 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
}
|
||||
|
||||
private void initState(final int initialState) {
|
||||
seenScript = (initialState & SEEN_SCRIPT)!=0;
|
||||
seenStyle = (initialState & SEEN_STYLE)!=0;
|
||||
seenTag = (initialState & SEEN_TAG)!=0;
|
||||
seenAttribute = (initialState & SEEN_ATTRIBUTE)!=0;
|
||||
seenContentType = (initialState & SEEN_CONTENT_TYPE) != 0;
|
||||
seenStylesheetType = (initialState & SEEN_STYLESHEET_TYPE) != 0;
|
||||
if (seenTag || seenAttribute) {
|
||||
int stack = (initialState & SEEN_STYLE_SCRIPT_MASK) >> SEEN_STYLE_SCRIPT_SHIFT + 1;
|
||||
scriptStyleStack[0] = stack / 3;
|
||||
scriptStyleStack[1] = stack % 3;
|
||||
}
|
||||
int position = scriptStyleStack[1] == 0 ? 0 : 1;
|
||||
seenStyle = scriptStyleStack[position] == STYLE;
|
||||
seenScript = scriptStyleStack[position] == SCRIPT;
|
||||
lexerOfCacheBufferSequence = null;
|
||||
cachedBufferSequence = null;
|
||||
}
|
||||
@@ -335,10 +356,8 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
if (base.getTokenType() != XmlTokenType.XML_END_TAG_START) { // we are inside comment
|
||||
base.start(buf,lastStart+1,getBufferEnd(),lastState);
|
||||
base.getTokenType();
|
||||
base.advance();
|
||||
} else {
|
||||
base.advance();
|
||||
}
|
||||
base.advance();
|
||||
|
||||
while(XmlTokenType.WHITESPACES.contains(base.getTokenType())) {
|
||||
base.advance();
|
||||
@@ -399,13 +418,15 @@ public abstract class BaseHtmlLexer extends DelegateLexer {
|
||||
public int getState() {
|
||||
int state = super.getState();
|
||||
|
||||
state |= ((seenScript)?SEEN_SCRIPT:0);
|
||||
state |= ((seenTag)?SEEN_TAG:0);
|
||||
state |= ((seenStyle)?SEEN_STYLE:0);
|
||||
state |= ((seenAttribute)?SEEN_ATTRIBUTE:0);
|
||||
state |= ((seenContentType)?SEEN_CONTENT_TYPE:0);
|
||||
state |= ((seenStylesheetType)?SEEN_STYLESHEET_TYPE:0);
|
||||
|
||||
if (seenTag || seenAttribute) {
|
||||
state |= (scriptStyleStack[0] * 3 + scriptStyleStack[1] - 1) << 9;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user