From 431caf64fd1ac07730dd41058fe3a6d0fe2e971e Mon Sep 17 00:00:00 2001 From: chrisly42 Date: Mon, 9 Aug 2021 11:26:13 +0200 Subject: [PATCH] Added inspection for unresolved symbols, macros and labels. --- README.md | 17 +++---- build.gradle | 1 + .../M68kUnresolvedReferenceInspection.kt | 48 +++++++++++++++++++ .../plugins/m68k/psi/M68kMacroCallMixin.kt | 4 ++ .../psi/M68kPreprocessorDirectiveMixin.kt | 4 ++ .../m68k/psi/M68kSymbolReferenceMixin.kt | 4 ++ .../m68k/refs/M68kLocalLabelReference.kt | 9 +++- .../plugins/m68k/refs/M68kMacroReference.kt | 7 ++- src/main/resources/META-INF/plugin.xml | 3 ++ .../M68kUnresolvedReference.html | 8 ++++ .../M68kUnresolvedReferenceInspectionTest.kt | 29 +++++++++++ 11 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspection.kt create mode 100644 src/main/resources/inspectionDescriptions/M68kUnresolvedReference.html create mode 100644 src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspectionTest.kt diff --git a/README.md b/README.md index ab505cc..cfd0238 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ good enough" to get started, and I can return to demo coding with its current st The plugin provides a few inspections for code analysis. An error or warning can be suppressed by placing a `; suppress ` comment either on an end of line comment behind the statement or in a full line comment above the statement. -#### M68kSyntaxInspection - Assembly instruction validity +#### M68kSyntax - Assembly instruction validity Checks the validity of the current instruction. If an instruction is not recognized, you may get one of the following errors: @@ -57,7 +57,7 @@ Checks the validity of the current instruction. If an instruction is not recogni - Operation size _(.b,.w,.l)_ unsupported for _mnemonic_ - Operation size _(.b,.w,.l)_ unsupported (should be _(.b,.w,.l)_) -#### M68kDeadWriteInspection - Dead writes to registers +#### M68kDeadWrite - Dead writes to registers This inspection looks at register writes and tries to find instructions that renders a write moot because it was overwritten by another instruction before anything useful was done with it. @@ -68,7 +68,7 @@ Analysis is aborted at global labels, flow control instructions, directives The inspection tries to take condition code changing into account and puts out a weak warning if the statement merely changes condition codes before the contents of the register are overwritten. In this case, it is sometimes better to replace `move` by `tst`. -#### M68kUnexpectedConditionalInstructionInspection - Unaffected condition codes before conditional instruction +#### M68kUnexpectedConditionalInstruction - Unaffected condition codes before conditional instruction Especially for novice coders, it is not clear that some instructions do not affect the condition codes for a subsequent condition branch or `scc` instruction. `movea`, `adda` and `suba` come to my mind. @@ -78,6 +78,11 @@ The inspection will report such suspicious instruction sequences. However, this does not need to be a programming error. Advanced coders sometimes make use of the fact that instructions do not change condition codes and thus optimize the order of execution. +#### M68kUnresolvedReference - Unresolved label/symbol/macro reference + +Points out unresolved references such for global and local labels, macros or symbols. Right now, missing symbol and global label references are shown only as +weak warnings as missing macro evaluation will not resolve symbols defined via `STRUCT` macros. + ### Documentation provider #### M68kSymbolDefinitionDocumentationProvider @@ -139,11 +144,6 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42. ## Private TODO list - code completion suggestion for unresolved global labels and symbols -- support `include` directive -- support `opt` directive -- suppression support via comments -- inspection: Unresolved local label -- inspection: Unresolved macro ## Changelog @@ -154,6 +154,7 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42. - New: Files in `include` directives can be referenced and renamed/refactored. - New: Code completion for local label definitions, suggesting undefined labels already referenced. - New: Added inspection suppression possibility and quickfix. +- New: Added inspection for unresolved symbols, macros and labels. ### V0.5 (06-Aug-21) diff --git a/build.gradle b/build.gradle index a9e3243..8c6fc3f 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,7 @@ patchPluginXml {
  • New: Files in 'include' directives can be referenced and renamed/refactored.
  • New: Code completion for local label definitions, suggesting undefined labels already referenced.
  • New: Added inspection suppression possibility and quickfix. +
  • New: Added inspection for unresolved symbols, macros and labels.

    V0.5 (06-Aug-21)

      diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspection.kt b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspection.kt new file mode 100644 index 0000000..63e3c3f --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspection.kt @@ -0,0 +1,48 @@ +package de.platon42.intellij.plugins.m68k.inspections + +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiPolyVariantReference +import de.platon42.intellij.plugins.m68k.psi.M68kMacroCall +import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference +import de.platon42.intellij.plugins.m68k.psi.M68kVisitor +import de.platon42.intellij.plugins.m68k.refs.M68kGlobalLabelSymbolReference + +class M68kUnresolvedReferenceInspection : AbstractBaseM68kLocalInspectionTool() { + + companion object { + private const val DISPLAY_NAME = "Unresolved label/symbol/macro reference" + } + + override fun getDisplayName() = DISPLAY_NAME + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return object : M68kVisitor() { + override fun visitMacroCall(macroCall: M68kMacroCall) { + val reference = macroCall.reference as? PsiPolyVariantReference ?: return + val resolve = reference.multiResolve(false) + if (resolve.isEmpty()) { + holder.registerProblem(reference) + } + } + + override fun visitSymbolReference(symbolReference: M68kSymbolReference) { + val references = symbolReference.references ?: return + if (references.isEmpty()) return + val resolve = references.mapNotNull { it as? PsiPolyVariantReference } + .firstNotNullOfOrNull { it.multiResolve(false).ifEmpty { null } } + if (resolve == null) { + // TODO currently, because macro invocations are not evaluated, mark missing symbols only as weak warning + val makeWeak = references.any { it is M68kGlobalLabelSymbolReference } + if (makeWeak) { + holder.registerProblem(references.first(), ProblemHighlightType.WEAK_WARNING) + } else { + holder.registerProblem(references.first()) + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kMacroCallMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kMacroCallMixin.kt index 660165d..b3e606a 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kMacroCallMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kMacroCallMixin.kt @@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry abstract class M68kMacroCallMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kMacroCall { + override fun getReference(): PsiReference? { + return references.firstOrNull() + } + override fun getReferences(): Array { return ReferenceProvidersRegistry.getReferencesFromProviders(this) } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPreprocessorDirectiveMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPreprocessorDirectiveMixin.kt index 96f0ffe..91e3632 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPreprocessorDirectiveMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kPreprocessorDirectiveMixin.kt @@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry abstract class M68kPreprocessorDirectiveMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kPreprocessorDirective { + override fun getReference(): PsiReference? { + return references.firstOrNull() + } + override fun getReferences(): Array { return ReferenceProvidersRegistry.getReferencesFromProviders(this) } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt index c58c4fd..9dea247 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/psi/M68kSymbolReferenceMixin.kt @@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry abstract class M68kSymbolReferenceMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolReference { + override fun getReference(): PsiReference? { + return references.firstOrNull() + } + override fun getReferences(): Array { return ReferenceProvidersRegistry.getReferencesFromProviders(this) } diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt index 0e0dee6..01e0788 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kLocalLabelReference.kt @@ -1,5 +1,6 @@ package de.platon42.intellij.plugins.m68k.refs +import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement @@ -14,11 +15,15 @@ import de.platon42.intellij.plugins.m68k.psi.M68kLocalLabel import de.platon42.intellij.plugins.m68k.psi.M68kStatement import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference -class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantReferenceBase(element, TextRange(0, element.textLength)) { +class M68kLocalLabelReference(element: M68kSymbolReference) : + PsiPolyVariantReferenceBase(element, TextRange(0, element.textLength)), + EmptyResolveMessageProvider { companion object { val INSTANCE = Resolver() + private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve local label ''{0}''" + fun findLocalLabels(element: M68kSymbolReference, predicate: (M68kLocalLabel) -> Boolean): List { val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!! val results = SmartList() @@ -42,6 +47,8 @@ class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantRefe } } + override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN + class Resolver : ResolveCache.PolyVariantResolver { override fun resolve(ref: M68kLocalLabelReference, incompleteCode: Boolean): Array { val refName = ref.element.symbolName diff --git a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kMacroReference.kt b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kMacroReference.kt index f358208..ff1a956 100644 --- a/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kMacroReference.kt +++ b/src/main/java/de/platon42/intellij/plugins/m68k/refs/M68kMacroReference.kt @@ -1,5 +1,6 @@ package de.platon42.intellij.plugins.m68k.refs +import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementResolveResult @@ -14,12 +15,16 @@ import de.platon42.intellij.plugins.m68k.psi.M68kMacroDefinition import de.platon42.intellij.plugins.m68k.stubs.M68kMacroDefinitionStubIndex class M68kMacroReference(element: M68kMacroCall) : - PsiPolyVariantReferenceBase(element, TextRange(0, element.macroName.length)) { + PsiPolyVariantReferenceBase(element, TextRange(0, element.macroName.length)), EmptyResolveMessageProvider { companion object { val INSTANCE = Resolver() + + private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve macro ''{0}''" } + override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN + class Resolver : ResolveCache.PolyVariantResolver { override fun resolve(ref: M68kMacroReference, incompleteCode: Boolean): Array { val macroName = ref.element.macroName diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index c4c4c83..91d6776 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -61,6 +61,9 @@ + diff --git a/src/main/resources/inspectionDescriptions/M68kUnresolvedReference.html b/src/main/resources/inspectionDescriptions/M68kUnresolvedReference.html new file mode 100644 index 0000000..1a95ea0 --- /dev/null +++ b/src/main/resources/inspectionDescriptions/M68kUnresolvedReference.html @@ -0,0 +1,8 @@ + + +Checks if references to symbols, macros, global and local labels are resolvable. + +

      Currently, issues a weak warning for global labels or symbols as those + can be defined via macros, which are not evaluated right now

      + + \ No newline at end of file diff --git a/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspectionTest.kt b/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspectionTest.kt new file mode 100644 index 0000000..c0d409f --- /dev/null +++ b/src/test/java/de/platon42/intellij/plugins/m68k/inspections/M68kUnresolvedReferenceInspectionTest.kt @@ -0,0 +1,29 @@ +package de.platon42.intellij.plugins.m68k.inspections + +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import de.platon42.intellij.jupiter.MyFixture +import org.junit.jupiter.api.Test + +internal class M68kUnresolvedReferenceInspectionTest : AbstractInspectionTest() { + + @Test + internal fun shows_warning_on_undefined_symbol_label(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java) + myFixture.configureByText("unresolvedref.asm", " bra foobar") + assertHighlightings(myFixture, 1, "Cannot resolve symbol 'foobar'") + } + + @Test + internal fun shows_warning_on_undefined_local_label(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java) + myFixture.configureByText("unresolvedref.asm", " bra .foobar") + assertHighlightings(myFixture, 1, "Cannot resolve local label '.foobar'") + } + + @Test + internal fun shows_warning_on_undefined_macro(@MyFixture myFixture: CodeInsightTestFixture) { + myFixture.enableInspections(M68kUnresolvedReferenceInspection::class.java) + myFixture.configureByText("unresolvedref.asm", " foobar") + assertHighlightings(myFixture, 1, "Cannot resolve macro 'foobar'") + } +} \ No newline at end of file