diff --git a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
index 0d2c664a5425..7d8e4851426c 100644
--- a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
+++ b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
@@ -767,5 +767,5 @@ inspections.message.string.without.path.annotation.used.in.path.constructor.or.f
inspections.message.string.without.path.annotation.used.in.path.resolve.method=String without path annotation is used in Path.resolve() method
inspections.message.first.argument.fs.getpath.should.be.annotated.with.nativepath=First argument of FileSystem.getPath() should be annotated with @NativePath
inspections.message.more.parameters.in.fs.getpath.should.be.annotated.with.nativepath.or.filename=Elements of 'more' parameter in FileSystem.getPath() should be annotated with either @NativePath or @Filename
-inspections.message.first.argument.path.of.should.be.annotated.with.multiroutingfilesystempath=First argument of Path.of() should be annotated with @MultiRoutingFileSystemPath
+inspections.message.first.argument.path.of.should.be.annotated.with.multiroutingfilesystempath=First argument of Path.of() should be annotated with @MultiRoutingFileSystemPath or @Filename
inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename=Elements of 'more' parameter in Path.of() should be annotated with either @MultiRoutingFileSystemPath or @Filename
diff --git a/plugins/devkit/devkit-core/src/inspections/path/PathAnnotationInspection.kt b/plugins/devkit/devkit-core/src/inspections/path/PathAnnotationInspection.kt
index aa65c0108d70..a173abba5a74 100644
--- a/plugins/devkit/devkit-core/src/inspections/path/PathAnnotationInspection.kt
+++ b/plugins/devkit/devkit-core/src/inspections/path/PathAnnotationInspection.kt
@@ -54,113 +54,63 @@ class PathAnnotationInspection : DevKitUastInspectionBase() {
// Check if the method is a Path constructor or factory method
if (isPathConstructorOrFactory(target)) {
val arguments = node.valueArguments
- if (arguments.isNotEmpty()) {
- // Check first argument
- val firstArg = arguments[0]
- val firstArgInfo = PathAnnotationInfo.forExpression(firstArg)
+ for (i in 0 until arguments.size) {
+ val arg = arguments[i]
- when (firstArgInfo) {
- is PathAnnotationInfo.Native -> {
- // Report error: @NativePath string used in Path constructor or factory method
- holder.registerProblem(
- sourcePsi,
- DevKitBundle.message("inspections.message.nativepath.should.not.be.used.directly.constructing.path"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.Unspecified -> {
- // Report normal warning: non-annotated string used in Path constructor or factory method
- holder.registerProblem(
- sourcePsi,
- DevKitBundle.message("inspections.message.string.without.path.annotation.used.in.path.constructor.or.factory.method"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.FilenameInfo -> {
- // Report error: first argument of Path.of() should be annotated with @MultiRoutingFileSystemPath
- holder.registerProblem(
- firstArg.sourcePsi ?: sourcePsi,
- DevKitBundle.message("inspections.message.first.argument.path.of.should.be.annotated.with.multiroutingfilesystempath"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.LocalPathInfo -> {
- // Report error: @LocalPath string used in Path constructor or factory method
- holder.registerProblem(
- sourcePsi,
- DevKitBundle.message("inspections.message.nativepath.should.not.be.used.directly.constructing.path"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.MultiRouting -> {
- // This is the correct annotation, no need to report anything
+ // Check if the argument is a string literal that denotes a valid filename
+ if (arg is UInjectionHost) {
+ val stringValue = arg.evaluateToString()
+ if (stringValue != null && PathAnnotationInfo.isValidFilename(stringValue)) {
+ // If it's a valid filename, don't register any problems
+ continue
}
}
- // Check remaining arguments
- if (arguments.size > 1) {
- for (i in 1 until arguments.size) {
- val arg = arguments[i]
-
- // Check if the argument is a string literal that denotes a valid filename
- if (arg is UInjectionHost) {
- val stringValue = arg.evaluateToString()
- if (stringValue != null && PathAnnotationInfo.isValidFilename(stringValue)) {
+ // Check if the argument is a reference to a variable with a string constant initializer that denotes a valid filename
+ if (arg is UReferenceExpression) {
+ val resolved = arg.resolve()
+ if (resolved is com.intellij.psi.PsiVariable) {
+ val initializer = resolved.initializer
+ if (initializer != null) {
+ // Try to evaluate the initializer as a string constant
+ val constantValue = com.intellij.psi.JavaPsiFacade.getInstance(resolved.project)
+ .constantEvaluationHelper.computeConstantExpression(initializer)
+ if (constantValue is String && PathAnnotationInfo.isValidFilename(constantValue)) {
// If it's a valid filename, don't register any problems
continue
}
}
+ }
+ }
- // Check if the argument is a reference to a variable with a string constant initializer that denotes a valid filename
- if (arg is UReferenceExpression) {
- val resolved = arg.resolve()
- if (resolved is com.intellij.psi.PsiVariable) {
- val initializer = resolved.initializer
- if (initializer != null) {
- // Try to evaluate the initializer as a string constant
- val constantValue = com.intellij.psi.JavaPsiFacade.getInstance(resolved.project)
- .constantEvaluationHelper.computeConstantExpression(initializer)
- if (constantValue is String && PathAnnotationInfo.isValidFilename(constantValue)) {
- // If it's a valid filename, don't register any problems
- continue
- }
- }
- }
+ val argInfo = PathAnnotationInfo.forExpression(arg)
+ if (argInfo !is PathAnnotationInfo.MultiRouting && argInfo !is PathAnnotationInfo.FilenameInfo && argInfo !is PathAnnotationInfo.LocalPathInfo) {
+ // Report error: elements of 'more' parameter should be annotated with either @MultiRoutingFileSystemPath or @Filename
+ when (argInfo) {
+ is PathAnnotationInfo.Native -> {
+ if (i == 0)
+ holder.registerProblem(
+ arg.sourcePsi ?: sourcePsi,
+ DevKitBundle.message("inspections.message.nativepath.should.not.be.used.directly.constructing.path"),
+ AddMultiRoutingAnnotationFix()
+ )
}
-
- val argInfo = PathAnnotationInfo.forExpression(arg)
- if (argInfo !is PathAnnotationInfo.MultiRouting && argInfo !is PathAnnotationInfo.FilenameInfo) {
- // Report error: elements of 'more' parameter should be annotated with either @MultiRoutingFileSystemPath or @Filename
- when (argInfo) {
- is PathAnnotationInfo.Native -> {
- holder.registerProblem(
- arg.sourcePsi ?: sourcePsi,
- DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.LocalPathInfo -> {
- holder.registerProblem(
- arg.sourcePsi ?: sourcePsi,
- DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename"),
- AddMultiRoutingAnnotationFix()
- )
- }
- is PathAnnotationInfo.Unspecified -> {
- holder.registerProblem(
- arg.sourcePsi ?: sourcePsi,
- DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename"),
- AddMultiRoutingAnnotationFix()
- )
- }
- else -> {
- // This should not happen, but we need to handle all cases
- holder.registerProblem(
- arg.sourcePsi ?: sourcePsi,
- DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename")
- )
- }
- }
+ is PathAnnotationInfo.Unspecified -> {
+ holder.registerProblem(
+ arg.sourcePsi ?: sourcePsi,
+ if (i == 0)
+ DevKitBundle.message("inspections.message.first.argument.path.of.should.be.annotated.with.multiroutingfilesystempath")
+ else
+ DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename"),
+ AddMultiRoutingAnnotationFix()
+ )
+ }
+ else -> {
+ // This should not happen, but we need to handle all cases
+ holder.registerProblem(
+ arg.sourcePsi ?: sourcePsi,
+ DevKitBundle.message("inspections.message.more.parameters.in.path.of.should.be.annotated.with.multiroutingfilesystempath.or.filename")
+ )
}
}
}
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/PathAnnotationInspectionTestBase.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/PathAnnotationInspectionTestBase.kt
index 4149ad375e65..462323ff1ef4 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/PathAnnotationInspectionTestBase.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/PathAnnotationInspectionTestBase.kt
@@ -226,7 +226,7 @@ abstract class PathAnnotationInspectionTestBase : LightJavaCodeInsightFixtureTes
public void testMethod() {
// Test @LocalPath in Path.of()
@LocalPath String localPath = "/local/path";
- Path path1 = Path.of(localPath); // Warning, @LocalPath should not be used directly in Path.of()
+ Path path1 = Path.of(localPath); // @LocalPath can be used directly in Path.of()
// Test @LocalPath in Path.resolve()
@MultiRoutingFileSystemPath String basePath = "/base/path";
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionJavaTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionJavaTest.kt
index 08f18f998e31..b80bb22d2477 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionJavaTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionJavaTest.kt
@@ -52,7 +52,7 @@ class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
public void testMethod() {
@NativePath String nativePath = "/usr/local/bin";
// This should be highlighted as an error because @NativePath strings should not be used directly in Path.of()
- Path path = Path.of(nativePath);
+ Path path = Path.of(nativePath);
}
}
""".trimIndent())
@@ -95,10 +95,10 @@ class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
public void testMethod() {
String nonAnnotatedPath = "/usr/local/bin";
// This should be highlighted as a normal warning because non-annotated strings should be annotated with @MultiRoutingFileSystemPath
- Path path = Path.of(nonAnnotatedPath);
+ Path path = Path.of(nonAnnotatedPath);
// Direct string literal should also be highlighted
- Path directPath = Path.of("/another/path");
+ Path directPath = Path.of("/another/path");
}
}
""".trimIndent())
@@ -113,7 +113,7 @@ class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
public class NonAnnotatedStringInPathResolve {
public void testMethod() {
- Path basePath = Paths.get("/base/path");
+ Path basePath = Paths.get("/base/path");
String nonAnnotatedPath = "invalid/path";
// This should be highlighted as a warning because non-annotated strings should be annotated with @MultiRoutingFileSystemPath
@@ -140,7 +140,7 @@ class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
public class FilenameAnnotatedStringInPathResolve {
public void testMethod() {
- Path basePath = Paths.get("/base/path");
+ Path basePath = Paths.get("/base/path");
// Non-annotated string should be highlighted
String nonAnnotatedPath = "invalid/path";
@@ -175,9 +175,9 @@ class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
String nonAnnotatedMore = "invalid/path";
Path path1 = Path.of(basePath, nonAnnotatedMore);
- // @NativePath string in 'more' parameter should be highlighted
+ // @NativePath string in 'more' parameter should not be highlighted
@NativePath String nativeMore = "invalid/path";
- Path path2 = Path.of(basePath, nativeMore);
+ Path path2 = Path.of(basePath, nativeMore);
// @Filename string in 'more' parameter should not be highlighted
@Filename String filenameMore = "file.txt";
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionOtherPathMethodsTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionOtherPathMethodsTest.kt
index d1efe4cca480..1992b5645b4f 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionOtherPathMethodsTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionOtherPathMethodsTest.kt
@@ -1,6 +1,8 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.inspections.path
+import org.jetbrains.idea.devkit.DevKitBundle
+import org.jetbrains.idea.devkit.DevKitBundle.message
import org.jetbrains.idea.devkit.inspections.PathAnnotationInspectionTestBase
class PathAnnotationInspectionOtherPathMethodsTest : PathAnnotationInspectionTestBase() {
@@ -15,8 +17,8 @@ class PathAnnotationInspectionOtherPathMethodsTest : PathAnnotationInspectionTes
public class OtherPathMethods {
public void testMethod() {
// Create paths
- Path base = Path.of("/base/path");
- Path other = Path.of("/other/path");
+ Path base = Path.of("/base/path");
+ Path other = Path.of("/other/path");
// Test Path.relativize(Path) - this should not be highlighted
Path relativized = base.relativize(other);
@@ -46,7 +48,7 @@ class PathAnnotationInspectionOtherPathMethodsTest : PathAnnotationInspectionTes
public class MixedPathMethods {
public void testMethod() {
// Create paths
- Path base = Path.of("/base/path");
+ Path base = Path.of("/base/path");
// Test Path.resolveSibling(String) - this should be highlighted if not annotated
Path resolvedSibling1 = base.resolveSibling("other/path");
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathOfTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathOfTest.kt
index b0d4d41b412c..3eb1a0daa66b 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathOfTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathOfTest.kt
@@ -26,6 +26,11 @@ class PathAnnotationInspectionPathOfTest : PathAnnotationInspectionTestBase() {
final String validFilename1 = "file.txt";
final String validFilename2 = "another.txt";
Path path4 = Path.of(basePath, validFilename1, validFilename2);
+
+ Path path10 = Path.of("hello", "world");
+
+ final String validDirectoryName = "hello";
+ Path path11 = Path.of(validDirectoryName, "world");
}
}
""".trimIndent())
@@ -41,15 +46,15 @@ class PathAnnotationInspectionPathOfTest : PathAnnotationInspectionTestBase() {
public void testMethod() {
// First argument is NOT correctly annotated - this should be highlighted
String nonAnnotatedPath = "/base/path";
- Path path1 = Path.of(nonAnnotatedPath, "file.txt");
+ Path path1 = Path.of(nonAnnotatedPath, "file.txt");
// Even if other arguments are correctly annotated, it should still be highlighted
@Filename String filename = "file.txt";
@MultiRoutingFileSystemPath String subdir = "subdir";
- Path path2 = Path.of(nonAnnotatedPath, filename, subdir);
+ Path path2 = Path.of(nonAnnotatedPath, filename, subdir);
// Direct string literal as first argument should also be highlighted
- Path path3 = Path.of("/another/path", "file.txt");
+ Path path3 = Path.of("/another/path", "file.txt");
}
}
""".trimIndent())
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathResolvePathTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathResolvePathTest.kt
index 6082b0b7c0d5..a603d1acb915 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathResolvePathTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathResolvePathTest.kt
@@ -1,6 +1,7 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.inspections.path
+import org.jetbrains.idea.devkit.DevKitBundle.message
import org.jetbrains.idea.devkit.inspections.PathAnnotationInspectionTestBase
class PathAnnotationInspectionPathResolvePathTest : PathAnnotationInspectionTestBase() {
@@ -15,10 +16,10 @@ class PathAnnotationInspectionPathResolvePathTest : PathAnnotationInspectionTest
public class PathResolvePath {
public void testMethod() {
// Create a base path
- Path base = Path.of("/base/path");
+ Path base = Path.of("/base/path");
// Create another path
- Path otherPath = Path.of("/other/path");
+ Path otherPath = Path.of("/other/path");
// Test Path.resolve(Path) - this should not be highlighted
Path resolvedPath = base.resolve(otherPath);
@@ -28,7 +29,7 @@ class PathAnnotationInspectionPathResolvePathTest : PathAnnotationInspectionTest
}
private Path getPath() {
- return Path.of("/some/path");
+ return Path.of("/some/path");
}
}
""".trimIndent())
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathsGetTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathsGetTest.kt
index 6ec86c1c68b5..ce47f2d62b24 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathsGetTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionPathsGetTest.kt
@@ -43,15 +43,15 @@ class PathAnnotationInspectionPathsGetTest : PathAnnotationInspectionTestBase()
public void testMethod() {
// First argument is NOT correctly annotated - this should be highlighted
String nonAnnotatedPath = "/base/path";
- Path path1 = Paths.get(nonAnnotatedPath, "file.txt");
+ Path path1 = Paths.get(nonAnnotatedPath, "file.txt");
// Even if other arguments are correctly annotated, it should still be highlighted
@Filename String filename = "file.txt";
@MultiRoutingFileSystemPath String subdir = "subdir";
- Path path2 = Paths.get(nonAnnotatedPath, filename, subdir);
+ Path path2 = Paths.get(nonAnnotatedPath, filename, subdir);
// Direct string literal as first argument should also be highlighted
- Path path3 = Paths.get("/another/path", "file.txt");
+ Path path3 = Paths.get("/another/path", "file.txt");
}
}
""".trimIndent())
diff --git a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionQuickFixTest.kt b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionQuickFixTest.kt
index 70e09c0a45c9..174cc69dfd93 100644
--- a/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionQuickFixTest.kt
+++ b/plugins/devkit/devkit-tests/testSrc/org/jetbrains/idea/devkit/inspections/path/PathAnnotationInspectionQuickFixTest.kt
@@ -58,7 +58,7 @@ class PathAnnotationInspectionQuickFixTest : PathAnnotationInspectionTestBase()
public void testMethod() {
String nonAnnotatedPath = "/usr/local/bin";
// This should be highlighted as a normal warning because non-annotated strings should be annotated with @MultiRoutingFileSystemPath
- Path path = Path.of(nonAnnotatedPath);
+ Path path = Path.of(nonAnnotatedPath);
}
}
""".trimIndent())
@@ -78,7 +78,7 @@ class PathAnnotationInspectionQuickFixTest : PathAnnotationInspectionTestBase()
public void testMethod() {
String nonAnnotatedPath = "/usr/local/bin";
// This should be highlighted as a normal warning because non-annotated strings should be annotated with @MultiRoutingFileSystemPath
- Path path = Path.of(nonAnnotatedPath);
+ Path path = Path.of(nonAnnotatedPath);
}
}
"""