mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[path-annotations] Add PathAnnotationInspection for verifying path annotation usage
Introduced `PathAnnotationInspection` to detect incorrect usage of `@NativePath` and `@MultiRoutingFileSystemPath` annotations. Added corresponding tests and test resources to validate the inspection functionality. Updated DevKit messages and inspection metadata for integration. GitOrigin-RevId: 759c0fda9da795e0ec2f9ceab1cbd70f3ce835b7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8d6a52d3f0
commit
58fbc8f1a4
@@ -0,0 +1,28 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports incorrect usage of path annotations.
|
||||
|
||||
<p>
|
||||
This inspection detects cases where a string annotated with one path annotation is used in a context that expects a string with a different path annotation.
|
||||
It helps ensure proper usage of <code>@MultiRoutingFileSystemPath</code> and <code>@NativePath</code> annotations.
|
||||
</p>
|
||||
|
||||
<p>The inspection highlights the following issues:</p>
|
||||
<ul>
|
||||
<li>When a string annotated with <code>@NativePath</code> is used in a Path constructor or factory method</li>
|
||||
<li>When a string annotated with <code>@NativePath</code> is passed to a method parameter annotated with <code>@MultiRoutingFileSystemPath</code></li>
|
||||
<li>When a string annotated with <code>@MultiRoutingFileSystemPath</code> is passed to a method parameter annotated with <code>@NativePath</code></li>
|
||||
<li>When a string literal is used in a context that expects either annotation</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Quick fixes are provided to add the appropriate annotation where needed.
|
||||
</p>
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
This inspection helps prevent runtime errors that can occur when paths with different formats are used incorrectly.
|
||||
The <code>@MultiRoutingFileSystemPath</code> annotation is used for paths that should work across different file systems,
|
||||
while the <code>@NativePath</code> annotation is used for paths that are specific to the native file system.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -499,6 +499,13 @@
|
||||
level="ERROR"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.PotentialDeadlockInServiceInitializationInspection"/>
|
||||
|
||||
<localInspection language="UAST" shortName="PathAnnotationInspection"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
|
||||
enabledByDefault="true" level="WARNING"
|
||||
implementationClass="org.jetbrains.idea.devkit.inspections.path.PathAnnotationInspection"
|
||||
key="inspection.path.annotation.display.name"/>
|
||||
|
||||
<localInspection language="JVM"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
|
||||
|
||||
@@ -725,6 +725,8 @@ inspection.can.be.dumb.aware.settings.ignore.classes.dialog.title=Specify Class
|
||||
inspection.can.be.dumb.aware.message=Can be made DumbAware if it does not access indexes
|
||||
inspection.can.be.dumb.aware.quickfix.add.to.ignore=Ignore ''{0}''
|
||||
|
||||
inspection.path.annotation.display.name=Path annotation usage problems
|
||||
|
||||
inspections.check.return.value=Return value must be checked
|
||||
# {0} is the identifier of the inspection. It's a relatively short ASCII string.
|
||||
inspection.message.return.value.must.be.checked=[{0}] Return value must be checked
|
||||
@@ -751,3 +753,12 @@ list.item.an.action=Action
|
||||
list.item.toggle.action=Toggle action
|
||||
command.create.bundle.properties=Create Bundle .properties File
|
||||
action.create.message.bundle.title=New Message Bundle
|
||||
|
||||
inspections.path.annotation.usage.problems=Path annotation usage problems
|
||||
inspections.intention.family.name.add.multiroutingfilesystempath.annotation=Add @MultiRoutingFileSystemPath annotation
|
||||
inspections.intention.family.name.add.nativepath.annotation=Add @NativePath annotation
|
||||
inspections.message.nativepath.passed.to.multiroutingfilesystempath.method.parameter=A string annotated with @NativePath is passed to a method parameter annotated with @MultiRoutingFileSystemPath
|
||||
inspections.message.multiroutingfilesystempath.passed.to.nativepath.method.parameter=A string annotated with @MultiRoutingFileSystemPath is passed to a method parameter annotated with @NativePath
|
||||
inspections.message.nativepath.should.not.be.used.directly.constructing.path=A string annotated with @NativePath should not be used directly in Path constructor or factory method
|
||||
inspections.message.multiroutingfilesystempath.expected=String literal is used in a context that expects @MultiRoutingFileSystemPath
|
||||
inspections.message.nativepath.expected=String literal is used in a context that expects @NativePath
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
// 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 com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.eel.annotations.MultiRoutingFileSystemPath
|
||||
import com.intellij.platform.eel.annotations.NativePath
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
import com.intellij.util.ThreeState
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.idea.devkit.DevKitBundle
|
||||
import org.jetbrains.idea.devkit.inspections.DevKitUastInspectionBase
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.expressions.UInjectionHost
|
||||
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
|
||||
|
||||
/**
|
||||
* Inspection that checks for proper usage of path annotations ([MultiRoutingFileSystemPath] and [NativePath]).
|
||||
* It highlights cases where a string annotated with one path annotation is used in a context that expects a string
|
||||
* with a different path annotation.
|
||||
*/
|
||||
class PathAnnotationInspection : DevKitUastInspectionBase() {
|
||||
override fun getDisplayName(): String = DevKitBundle.message("inspections.path.annotation.usage.problems")
|
||||
override fun getGroupDisplayName(): String = "DevKit"
|
||||
override fun getShortName(): String = "PathAnnotationInspection"
|
||||
|
||||
override fun buildInternalVisitor(@NotNull holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return com.intellij.uast.UastHintedVisitorAdapter.create(
|
||||
holder.file.language,
|
||||
PathAnnotationVisitor(holder),
|
||||
arrayOf(UCallExpression::class.java, UInjectionHost::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor that checks for path annotation issues.
|
||||
*/
|
||||
private inner class PathAnnotationVisitor(
|
||||
private val holder: ProblemsHolder,
|
||||
) : AbstractUastNonRecursiveVisitor() {
|
||||
override fun visitCallExpression(node: UCallExpression): Boolean {
|
||||
val sourcePsi = node.sourcePsi ?: return true
|
||||
val target = node.resolve() ?: return true
|
||||
|
||||
// Check if the method is a Path constructor or factory method
|
||||
if (isPathConstructorOrFactory(target)) {
|
||||
// Check if the argument is annotated with @NativePath
|
||||
val arguments = node.valueArguments
|
||||
if (arguments.isNotEmpty()) {
|
||||
val arg = arguments[0]
|
||||
val argInfo = PathAnnotationInfo.forExpression(arg)
|
||||
if (argInfo 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(argInfo.getAnnotationCandidate())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the method expects a specific path annotation
|
||||
for ((index, arg) in node.valueArguments.withIndex()) {
|
||||
val parameter = getParameterForArgument(target, index) ?: continue
|
||||
val expectedInfo = PathAnnotationInfo.forModifierListOwner(parameter)
|
||||
val actualInfo = PathAnnotationInfo.forExpression(arg)
|
||||
|
||||
if (expectedInfo is PathAnnotationInfo.MultiRouting && actualInfo is PathAnnotationInfo.Native) {
|
||||
// Report error: @NativePath string passed to method expecting @MultiRoutingFileSystemPath
|
||||
holder.registerProblem(
|
||||
arg.sourcePsi ?: sourcePsi,
|
||||
DevKitBundle.message("inspections.message.nativepath.passed.to.multiroutingfilesystempath.method.parameter"),
|
||||
AddMultiRoutingAnnotationFix(actualInfo.getAnnotationCandidate())
|
||||
)
|
||||
}
|
||||
else if (expectedInfo is PathAnnotationInfo.Native && actualInfo is PathAnnotationInfo.MultiRouting) {
|
||||
// Report error: @MultiRoutingFileSystemPath string passed to method expecting @NativePath
|
||||
holder.registerProblem(
|
||||
arg.sourcePsi ?: sourcePsi,
|
||||
DevKitBundle.message("inspections.message.multiroutingfilesystempath.passed.to.nativepath.method.parameter"),
|
||||
AddNativePathAnnotationFix(actualInfo.getAnnotationCandidate())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun visitElement(node: UElement): Boolean {
|
||||
if (node is UInjectionHost) {
|
||||
val psi = node.sourcePsi
|
||||
if (psi != null) {
|
||||
visitLiteralExpression(psi, node)
|
||||
}
|
||||
}
|
||||
return super.visitElement(node)
|
||||
}
|
||||
|
||||
private fun visitLiteralExpression(sourcePsi: PsiElement, expression: UInjectionHost) {
|
||||
val stringValue = expression.evaluateToString() ?: return
|
||||
if (stringValue.isBlank()) return
|
||||
|
||||
val nonAnnotatedTargets = mutableSetOf<PsiModifierListOwner>()
|
||||
val expectedInfo = getExpectedPathAnnotationInfo(expression, nonAnnotatedTargets)
|
||||
|
||||
if (expectedInfo is PathAnnotationInfo.MultiRouting) {
|
||||
// Check if the string literal is used in a context that expects @MultiRoutingFileSystemPath
|
||||
val fixes = mutableListOf<LocalQuickFix>()
|
||||
for (target in nonAnnotatedTargets) {
|
||||
if (target is PsiModifierListOwner) {
|
||||
fixes.add(AddMultiRoutingAnnotationFix(target))
|
||||
}
|
||||
}
|
||||
if (fixes.isNotEmpty()) {
|
||||
holder.registerProblem(
|
||||
sourcePsi,
|
||||
DevKitBundle.message("inspections.message.multiroutingfilesystempath.expected"),
|
||||
*fixes.toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
else if (expectedInfo is PathAnnotationInfo.Native) {
|
||||
// Check if the string literal is used in a context that expects @NativePath
|
||||
val fixes = mutableListOf<LocalQuickFix>()
|
||||
for (target in nonAnnotatedTargets) {
|
||||
if (target is PsiModifierListOwner) {
|
||||
fixes.add(AddNativePathAnnotationFix(target))
|
||||
}
|
||||
}
|
||||
if (fixes.isNotEmpty()) {
|
||||
holder.registerProblem(
|
||||
sourcePsi,
|
||||
DevKitBundle.message("inspections.message.nativepath.expected"),
|
||||
*fixes.toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExpectedPathAnnotationInfo(
|
||||
expression: UExpression,
|
||||
nonAnnotatedTargets: MutableSet<PsiModifierListOwner>,
|
||||
): PathAnnotationInfo {
|
||||
// Check if the expression is passed to a method that expects a specific path annotation
|
||||
val parent = expression.uastParent
|
||||
if (parent is UCallExpression) {
|
||||
val method = parent.resolve() ?: return PathAnnotationInfo.Unspecified(null)
|
||||
val index = parent.valueArguments.indexOf(expression)
|
||||
if (index >= 0) {
|
||||
val parameter = getParameterForArgument(method, index) ?: return PathAnnotationInfo.Unspecified(null)
|
||||
val info = PathAnnotationInfo.forModifierListOwner(parameter)
|
||||
if (info !is PathAnnotationInfo.Unspecified) {
|
||||
return info
|
||||
}
|
||||
nonAnnotatedTargets.add(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the expression is assigned to a variable with a specific path annotation
|
||||
if (parent is UVariable) {
|
||||
val javaPsi = parent.javaPsi
|
||||
if (javaPsi is PsiModifierListOwner) {
|
||||
val info = PathAnnotationInfo.forModifierListOwner(javaPsi)
|
||||
if (info !is PathAnnotationInfo.Unspecified) {
|
||||
return info
|
||||
}
|
||||
nonAnnotatedTargets.add(javaPsi)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the expression is passed to a Path constructor or factory method
|
||||
if (isPassedToMultiRoutingMethod(expression, nonAnnotatedTargets)) {
|
||||
return PathAnnotationInfo.MultiRouting()
|
||||
}
|
||||
|
||||
// Check if the expression is passed to a method that expects a native path
|
||||
if (isPassedToNativePathMethod(expression, nonAnnotatedTargets)) {
|
||||
return PathAnnotationInfo.Native()
|
||||
}
|
||||
|
||||
return PathAnnotationInfo.Unspecified(null)
|
||||
}
|
||||
|
||||
private fun isPathConstructorOrFactory(method: PsiElement): Boolean {
|
||||
// Check if the method is a Path constructor or factory method like Path.of()
|
||||
if (method is PsiModifierListOwner) {
|
||||
val containingClass = (method as? com.intellij.psi.PsiMember)?.containingClass
|
||||
if (containingClass != null) {
|
||||
val qualifiedName = containingClass.qualifiedName
|
||||
return qualifiedName == "java.nio.file.Path" || qualifiedName == "java.nio.file.Paths"
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isPassedToMultiRoutingMethod(
|
||||
expression: UExpression,
|
||||
nonAnnotatedTargets: MutableSet<PsiModifierListOwner>,
|
||||
): Boolean {
|
||||
// Check if the expression is passed to a Path constructor or factory method
|
||||
val parent = expression.uastParent
|
||||
if (parent is UCallExpression) {
|
||||
val method = parent.resolve() ?: return false
|
||||
if (isPathConstructorOrFactory(method)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isPassedToNativePathMethod(
|
||||
expression: UExpression,
|
||||
nonAnnotatedTargets: MutableSet<PsiModifierListOwner>,
|
||||
): Boolean {
|
||||
// Check if the expression is passed to a method that expects a native path
|
||||
// This would be specific to your codebase, but could include methods like:
|
||||
// - Docker container path methods
|
||||
// - WSL path methods
|
||||
// For now, we'll just return false as a placeholder
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getParameterForArgument(method: PsiElement, index: Int): PsiModifierListOwner? {
|
||||
if (method is com.intellij.psi.PsiMethod) {
|
||||
val parameters = method.parameterList.parameters
|
||||
if (index < parameters.size) {
|
||||
return parameters[index]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains information about path annotation status.
|
||||
*/
|
||||
sealed class PathAnnotationInfo {
|
||||
abstract fun getPathAnnotationStatus(): ThreeState
|
||||
|
||||
class MultiRouting(private val annotationCandidate: PsiModifierListOwner? = null) : PathAnnotationInfo() {
|
||||
override fun getPathAnnotationStatus(): ThreeState = ThreeState.YES
|
||||
|
||||
fun getAnnotationCandidate(): PsiModifierListOwner? = annotationCandidate
|
||||
}
|
||||
|
||||
class Native(private val annotationCandidate: PsiModifierListOwner? = null) : PathAnnotationInfo() {
|
||||
override fun getPathAnnotationStatus(): ThreeState = ThreeState.YES
|
||||
|
||||
fun getAnnotationCandidate(): PsiModifierListOwner? = annotationCandidate
|
||||
}
|
||||
|
||||
class Unspecified(private val annotationCandidate: PsiModifierListOwner?) : PathAnnotationInfo() {
|
||||
override fun getPathAnnotationStatus(): ThreeState = ThreeState.UNSURE
|
||||
fun getAnnotationCandidate(): PsiModifierListOwner? = annotationCandidate
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun forExpression(expression: UExpression): PathAnnotationInfo {
|
||||
// Check if the expression has a path annotation
|
||||
val sourcePsi = expression.sourcePsi
|
||||
if (sourcePsi != null && sourcePsi is PsiModifierListOwner) {
|
||||
return forModifierListOwner(sourcePsi)
|
||||
}
|
||||
|
||||
// Check if the expression is a reference to a variable with a path annotation
|
||||
if (expression is UReferenceExpression) {
|
||||
val resolved = expression.resolve()
|
||||
if (resolved is PsiModifierListOwner) {
|
||||
return forModifierListOwner(resolved)
|
||||
}
|
||||
}
|
||||
|
||||
return Unspecified(null)
|
||||
}
|
||||
|
||||
fun forModifierListOwner(owner: PsiModifierListOwner): PathAnnotationInfo {
|
||||
// Check if the owner has a path annotation
|
||||
if (AnnotationUtil.isAnnotated(owner, MultiRoutingFileSystemPath::class.java.name, AnnotationUtil.CHECK_TYPE)) {
|
||||
return MultiRouting(owner)
|
||||
}
|
||||
if (AnnotationUtil.isAnnotated(owner, NativePath::class.java.name, AnnotationUtil.CHECK_TYPE)) {
|
||||
return Native(owner)
|
||||
}
|
||||
return Unspecified(owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick fix to add @MultiRoutingFileSystemPath annotation.
|
||||
*/
|
||||
private class AddMultiRoutingAnnotationFix(private val target: PsiModifierListOwner?) : LocalQuickFix {
|
||||
override fun getFamilyName(): String = DevKitBundle.message("inspections.intention.family.name.add.multiroutingfilesystempath.annotation")
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
if (target != null) {
|
||||
val annotationOwner = target.modifierList
|
||||
if (annotationOwner != null) {
|
||||
AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(
|
||||
MultiRoutingFileSystemPath::class.java.name,
|
||||
emptyArray(),
|
||||
annotationOwner
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick fix to add @NativePath annotation.
|
||||
*/
|
||||
private class AddNativePathAnnotationFix(private val target: PsiModifierListOwner?) : LocalQuickFix {
|
||||
override fun getFamilyName(): String = DevKitBundle.message("inspections.intention.family.name.add.nativepath.annotation")
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
if (target != null) {
|
||||
val annotationOwner = target.modifierList
|
||||
if (annotationOwner != null) {
|
||||
AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(
|
||||
NativePath::class.java.name,
|
||||
emptyArray(),
|
||||
annotationOwner
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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
|
||||
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.testFramework.IdeaTestUtil
|
||||
import com.intellij.testFramework.IndexingTestUtil
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.jetbrains.idea.devkit.inspections.path.PathAnnotationInspection
|
||||
|
||||
abstract class PathAnnotationInspectionTestBase : LightJavaCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_17
|
||||
|
||||
protected abstract fun getFileExtension(): String
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
addPlatformClasses()
|
||||
IdeaTestUtil.setProjectLanguageLevel(project, LanguageLevel.JDK_17)
|
||||
IndexingTestUtil.waitUntilIndexesAreReady(project)
|
||||
myFixture.enableInspections(PathAnnotationInspection())
|
||||
}
|
||||
|
||||
private fun addPlatformClasses() {
|
||||
// Add the path annotations
|
||||
myFixture.addClass(
|
||||
"""
|
||||
package com.intellij.platform.eel.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
/**
|
||||
* This annotation should be applied to strings that could be directly used to construct java.nio.file.Path instances.
|
||||
* These strings are either local to the IDE process or have prefix pointing to the specific environment.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({FIELD, LOCAL_VARIABLE, PARAMETER, METHOD, TYPE_USE})
|
||||
public @interface MultiRoutingFileSystemPath {}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
myFixture.addClass(
|
||||
"""
|
||||
package com.intellij.platform.eel.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
/**
|
||||
* This is the path within the specific environment.
|
||||
* For example, for a path in WSL it would be a Unix path within the WSL machine,
|
||||
* and for a path in a Docker container it would be a path within this Docker container.
|
||||
* <p>
|
||||
* It should not be directly used in the java.nio.file.Path ctor and auxiliary methods like java.nio.file.Path.of.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({FIELD, LOCAL_VARIABLE, PARAMETER, METHOD, TYPE_USE})
|
||||
public @interface NativePath {}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
// Add the NativeContext annotation
|
||||
myFixture.addClass(
|
||||
"""
|
||||
package com.intellij.platform.eel.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
/**
|
||||
* @see NativePath
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({FIELD, LOCAL_VARIABLE, PARAMETER, METHOD, TYPE_USE})
|
||||
public @interface NativeContext {}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun doTest(@Language("JAVA") code: String) {
|
||||
val filePath = getTestName(false) + '.' + getFileExtension()
|
||||
myFixture.configureByText(filePath, code.trimIndent())
|
||||
myFixture.testHighlighting(filePath)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.inspections.PathAnnotationInspectionTestBase
|
||||
|
||||
class PathAnnotationInspectionJavaTest : PathAnnotationInspectionTestBase() {
|
||||
override fun getFileExtension(): String = "java"
|
||||
|
||||
fun testNativePathInPathOf() {
|
||||
doTest("""
|
||||
import com.intellij.platform.eel.annotations.NativePath;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class NativePathInPathOf {
|
||||
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 = <warning descr="A string annotated with @NativePath should not be used directly in Path constructor or factory method">Path.of(nativePath)</warning>;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
fun testMultiRoutingPathInNativeContext() {
|
||||
doTest("""
|
||||
import com.intellij.platform.eel.annotations.MultiRoutingFileSystemPath;
|
||||
import com.intellij.platform.eel.annotations.NativePath;
|
||||
import com.intellij.platform.eel.annotations.NativeContext;
|
||||
|
||||
public class MultiRoutingPathInNativeContext {
|
||||
public void testMethod() {
|
||||
@MultiRoutingFileSystemPath String multiRoutingPath = "/home/user/documents";
|
||||
|
||||
// This method expects a @NativePath string
|
||||
processNativePath(<warning descr="A string annotated with @MultiRoutingFileSystemPath is passed to a method parameter annotated with @NativePath">multiRoutingPath</warning>);
|
||||
}
|
||||
|
||||
public void processNativePath(@NativePath String path) {
|
||||
// Process the native path
|
||||
System.out.println("Processing native path: " + path);
|
||||
}
|
||||
|
||||
@NativeContext
|
||||
public void nativeContextMethod() {
|
||||
@MultiRoutingFileSystemPath String multiRoutingPath = "/home/user/documents";
|
||||
// Using a @MultiRoutingFileSystemPath string in a @NativeContext method
|
||||
String processedPath = multiRoutingPath + "/file.txt";
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user