Add unresolved import and load inspection
This commit is contained in:
17
AGENTS.md
17
AGENTS.md
@@ -66,6 +66,7 @@ dev.hgh.jai.editor.JaiEditorSupportTest tests=7
|
|||||||
dev.hgh.jai.editor.JaiFoldingBuilderTest tests=3
|
dev.hgh.jai.editor.JaiFoldingBuilderTest tests=3
|
||||||
dev.hgh.jai.findusages.JaiFindUsagesTest tests=2
|
dev.hgh.jai.findusages.JaiFindUsagesTest tests=2
|
||||||
dev.hgh.jai.highlighting.* tests=8
|
dev.hgh.jai.highlighting.* tests=8
|
||||||
|
dev.hgh.jai.inspection.JaiUnresolvedModuleInspectionTest tests=5
|
||||||
dev.hgh.jai.lexer.JaiCorpusLexerTest tests=2 <- the Tier 0 gate
|
dev.hgh.jai.lexer.JaiCorpusLexerTest tests=2 <- the Tier 0 gate
|
||||||
dev.hgh.jai.lexer.JaiLexerTest tests=13
|
dev.hgh.jai.lexer.JaiLexerTest tests=13
|
||||||
dev.hgh.jai.parser.JaiCorpusParserTest tests=1 <- the Tier 3 gate
|
dev.hgh.jai.parser.JaiCorpusParserTest tests=1 <- the Tier 3 gate
|
||||||
@@ -78,7 +79,7 @@ dev.hgh.jai.structure.JaiStructureViewTest tests=3
|
|||||||
dev.hgh.jai.formatter.JaiFormatterTest tests=4
|
dev.hgh.jai.formatter.JaiFormatterTest tests=4
|
||||||
dev.hgh.jai.settings.JaiProjectSettingsTest tests=3
|
dev.hgh.jai.settings.JaiProjectSettingsTest tests=3
|
||||||
dev.hgh.jai.settings.JaiConfiguredRootTest tests=6
|
dev.hgh.jai.settings.JaiConfiguredRootTest tests=6
|
||||||
-> total 99, failures+errors 0
|
-> total 104, failures+errors 0
|
||||||
```
|
```
|
||||||
|
|
||||||
The corpus gates report what they actually did; check both lines are still there:
|
The corpus gates report what they actually did; check both lines are still there:
|
||||||
@@ -134,6 +135,10 @@ Tier 3: parsed 714 files, 714 clean (100.0%), 0 with errors, 0 PsiErrorElements
|
|||||||
block indentation, opaque `#asm` preservation, and a corpus-wide idempotence
|
block indentation, opaque `#asm` preservation, and a corpus-wide idempotence
|
||||||
gate. Headless formatter tests cover registration, representative formatting,
|
gate. Headless formatter tests cover registration, representative formatting,
|
||||||
opaque/directive token preservation, and all 714 corpus files.
|
opaque/directive token preservation, and all 714 corpus files.
|
||||||
|
- **Phase 7a** — unresolved `#import`/`#load` module and file path inspection,
|
||||||
|
reusing the existing reference resolver. Headless tests cover unresolved
|
||||||
|
imports and loads, resolved project/local modules and files, and
|
||||||
|
`#import,string` exclusions.
|
||||||
|
|
||||||
### Lexer design facts worth knowing before touching it
|
### Lexer design facts worth knowing before touching it
|
||||||
|
|
||||||
@@ -202,14 +207,14 @@ Tier 3: parsed 714 files, 714 clean (100.0%), 0 with errors, 0 PsiErrorElements
|
|||||||
|
|
||||||
### In progress — pick up here
|
### In progress — pick up here
|
||||||
|
|
||||||
1. **Phases 7+** — inspections, compiler integration, and other optional work
|
1. **Phases 7+** — additional inspections, quick fixes, live templates, and
|
||||||
remain. `#asm` bodies are intentionally consumed opaquely, so nothing inside
|
compiler integration remain. `#asm` bodies are intentionally consumed
|
||||||
them has PSI yet.
|
opaquely, so nothing inside them has PSI yet.
|
||||||
|
|
||||||
### Next planned increment
|
### Next planned increment
|
||||||
|
|
||||||
- Decide whether to continue with inspections and compiler integration phases
|
- Expand Phase 7 with additional high-confidence inspections and quick fixes;
|
||||||
(7–8).
|
struct-field resolution and completion remain a separate known gap.
|
||||||
|
|
||||||
### Open questions for the user (unanswered)
|
### Open questions for the user (unanswered)
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
- PSI-aware formatter with operator and punctuation spacing, block indentation,
|
- PSI-aware formatter with operator and punctuation spacing, block indentation,
|
||||||
directive-flag handling, and preservation of opaque `#asm` bodies; formatting
|
directive-flag handling, and preservation of opaque `#asm` bodies; formatting
|
||||||
is idempotent across all 714 corpus files.
|
is idempotent across all 714 corpus files.
|
||||||
|
- Inspection warnings for unresolved `#import` and `#load` module/file paths.
|
||||||
|
|
||||||
### Known gaps
|
### Known gaps
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ never from online material (see `AGENTS.md`).
|
|||||||
- PSI-aware formatting for operator and punctuation spacing, block indentation,
|
- PSI-aware formatting for operator and punctuation spacing, block indentation,
|
||||||
directive flags, and opaque `#asm` bodies; formatting is idempotent across the
|
directive flags, and opaque `#asm` bodies; formatting is idempotent across the
|
||||||
full local corpus
|
full local corpus
|
||||||
|
- Inspection warnings for unresolved `#import` and `#load` module/file paths
|
||||||
|
|
||||||
Remaining code-insight gaps include completion and symbol resolution for struct
|
Remaining code-insight gaps include completion and symbol resolution for struct
|
||||||
fields and other type-qualified members. Inspections and compiler integration
|
fields and other type-qualified members. Additional inspections and compiler
|
||||||
are planned later.
|
integration are planned later.
|
||||||
See `docs/BUILD_PLAN.md` for the phase plan.
|
See `docs/BUILD_PLAN.md` for the phase plan.
|
||||||
|
|
||||||
## Install it locally
|
## Install it locally
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ Each phase has a machine-checkable gate. Do not advance without a green gate.
|
|||||||
| **5** | Completion (keywords, directives, module names, visible declarations, parameters, module aliases, incomplete-expression recovery), rename, find-usages | Tier 4 fixture tests — **done** (84-test suite) |
|
| **5** | Completion (keywords, directives, module names, visible declarations, parameters, module aliases, incomplete-expression recovery), rename, find-usages | Tier 4 fixture tests — **done** (84-test suite) |
|
||||||
| **5a** | Configurable Jai module/import roots and indexed external search scope | Tier 4 settings, resolution, completion, navigation, and find-usages tests — **done** (93-test suite) |
|
| **5a** | Configurable Jai module/import roots and indexed external search scope | Tier 4 settings, resolution, completion, navigation, and find-usages tests — **done** (93-test suite) |
|
||||||
| **6** | Formatter, code style settings | Formatter round-trip — **done**, formatting all 714 corpus files is idempotent |
|
| **6** | Formatter, code style settings | Formatter round-trip — **done**, formatting all 714 corpus files is idempotent |
|
||||||
| **7** | Inspections (e.g. `#must` misuse), quick fixes, live templates | Tier 4 + `verifyPlugin` |
|
| **7** | Inspections, quick fixes, live templates | Tier 4 + `verifyPlugin` — initial unresolved `#import`/`#load` path inspection implemented |
|
||||||
| **8** | Optional: run-configuration to invoke the `jai` compiler, parse its error output | Integration test against `~/.local/jai/bin` |
|
| **8** | Optional: run-configuration to invoke the `jai` compiler, parse its error output | Integration test against `~/.local/jai/bin` |
|
||||||
|
|
||||||
Phase 6's idempotence check (format twice, assert no change) is another
|
Phase 6's idempotence check (format twice, assert no change) is another
|
||||||
@@ -218,6 +218,6 @@ currently a committed milestone.
|
|||||||
setup, works today), or install a system JDK via Homebrew? I'd default to the
|
setup, works today), or install a system JDK via Homebrew? I'd default to the
|
||||||
wrapper.
|
wrapper.
|
||||||
|
|
||||||
Phases 0–6 are implemented and verified headlessly. The next decision is
|
Phases 0–6 are implemented and verified headlessly. Phase 7 has an initial
|
||||||
whether to continue with the optional inspections and compiler integration work
|
unresolved `#import`/`#load` path inspection; additional inspections and
|
||||||
in Phases 7–8.
|
compiler integration remain optional follow-up work.
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package dev.hgh.jai.inspection
|
||||||
|
|
||||||
|
import com.intellij.codeInspection.LocalInspectionTool
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import com.intellij.psi.PsiReferenceService
|
||||||
|
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||||
|
import dev.hgh.jai.psi.JaiDirectiveExpr
|
||||||
|
import dev.hgh.jai.psi.JaiLiteralExpr
|
||||||
|
|
||||||
|
/** Reports file and module paths in #import/#load directives that do not resolve. */
|
||||||
|
class JaiUnresolvedModuleInspection : LocalInspectionTool() {
|
||||||
|
override fun getDisplayName(): String = "Unresolved Jai import or load"
|
||||||
|
|
||||||
|
override fun buildVisitor(
|
||||||
|
holder: ProblemsHolder,
|
||||||
|
isOnTheFly: Boolean,
|
||||||
|
): PsiElementVisitor =
|
||||||
|
object : PsiElementVisitor() {
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
val literal = element as? JaiLiteralExpr ?: return
|
||||||
|
val directive = literal.parent as? JaiDirectiveExpr ?: return
|
||||||
|
val directiveName = directive.node.findChildByType(JaiTokenTypes.DIRECTIVE)?.text ?: return
|
||||||
|
if (directiveName != "#import" && directiveName != "#load") return
|
||||||
|
|
||||||
|
val reference =
|
||||||
|
PsiReferenceService
|
||||||
|
.getService()
|
||||||
|
.getContributedReferences(literal)
|
||||||
|
.firstOrNull()
|
||||||
|
?: return
|
||||||
|
if (reference.resolve() != null) return
|
||||||
|
|
||||||
|
val path = literal.text.removeSurrounding("\"")
|
||||||
|
holder.registerProblem(
|
||||||
|
reference.element,
|
||||||
|
"Cannot resolve $directiveName path '$path'",
|
||||||
|
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL,
|
||||||
|
reference.rangeInElement,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,13 @@
|
|||||||
<psi.referenceContributor
|
<psi.referenceContributor
|
||||||
language="Jai"
|
language="Jai"
|
||||||
implementation="dev.hgh.jai.reference.JaiReferenceContributor"/>
|
implementation="dev.hgh.jai.reference.JaiReferenceContributor"/>
|
||||||
|
<localInspection
|
||||||
|
language="Jai"
|
||||||
|
displayName="Unresolved Jai import or load"
|
||||||
|
groupName="Jai"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WARNING"
|
||||||
|
implementationClass="dev.hgh.jai.inspection.JaiUnresolvedModuleInspection"/>
|
||||||
<completion.contributor
|
<completion.contributor
|
||||||
language="Jai"
|
language="Jai"
|
||||||
implementationClass="dev.hgh.jai.completion.JaiCompletionContributor"/>
|
implementationClass="dev.hgh.jai.completion.JaiCompletionContributor"/>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package dev.hgh.jai.inspection
|
||||||
|
|
||||||
|
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||||
|
|
||||||
|
class JaiUnresolvedModuleInspectionTest : BasePlatformTestCase() {
|
||||||
|
override fun setUp() {
|
||||||
|
super.setUp()
|
||||||
|
myFixture.enableInspections(JaiUnresolvedModuleInspection::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testUnresolvedLoadPathIsReported() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"main.jai",
|
||||||
|
"#load \"<warning descr=\"Cannot resolve #load path 'missing.jai'\">missing.jai</warning>\";",
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.checkHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testUnresolvedImportPathIsReported() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"main.jai",
|
||||||
|
"#import \"<warning descr=\"Cannot resolve #import path 'missing-module'\">missing-module</warning>\";",
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.checkHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testResolvedLoadPathIsNotReported() {
|
||||||
|
myFixture.addFileToProject("other.jai", "value := 1;")
|
||||||
|
myFixture.configureByText("main.jai", "#load \"other.jai\";")
|
||||||
|
|
||||||
|
myFixture.checkHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testResolvedImportPathIsNotReported() {
|
||||||
|
myFixture.configureByText("main.jai", "#import \"Basic\";")
|
||||||
|
|
||||||
|
myFixture.checkHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testImportStringIsNotReportedAsAnUnresolvedPath() {
|
||||||
|
myFixture.configureByText("main.jai", "#import,string \"inline code\";")
|
||||||
|
|
||||||
|
myFixture.checkHighlighting()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user