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.findusages.JaiFindUsagesTest tests=2
|
||||
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.JaiLexerTest tests=13
|
||||
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.settings.JaiProjectSettingsTest tests=3
|
||||
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:
|
||||
@@ -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
|
||||
gate. Headless formatter tests cover registration, representative formatting,
|
||||
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
|
||||
|
||||
@@ -202,14 +207,14 @@ Tier 3: parsed 714 files, 714 clean (100.0%), 0 with errors, 0 PsiErrorElements
|
||||
|
||||
### In progress — pick up here
|
||||
|
||||
1. **Phases 7+** — inspections, compiler integration, and other optional work
|
||||
remain. `#asm` bodies are intentionally consumed opaquely, so nothing inside
|
||||
them has PSI yet.
|
||||
1. **Phases 7+** — additional inspections, quick fixes, live templates, and
|
||||
compiler integration remain. `#asm` bodies are intentionally consumed
|
||||
opaquely, so nothing inside them has PSI yet.
|
||||
|
||||
### Next planned increment
|
||||
|
||||
- Decide whether to continue with inspections and compiler integration phases
|
||||
(7–8).
|
||||
- Expand Phase 7 with additional high-confidence inspections and quick fixes;
|
||||
struct-field resolution and completion remain a separate known gap.
|
||||
|
||||
### Open questions for the user (unanswered)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
- PSI-aware formatter with operator and punctuation spacing, block indentation,
|
||||
directive-flag handling, and preservation of opaque `#asm` bodies; formatting
|
||||
is idempotent across all 714 corpus files.
|
||||
- Inspection warnings for unresolved `#import` and `#load` module/file paths.
|
||||
|
||||
### Known gaps
|
||||
|
||||
|
||||
@@ -28,10 +28,11 @@ never from online material (see `AGENTS.md`).
|
||||
- PSI-aware formatting for operator and punctuation spacing, block indentation,
|
||||
directive flags, and opaque `#asm` bodies; formatting is idempotent across the
|
||||
full local corpus
|
||||
- Inspection warnings for unresolved `#import` and `#load` module/file paths
|
||||
|
||||
Remaining code-insight gaps include completion and symbol resolution for struct
|
||||
fields and other type-qualified members. Inspections and compiler integration
|
||||
are planned later.
|
||||
fields and other type-qualified members. Additional inspections and compiler
|
||||
integration are planned later.
|
||||
See `docs/BUILD_PLAN.md` for the phase plan.
|
||||
|
||||
## 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) |
|
||||
| **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 |
|
||||
| **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` |
|
||||
|
||||
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
|
||||
wrapper.
|
||||
|
||||
Phases 0–6 are implemented and verified headlessly. The next decision is
|
||||
whether to continue with the optional inspections and compiler integration work
|
||||
in Phases 7–8.
|
||||
Phases 0–6 are implemented and verified headlessly. Phase 7 has an initial
|
||||
unresolved `#import`/`#load` path inspection; additional inspections and
|
||||
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
|
||||
language="Jai"
|
||||
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
|
||||
language="Jai"
|
||||
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