Added inspection for unresolved symbols, macros and labels.
This commit is contained in:
parent
a3f7ddb4f7
commit
431caf64fd
17
README.md
17
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 <InspectionName>` comment either on an
|
The plugin provides a few inspections for code analysis. An error or warning can be suppressed by placing a `; suppress <InspectionName>` comment either on an
|
||||||
end of line comment behind the statement or in a full line comment above the statement.
|
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:
|
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 for _mnemonic_
|
||||||
- Operation size _(.b,.w,.l)_ unsupported (should be _(.b,.w,.l)_)
|
- 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
|
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.
|
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
|
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`.
|
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.
|
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.
|
`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
|
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.
|
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
|
### Documentation provider
|
||||||
|
|
||||||
#### M68kSymbolDefinitionDocumentationProvider
|
#### M68kSymbolDefinitionDocumentationProvider
|
||||||
@ -139,11 +144,6 @@ make it work with JUnit 5. Feel free to use the code (in package ```de.platon42.
|
|||||||
## Private TODO list
|
## Private TODO list
|
||||||
|
|
||||||
- code completion suggestion for unresolved global labels and symbols
|
- 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
|
## 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: Files in `include` directives can be referenced and renamed/refactored.
|
||||||
- New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
- New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
||||||
- New: Added inspection suppression possibility and quickfix.
|
- New: Added inspection suppression possibility and quickfix.
|
||||||
|
- New: Added inspection for unresolved symbols, macros and labels.
|
||||||
|
|
||||||
### V0.5 (06-Aug-21)
|
### V0.5 (06-Aug-21)
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ patchPluginXml {
|
|||||||
<li>New: Files in 'include' directives can be referenced and renamed/refactored.
|
<li>New: Files in 'include' directives can be referenced and renamed/refactored.
|
||||||
<li>New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
<li>New: Code completion for local label definitions, suggesting undefined labels already referenced.
|
||||||
<li>New: Added inspection suppression possibility and quickfix.
|
<li>New: Added inspection suppression possibility and quickfix.
|
||||||
|
<li>New: Added inspection for unresolved symbols, macros and labels.
|
||||||
</ul>
|
</ul>
|
||||||
<h4>V0.5 (06-Aug-21)</h4>
|
<h4>V0.5 (06-Aug-21)</h4>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
|||||||
|
|
||||||
abstract class M68kMacroCallMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kMacroCall {
|
abstract class M68kMacroCallMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kMacroCall {
|
||||||
|
|
||||||
|
override fun getReference(): PsiReference? {
|
||||||
|
return references.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getReferences(): Array<PsiReference> {
|
override fun getReferences(): Array<PsiReference> {
|
||||||
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
|||||||
|
|
||||||
abstract class M68kPreprocessorDirectiveMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kPreprocessorDirective {
|
abstract class M68kPreprocessorDirectiveMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kPreprocessorDirective {
|
||||||
|
|
||||||
|
override fun getReference(): PsiReference? {
|
||||||
|
return references.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getReferences(): Array<PsiReference> {
|
override fun getReferences(): Array<PsiReference> {
|
||||||
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
|||||||
|
|
||||||
abstract class M68kSymbolReferenceMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolReference {
|
abstract class M68kSymbolReferenceMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kSymbolReference {
|
||||||
|
|
||||||
|
override fun getReference(): PsiReference? {
|
||||||
|
return references.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getReferences(): Array<PsiReference> {
|
override fun getReferences(): Array<PsiReference> {
|
||||||
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package de.platon42.intellij.plugins.m68k.refs
|
package de.platon42.intellij.plugins.m68k.refs
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
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.M68kStatement
|
||||||
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
|
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
|
||||||
|
|
||||||
class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantReferenceBase<M68kSymbolReference>(element, TextRange(0, element.textLength)) {
|
class M68kLocalLabelReference(element: M68kSymbolReference) :
|
||||||
|
PsiPolyVariantReferenceBase<M68kSymbolReference>(element, TextRange(0, element.textLength)),
|
||||||
|
EmptyResolveMessageProvider {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val INSTANCE = Resolver()
|
val INSTANCE = Resolver()
|
||||||
|
|
||||||
|
private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve local label ''{0}''"
|
||||||
|
|
||||||
fun findLocalLabels(element: M68kSymbolReference, predicate: (M68kLocalLabel) -> Boolean): List<M68kLocalLabel> {
|
fun findLocalLabels(element: M68kSymbolReference, predicate: (M68kLocalLabel) -> Boolean): List<M68kLocalLabel> {
|
||||||
val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!!
|
val statement = PsiTreeUtil.getStubOrPsiParentOfType(element, M68kStatement::class.java)!!
|
||||||
val results = SmartList<M68kLocalLabel>()
|
val results = SmartList<M68kLocalLabel>()
|
||||||
@ -42,6 +47,8 @@ class M68kLocalLabelReference(element: M68kSymbolReference) : PsiPolyVariantRefe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN
|
||||||
|
|
||||||
class Resolver : ResolveCache.PolyVariantResolver<M68kLocalLabelReference> {
|
class Resolver : ResolveCache.PolyVariantResolver<M68kLocalLabelReference> {
|
||||||
override fun resolve(ref: M68kLocalLabelReference, incompleteCode: Boolean): Array<ResolveResult> {
|
override fun resolve(ref: M68kLocalLabelReference, incompleteCode: Boolean): Array<ResolveResult> {
|
||||||
val refName = ref.element.symbolName
|
val refName = ref.element.symbolName
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package de.platon42.intellij.plugins.m68k.refs
|
package de.platon42.intellij.plugins.m68k.refs
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiElementResolveResult
|
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
|
import de.platon42.intellij.plugins.m68k.stubs.M68kMacroDefinitionStubIndex
|
||||||
|
|
||||||
class M68kMacroReference(element: M68kMacroCall) :
|
class M68kMacroReference(element: M68kMacroCall) :
|
||||||
PsiPolyVariantReferenceBase<M68kMacroCall>(element, TextRange(0, element.macroName.length)) {
|
PsiPolyVariantReferenceBase<M68kMacroCall>(element, TextRange(0, element.macroName.length)), EmptyResolveMessageProvider {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val INSTANCE = Resolver()
|
val INSTANCE = Resolver()
|
||||||
|
|
||||||
|
private const val UNRESOLVED_MESSAGE_PATTERN = "Cannot resolve macro ''{0}''"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUnresolvedMessagePattern() = UNRESOLVED_MESSAGE_PATTERN
|
||||||
|
|
||||||
class Resolver : ResolveCache.PolyVariantResolver<M68kMacroReference> {
|
class Resolver : ResolveCache.PolyVariantResolver<M68kMacroReference> {
|
||||||
override fun resolve(ref: M68kMacroReference, incompleteCode: Boolean): Array<ResolveResult> {
|
override fun resolve(ref: M68kMacroReference, incompleteCode: Boolean): Array<ResolveResult> {
|
||||||
val macroName = ref.element.macroName
|
val macroName = ref.element.macroName
|
||||||
|
@ -61,6 +61,9 @@
|
|||||||
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnexpectedConditionalInstructionInspection"
|
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnexpectedConditionalInstructionInspection"
|
||||||
displayName="Unaffected condition codes before conditional instruction" groupName="M68k"
|
displayName="Unaffected condition codes before conditional instruction" groupName="M68k"
|
||||||
enabledByDefault="true" level="WARNING"/>
|
enabledByDefault="true" level="WARNING"/>
|
||||||
|
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kUnresolvedReferenceInspection"
|
||||||
|
displayName="Unresolved label/symbol/macro reference" groupName="M68k"
|
||||||
|
enabledByDefault="true" level="WARNING"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
Checks if references to symbols, macros, global and local labels are resolvable.
|
||||||
|
<!-- tooltip end -->
|
||||||
|
<p>Currently, issues a weak warning for global labels or symbols as those
|
||||||
|
can be defined via macros, which are not evaluated right now</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -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'")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user