[java, fix] handle invalid module names

GitOrigin-RevId: 2b2a88ae4536a47376ee5e9d21d418c7165e4d69
This commit is contained in:
Aleksey Dobrynin
2024-02-16 11:31:17 +00:00
committed by intellij-monorepo-bot
parent 1714ca86b3
commit 2fcfda59c0
17 changed files with 150 additions and 12 deletions
@@ -99,7 +99,7 @@ public final class JavaModuleGraphUtil {
return CachedValuesManager.getCachedValue(rootPsi, () -> {
VirtualFile _root = rootPsi.getVirtualFile();
LightJavaModule result = LightJavaModule.create(rootPsi.getManager(), _root, LightJavaModule.moduleName(_root));
return Result.create(result, _root, ProjectRootModificationTracker.getInstance(rootPsi.getProject()));
return Result.create(result, _root, ProjectRootModificationTracker.getInstance(rootPsi.getProject()));
});
}
}
@@ -249,6 +249,7 @@ public final class JavaModuleGraphUtil {
@Nullable DependencyScope scope) {
if (to.getName().equals(JAVA_BASE)) return false;
if (!PsiUtil.isAvailable(JavaFeature.MODULES, from)) return false;
if (!PsiNameHelper.isValidModuleName(to.getName(), to)) return false;
if (alreadyContainsRequires(from, to.getName())) return false;
PsiUtil.addModuleStatement(from, PsiKeyword.REQUIRES + " " +
(isStaticModule(to.getName(), scope) ? PsiKeyword.STATIC + " " : "") +
@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.actions;
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil;
@@ -18,6 +18,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.impl.light.LightJavaModule;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
@@ -32,6 +33,8 @@ import static com.intellij.ide.fileTemplates.JavaTemplateUtil.INTERNAL_MODULE_IN
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_CLASS;
public class CreateModuleInfoAction extends CreateFromTemplateActionBase {
private static final String DEFAULT_MODULE_NAME = "module_name";
public CreateModuleInfoAction() {
super(JavaBundle.messagePointer("action.create.new.module-info.title"), JavaBundle.messagePointer("action.create.new.module-info.description"), AllIcons.FileTypes.Java);
}
@@ -86,6 +89,8 @@ public class CreateModuleInfoAction extends CreateFromTemplateActionBase {
@Override
protected Map<String, String> getLiveTemplateDefaults(@NotNull DataContext ctx, @NotNull PsiFile file) {
Module module = PlatformCoreDataKeys.MODULE.getData(ctx);
return Collections.singletonMap("MODULE_NAME", module != null ? LightJavaModule.moduleName(module.getName()) : "module_name");
String moduleName = module != null ? LightJavaModule.moduleName(module.getName()) : DEFAULT_MODULE_NAME;
moduleName = PsiNameHelper.isValidModuleName(moduleName, file) ? moduleName : DEFAULT_MODULE_NAME;
return Collections.singletonMap("MODULE_NAME", moduleName);
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.light;
import com.intellij.lang.java.JavaLanguage;
@@ -254,7 +254,7 @@ public final class LightJavaModule extends LightElement implements PsiJavaModule
public static @Nullable String claimedModuleName(@NotNull VirtualFile manifest) {
try (InputStream stream = manifest.getInputStream()) {
return new Manifest(stream).getMainAttributes().getValue(PsiJavaModule.AUTO_MODULE_NAME);
return new Manifest(stream).getMainAttributes().getValue(AUTO_MODULE_NAME);
}
catch (IOException e) {
Logger.getInstance(LightJavaModule.class).warn(manifest.getPath(), e);
@@ -274,7 +274,7 @@ public final class LightJavaModule extends LightElement implements PsiJavaModule
* <p>Implements a name deriving for automatic modules as described in ModuleFinder.of(Path...) method documentation.</p>
*
* <p>Please note that the result may not be a valid module name when the source contains a sequence that starts with a digit
* (e.g. "org.7gnomes..."). One may validate the result with {@link PsiNameHelper#isValidModuleName}.</p>
* (e.g. "org.7gnomes...", "module.for...."). One may validate the result with {@link PsiNameHelper#isValidModuleName}.</p>
*
* @param name a .jar file name without the extension
* @see <a href="http://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-">ModuleFinder.of(Path...)</a>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/main.iml" filepath="$PROJECT_DIR$/main.iml" />
</modules>
</component>
</project>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../lib/lib-for-test.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../lib/test.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -0,0 +1,9 @@
import org.jetbrains.org.jetbrains.intellij.java.test.library.*;
import l.InLib;
public class MainTest {
public static void main(String[] args) {
System.out.println(Util.name());
System.out.println(InLib.class);
}
}
@@ -0,0 +1,3 @@
module main {
requires intellij.java.test.library;
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/main.iml" filepath="$PROJECT_DIR$/main.iml" />
</modules>
</component>
</project>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../lib/lib-for-test.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library" scope="TEST">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../lib/test.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -0,0 +1,9 @@
import org.jetbrains.org.jetbrains.intellij.java.test.library.*;
import l.InLib;
public class MainTest {
public static void main(String[] args) {
System.out.println(Util.name());
System.out.println(InLib.class);
}
}
@@ -0,0 +1,2 @@
<warning descr="Unresolved module dependencies">module main {
}</warning>
@@ -55,6 +55,10 @@ public class JavaEmptyModuleInfoFileInspectionTest extends LightMultiFileTestCas
doTest("test/module-info.java");
}
public void testInvalidLibraryName() {
doTest("test/module-info.java");
}
public void testMultiModuleProject() {
doTest("src/module-info.java", "main/");
}
@@ -76,6 +80,6 @@ public class JavaEmptyModuleInfoFileInspectionTest extends LightMultiFileTestCas
myFixture.launchAction(intention);
NonBlockingReadActionImpl.waitForAsyncTaskCompletion();
}
myFixture.checkResultByFile(getTestName(false) + "/after/" + dir + path);
myFixture.checkResultByFile(getTestName(true) + "/after/" + dir + path);
}
}
@@ -1,14 +1,16 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.ide.actions
import com.intellij.ide.IdeView
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.M2
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.MAIN
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
import com.intellij.openapi.module.ModuleManager
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiJavaModule
import com.intellij.psi.PsiManager
@@ -17,6 +19,24 @@ import com.intellij.testFramework.PlatformTestUtil
class CreateModuleInfoActionTest : LightJava9ModulesCodeInsightFixtureTestCase() {
fun test() {
val testModule = ModuleManager.getInstance(project).findModuleByName(M2.moduleName)!!
val dir = PsiManager.getInstance(project).findDirectory(M2.sourceRoot()!!)!!
val ctx = MapDataContext(mapOf(LangDataKeys.IDE_VIEW to TestIdeView(dir), PlatformCoreDataKeys.MODULE to testModule))
val event = AnActionEvent.createFromDataContext("", null, ctx)
ActionManager.getInstance().getAction("NewModuleInfo")!!.actionPerformed(event)
val file = dir.findFile(PsiJavaModule.MODULE_INFO_FILE)!!
val p = FileTemplateManager.getInstance(project).defaultProperties
assertEquals("""
/**
* Created by ${p["USER"]} on ${p["DATE"]}.
*/
module light.idea.test.m2 {
}""".trimIndent(), file.text)
}
fun testNotValidName() {
module.name
val dir = PsiManager.getInstance(project).findDirectory(MAIN.sourceRoot()!!)!!
val ctx = MapDataContext(mapOf(LangDataKeys.IDE_VIEW to TestIdeView(dir), PlatformCoreDataKeys.MODULE to module))
val event = AnActionEvent.createFromDataContext("", null, ctx)
@@ -28,7 +48,7 @@ class CreateModuleInfoActionTest : LightJava9ModulesCodeInsightFixtureTestCase()
/**
* Created by ${p["USER"]} on ${p["DATE"]}.
*/
module light.idea.test.case {
module module_name {
}""".trimIndent(), file.text)
}
@@ -1,6 +1,4 @@
/*
* 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.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.psi.impl.light
import com.intellij.psi.impl.light.LightJavaModule
@@ -15,6 +13,7 @@ class JavaModuleNameDetectionTest {
@Test fun collapsing() = doTest("foo...bar", "foo.bar")
@Test fun trimming() = doTest("...foo.bar...", "foo.bar")
@Test fun invalid() = doTest("lib_invalid_1_2", "lib.invalid.1.2")
@Test fun withKeywords() = doTest("lib.for.ide", "lib.for.ide")
private fun doTest(original: String, expected: String) = assertEquals(expected, LightJavaModule.moduleName(original))
}