7 Commits
Author SHA1 Message Date
chrisly42 0cb90ff8d7 'END' directive stops parsing. Prepared next release. 2021-08-09 12:14:02 +02:00
chrisly42 431caf64fd Added inspection for unresolved symbols, macros and labels. 2021-08-09 11:26:13 +02:00
chrisly42 a3f7ddb4f7 New: Added inspection suppression possibility and quickfix. 2021-08-09 10:08:41 +02:00
chrisly42 593719043e New: Code completion for local label definitions, suggesting undefined labels already referenced. 2021-08-08 13:40:01 +02:00
chrisly42 e0cdfef42b New: Files in 'include' directives can be referenced and renamed/refactored. 2021-08-06 20:03:47 +02:00
chrisly42 8d7977927f Added M68kIncludeFileProvider, but dunno what it's actually for. 2021-08-06 19:12:54 +02:00
chrisly42 ddf78ec210 Slightly reworked lexer to for a few directives:
'opt' and several other directives ('printt', 'fail' etc.) no longer causes a syntax error when unquoted.
'include', 'incdir' and 'incbin' and 'output' with '<pathname>' quotes no longer cause syntax error.
2021-08-06 10:06:48 +02:00
54 changed files with 1163 additions and 302 deletions
+22 -9
View File
@@ -39,7 +39,10 @@ good enough" to get started, and I can return to demo coding with its current st
### Inspections ### Inspections
#### M68kSyntaxInspection - Assembly instruction validity 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.
#### 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:
@@ -54,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.
@@ -65,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.
@@ -75,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
@@ -135,15 +143,20 @@ 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 local labels, 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
### V0.6 (09-Aug-21)
- Enhancement: `opt` and several other directives (`printt`, `fail` etc.) no longer causes a syntax error when unquoted.
- Enhancement: `include`, `incdir` and `incbin` and `output` with `<pathname>` quotes no longer cause syntax error.
- 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.
- Enhancement: 'END' directive stops parsing.
### V0.5 (06-Aug-21) ### V0.5 (06-Aug-21)
- Bugfix: `movem` ISA was wrong regarding the `movem.w <ea>,<registerlist>` (sign extends registers). - Bugfix: `movem` ISA was wrong regarding the `movem.w <ea>,<registerlist>` (sign extends registers).
+13 -3
View File
@@ -7,7 +7,7 @@ plugins {
} }
group = 'de.platon42' group = 'de.platon42'
version = '0.5' version = '0.6'
sourceCompatibility = "1.8" sourceCompatibility = "1.8"
targetCompatibility = "1.8" targetCompatibility = "1.8"
@@ -57,9 +57,19 @@ runPluginVerifier {
patchPluginXml { patchPluginXml {
setChangeNotes(""" setChangeNotes("""
<h4>V0.6 (09-Aug-21)</h4>
<ul>
<li>Enhancement: 'opt' and several other directives ('printt', 'fail' etc.) no longer causes a syntax error when unquoted.
<li>Enhancement: 'include', 'incdir' and 'incbin' and 'output' with '&lt;pathname&gt;' quotes no longer cause syntax error.
<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: Added inspection suppression possibility and quickfix.
<li>New: Added inspection for unresolved symbols, macros and labels.
<li>Enhancement: 'END' directive stops parsing.
</ul>
<h4>V0.5 (06-Aug-21)</h4> <h4>V0.5 (06-Aug-21)</h4>
<ul> <ul>
<li>Bugfix: movem ISA was wrong regarding movem.w <ea>,<registerlist> (sign extends registers). <li>Bugfix: movem ISA was wrong regarding movem.w &lt;ea&gt;,&lt;registerlist&gt; (sign extends registers).
<li>Cosmetics: Changed Register Flow Documentation wording from 'reads' to 'uses' and from 'modifies' to 'changes'. <li>Cosmetics: Changed Register Flow Documentation wording from 'reads' to 'uses' and from 'modifies' to 'changes'.
<li>Bugfix: Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA. <li>Bugfix: Minor fix for `andi/eori/ori to ccr` which were not byte sized in ISA.
<li>Bugfix: Added alternate condition code tests HS (=CC) and LO (=CS). <li>Bugfix: Added alternate condition code tests HS (=CC) and LO (=CS).
@@ -71,7 +81,7 @@ patchPluginXml {
</ul> </ul>
<h4>V0.4 (03-Aug-21)</h4> <h4>V0.4 (03-Aug-21)</h4>
<ul> <ul>
<li>Notice: Due to major new API use, this plugin no longer works on IDEs >=2019.3.1, but rather requires >=2020.3. <li>Notice: Due to major new API use, this plugin no longer works on IDEs &gt;=2019.3.1, but rather requires &gt;=2020.3.
<li>Enhancement: Added Structure View filters. <li>Enhancement: Added Structure View filters.
<li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes. <li>New: Added inspection to validate the correctness of a MC68000 instruction regarding operation size and address modes.
<li>Bugfix: Added several missing assembler directives (opt, machine, etc.). <li>Bugfix: Added several missing assembler directives (opt, machine, etc.).
@@ -40,7 +40,7 @@ public class _M68kLexer implements FlexLexer {
public static final int ASSIGNMENT = 12; public static final int ASSIGNMENT = 12;
public static final int EXPR = 14; public static final int EXPR = 14;
public static final int EXPR_OP = 16; public static final int EXPR_OP = 16;
public static final int MACROCALL = 18; public static final int PLAINPARAMS = 18;
public static final int WAITEOL = 20; public static final int WAITEOL = 20;
public static final int MACRODEF = 22; public static final int MACRODEF = 22;
public static final int MACROLINE = 24; public static final int MACROLINE = 24;
@@ -194,23 +194,23 @@ public class _M68kLexer implements FlexLexer {
private static final int[] ZZ_ACTION = zzUnpackAction(); private static final int[] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 = private static final String ZZ_ACTION_PACKED_0 =
"\17\0\1\1\2\2\1\3\1\4\1\1\1\5\1\6" + "\17\0\1\1\2\2\1\3\1\4\1\5\1\6\1\7" +
"\1\7\1\10\1\1\1\7\2\11\1\7\1\10\1\12" + "\1\10\1\11\1\1\1\10\2\12\1\10\1\11\1\13" +
"\1\13\1\14\1\1\1\15\1\16\1\17\4\16\1\1" + "\1\14\1\15\1\1\1\16\1\17\1\20\4\17\1\1" +
"\1\16\5\1\1\20\1\21\1\22\1\23\1\24\1\25" + "\1\17\5\1\1\21\1\22\1\23\1\24\1\25\1\26" +
"\1\26\1\27\1\30\1\31\2\16\1\32\1\33\1\34" + "\1\27\1\30\1\31\1\32\2\17\1\33\1\34\1\35" +
"\1\35\1\36\1\37\1\40\1\41\1\42\1\43\1\44" + "\1\36\1\37\1\40\1\41\1\42\1\43\1\44\1\45" +
"\1\45\1\46\1\47\1\42\1\50\2\1\1\51\1\16" + "\1\46\1\47\1\50\1\43\1\51\2\1\1\52\1\17" +
"\1\52\1\11\4\52\1\53\1\11\2\54\2\55\1\56" + "\1\53\1\12\4\53\1\54\1\12\2\55\2\56\1\57" +
"\1\57\3\60\1\57\1\60\1\0\1\4\1\61\2\62" + "\1\60\3\61\1\60\1\61\1\0\1\4\1\62\2\63" +
"\1\63\3\0\1\7\1\64\1\65\1\66\1\67\1\70" + "\1\64\3\0\1\10\1\65\1\66\1\67\1\70\1\71" +
"\1\71\1\16\1\72\1\73\1\16\1\74\1\75\2\0" + "\1\72\1\17\1\73\1\74\1\17\1\75\1\76\2\0" +
"\1\76\4\0\1\77\1\16\1\32\1\100\1\101\1\102" + "\1\77\4\0\1\100\1\17\1\33\1\101\1\102\1\103" +
"\1\103\1\104\1\105\1\106\3\0\1\52\1\0\1\52" + "\1\104\1\105\1\106\1\107\3\0\1\53\1\0\1\53" +
"\1\0\1\52\1\0\1\52\1\55\1\57\3\0\1\4" + "\1\0\1\53\1\0\1\53\1\56\1\60\3\0\1\4" +
"\1\62\1\63\1\7\1\107\1\7\1\110\1\111\1\112" + "\1\63\1\64\1\10\1\110\1\10\1\111\1\112\1\113" +
"\1\113\1\0\1\52\3\0\1\55\1\57\3\0\1\7" + "\1\114\1\0\1\53\3\0\1\56\1\60\3\0\1\10" +
"\1\55\1\57\2\0\1\114\1\115\1\116\1\0\1\117"; "\1\56\1\60\2\0\1\115\1\116\1\117\1\0\1\120";
private static int[] zzUnpackAction() { private static int[] zzUnpackAction() {
int[] result = new int[184]; int[] result = new int[184];
@@ -624,11 +624,11 @@ public class _M68kLexer implements FlexLexer {
/** /**
* Refills the input buffer. * Refills the input buffer.
* *
* @return {@code false}, iff there was new input. * @return {@code false}, iff there was NO new input.
* @throws java.io.IOException if any I/O-Error occurs * @throws java.io.IOException if any I/O-Error occurs
*/ */
private boolean zzRefill() throws java.io.IOException { private boolean zzRefill() throws java.io.IOException {
return true; return !zzAtEOF;
} }
@@ -814,14 +814,14 @@ public class _M68kLexer implements FlexLexer {
return BAD_CHARACTER; return BAD_CHARACTER;
} }
// fall through // fall through
case 80: case 81:
break; break;
case 2: { case 2: {
yybegin(YYINITIAL); yybegin(YYINITIAL);
return WHITE_SPACE; return WHITE_SPACE;
} }
// fall through // fall through
case 81: case 82:
break; break;
case 3: { case 3: {
yybegin(NOSOL); yybegin(NOSOL);
@@ -829,7 +829,7 @@ public class _M68kLexer implements FlexLexer {
return WHITE_SPACE; return WHITE_SPACE;
} }
// fall through // fall through
case 82: case 83:
break; break;
case 4: { case 4: {
yybegin(INSTRPART); yybegin(INSTRPART);
@@ -838,22 +838,30 @@ public class _M68kLexer implements FlexLexer {
return GLOBAL_LABEL_DEF; return GLOBAL_LABEL_DEF;
} }
// fall through // fall through
case 83:
break;
case 5: {
yybegin(YYINITIAL);
return COMMENT;
}
// fall through
case 84: case 84:
break; break;
case 6: { case 5: {
return WHITE_SPACE; yybegin(INSTRPART);
eatOneWhitespace = false;
return LOCAL_LABEL_DEF;
} }
// fall through // fall through
case 85: case 85:
break; break;
case 6: {
yybegin(YYINITIAL);
return COMMENT;
}
// fall through
case 86:
break;
case 7: { case 7: {
return WHITE_SPACE;
}
// fall through
case 87:
break;
case 8: {
if (isAsmMnemonicWithSize(yytext())) { if (isAsmMnemonicWithSize(yytext())) {
yybegin(ASMINSTR); yybegin(ASMINSTR);
yypushback(2); yypushback(2);
@@ -863,10 +871,19 @@ public class _M68kLexer implements FlexLexer {
yybegin(ASMINSTR); yybegin(ASMINSTR);
return MNEMONIC; return MNEMONIC;
} }
if (isEndDirective(yytext())) {
yybegin(YYINITIAL);
zzAtEOF = true;
return null;
}
if (isDataDirective(yytext())) { if (isDataDirective(yytext())) {
startExpr(EXPR, EXPR_OP); startExpr(EXPR, EXPR_OP);
return DATA_DIRECTIVE; return DATA_DIRECTIVE;
} }
if (isPlainDirective(yytext())) {
yybegin(PLAINPARAMS);
return OTHER_DIRECTIVE;
}
if (isOtherDirective(yytext())) { if (isOtherDirective(yytext())) {
startExpr(EXPR, EXPR_OP); startExpr(EXPR, EXPR_OP);
return OTHER_DIRECTIVE; return OTHER_DIRECTIVE;
@@ -874,498 +891,498 @@ public class _M68kLexer implements FlexLexer {
return handleMacroMode(this); return handleMacroMode(this);
} }
// fall through // fall through
case 86:
break;
case 8: {
return handleMacroMode(this);
}
// fall through
case 87:
break;
case 9: {
yybegin(YYINITIAL);
return EOL;
}
// fall through
case 88: case 88:
break; break;
case 10: { case 9: {
return COLON; return handleMacroMode(this);
} }
// fall through // fall through
case 89: case 89:
break; break;
case 11: { case 10: {
yybegin(WAITEOL); yybegin(YYINITIAL);
return COMMENT; return EOL;
} }
// fall through // fall through
case 90: case 90:
break; break;
case 12: { case 11: {
startExpr(ASMOPS, ASMOPS_OP); return COLON;
return WHITE_SPACE;
} }
// fall through // fall through
case 91: case 91:
break; break;
case 13: { case 12: {
return handleEolCommentWhitespace(this); yybegin(WAITEOL);
return COMMENT;
} }
// fall through // fall through
case 92: case 92:
break; break;
case 14: { case 13: {
yybegin(exprOpState); startExpr(ASMOPS, ASMOPS_OP);
return SYMBOL; return WHITE_SPACE;
} }
// fall through // fall through
case 93: case 93:
break; break;
case 15: { case 14: {
yybegin(exprOpState); return handleEolCommentWhitespace(this);
return DECIMAL;
} }
// fall through // fall through
case 94: case 94:
break; break;
case 16: { case 15: {
return SEPARATOR; yybegin(exprOpState);
return SYMBOL;
} }
// fall through // fall through
case 95: case 95:
break; break;
case 17: { case 16: {
return HASH; yybegin(exprOpState);
return DECIMAL;
} }
// fall through // fall through
case 96: case 96:
break; break;
case 18: { case 17: {
return OP_BITWISE_XOR; return SEPARATOR;
} }
// fall through // fall through
case 97: case 97:
break; break;
case 19: { case 18: {
return ROUND_L; return HASH;
} }
// fall through // fall through
case 98: case 98:
break; break;
case 20: { case 19: {
yybegin(exprOpState); return OP_BITWISE_XOR;
return ROUND_R;
} }
// fall through // fall through
case 99: case 99:
break; break;
case 21: { case 20: {
return OP_UNARY_NOT; return ROUND_L;
} }
// fall through // fall through
case 100: case 100:
break; break;
case 22: { case 21: {
return OP_UNARY_COMPL; yybegin(exprOpState);
return ROUND_R;
} }
// fall through // fall through
case 101: case 101:
break; break;
case 23: { case 22: {
return OP_PLUS; return OP_UNARY_NOT;
} }
// fall through // fall through
case 102: case 102:
break; break;
case 24: { case 23: {
return OP_MINUS; return OP_UNARY_COMPL;
} }
// fall through // fall through
case 103: case 103:
break; break;
case 25: { case 24: {
yybegin(exprOpState); return OP_PLUS;
return CURRENT_PC_SYMBOL;
} }
// fall through // fall through
case 104: case 104:
break; break;
case 26: { case 25: {
yybegin(exprState); return OP_MINUS;
return OP_CMP_EQ;
} }
// fall through // fall through
case 105: case 105:
break; break;
case 27: { case 26: {
yybegin(exprState); yybegin(exprOpState);
return OP_AR_MOD; return CURRENT_PC_SYMBOL;
} }
// fall through // fall through
case 106: case 106:
break; break;
case 28: { case 27: {
yybegin(exprState); yybegin(exprState);
return OP_CMP_LT; return OP_CMP_EQ;
} }
// fall through // fall through
case 107: case 107:
break; break;
case 29: { case 28: {
yybegin(exprState); yybegin(exprState);
return OP_CMP_GT; return OP_AR_MOD;
} }
// fall through // fall through
case 108: case 108:
break; break;
case 30: { case 29: {
yybegin(exprState); yybegin(exprState);
return SEPARATOR; return OP_CMP_LT;
} }
// fall through // fall through
case 109: case 109:
break; break;
case 31: { case 30: {
yybegin(exprState); yybegin(exprState);
return OP_BITWISE_XOR; return OP_CMP_GT;
} }
// fall through // fall through
case 110: case 110:
break; break;
case 32: { case 31: {
yybegin(exprState); yybegin(exprState);
return ROUND_L; return SEPARATOR;
} }
// fall through // fall through
case 111: case 111:
break; break;
case 33: { case 32: {
return ROUND_R; yybegin(exprState);
return OP_BITWISE_XOR;
} }
// fall through // fall through
case 112: case 112:
break; break;
case 34: { case 33: {
yybegin(exprState); yybegin(exprState);
return OP_BITWISE_OR; return ROUND_L;
} }
// fall through // fall through
case 113: case 113:
break; break;
case 35: { case 34: {
yybegin(exprState); return ROUND_R;
return OP_UNARY_COMPL;
} }
// fall through // fall through
case 114: case 114:
break; break;
case 36: { case 35: {
yybegin(exprState); yybegin(exprState);
return OP_PLUS; return OP_BITWISE_OR;
} }
// fall through // fall through
case 115: case 115:
break; break;
case 37: { case 36: {
yybegin(exprState); yybegin(exprState);
return OP_MINUS; return OP_UNARY_COMPL;
} }
// fall through // fall through
case 116: case 116:
break; break;
case 38: { case 37: {
yybegin(exprState); yybegin(exprState);
return OP_AR_MUL; return OP_PLUS;
} }
// fall through // fall through
case 117: case 117:
break; break;
case 39: { case 38: {
yybegin(exprState); yybegin(exprState);
return OP_BITWISE_AND; return OP_MINUS;
} }
// fall through // fall through
case 118: case 118:
break; break;
case 40: { case 39: {
yybegin(exprState); yybegin(exprState);
return OP_AR_DIV; return OP_AR_MUL;
} }
// fall through // fall through
case 119: case 119:
break; break;
case 41: { case 40: {
startExpr(EXPR, EXPR_OP); yybegin(exprState);
return OP_ASSIGN; return OP_BITWISE_AND;
} }
// fall through // fall through
case 120: case 120:
break; break;
case 42: { case 41: {
return STRINGLIT; yybegin(exprState);
return OP_AR_DIV;
} }
// fall through // fall through
case 121: case 121:
break; break;
case 43: { case 42: {
return COMMENT; startExpr(EXPR, EXPR_OP);
return OP_ASSIGN;
} }
// fall through // fall through
case 122: case 122:
break; break;
case 43: {
return STRINGLIT;
}
// fall through
case 123:
break;
case 44: { case 44: {
return COMMENT;
}
// fall through
case 124:
break;
case 45: {
yybegin(MACROLINE); yybegin(MACROLINE);
macroLines = 0; macroLines = 0;
return WHITE_SPACE; return WHITE_SPACE;
} }
// fall through // fall through
case 123:
break;
case 45: {
return MACRO_NAME;
}
// fall through
case 124:
break;
case 46: {
yybegin(MACROWAITEOL);
return COMMENT;
}
// fall through
case 125: case 125:
break; break;
case 47: { case 46: {
return MACRO_LINE; return MACRO_NAME;
} }
// fall through // fall through
case 126: case 126:
break; break;
case 48: { case 47: {
return handleMacroLineEol(this); yybegin(MACROWAITEOL);
return COMMENT;
} }
// fall through // fall through
case 127: case 127:
break; break;
case 48: {
return MACRO_LINE;
}
// fall through
case 128:
break;
case 49: { case 49: {
return handleMacroLineEol(this);
}
// fall through
case 129:
break;
case 50: {
yybegin(ASSIGNMENT); yybegin(ASSIGNMENT);
eatOneWhitespace = true; eatOneWhitespace = true;
yypushback(pushbackAssignment(yytext())); yypushback(pushbackAssignment(yytext()));
return SYMBOLDEF; return SYMBOLDEF;
} }
// fall through // fall through
case 128: case 130:
break; break;
case 50: { case 51: {
yybegin(INSTRPART); yybegin(INSTRPART);
eatOneWhitespace = false; eatOneWhitespace = false;
yypushback(pushbackLabelColons(yytext())); yypushback(pushbackLabelColons(yytext()));
return LOCAL_LABEL_DEF; return LOCAL_LABEL_DEF;
} }
// fall through // fall through
case 129: case 131:
break; break;
case 51: { case 52: {
yybegin(INSTRPART); yybegin(INSTRPART);
yypushback(pushbackLabelColons(yytext())); yypushback(pushbackLabelColons(yytext()));
return GLOBAL_LABEL_DEF; return GLOBAL_LABEL_DEF;
} }
// fall through // fall through
case 130:
break;
case 52: {
return OPSIZE_BS;
}
// fall through
case 131:
break;
case 53: {
return OPSIZE_W;
}
// fall through
case 132: case 132:
break; break;
case 54: { case 53: {
return OPSIZE_L; return OPSIZE_BS;
} }
// fall through // fall through
case 133: case 133:
break; break;
case 55: { case 54: {
yybegin(exprOpState); return OPSIZE_W;
return AREG;
} }
// fall through // fall through
case 134: case 134:
break; break;
case 56: { case 55: {
yybegin(exprOpState); return OPSIZE_L;
return REG_SP;
} }
// fall through // fall through
case 135: case 135:
break; break;
case 57: { case 56: {
yybegin(exprOpState); yybegin(exprOpState);
return DREG; return AREG;
} }
// fall through // fall through
case 136: case 136:
break; break;
case 58: { case 57: {
yybegin(exprOpState); yybegin(exprOpState);
return REG_SR; return REG_SP;
} }
// fall through // fall through
case 137: case 137:
break; break;
case 59: { case 58: {
yybegin(exprOpState); yybegin(exprOpState);
return HEXADECIMAL; return DREG;
} }
// fall through // fall through
case 138: case 138:
break; break;
case 60: { case 59: {
yybegin(exprOpState); yybegin(exprOpState);
return BINARY; return REG_SR;
} }
// fall through // fall through
case 139: case 139:
break; break;
case 61: { case 60: {
yybegin(exprOpState); yybegin(exprOpState);
return OCTAL; return HEXADECIMAL;
} }
// fall through // fall through
case 140: case 140:
break; break;
case 62: { case 61: {
yybegin(exprOpState); yybegin(exprOpState);
return STRINGLIT; return BINARY;
} }
// fall through // fall through
case 141: case 141:
break; break;
case 63: { case 62: {
yybegin(exprOpState); yybegin(exprOpState);
return PC; return OCTAL;
} }
// fall through // fall through
case 142: case 142:
break; break;
case 64: { case 63: {
yybegin(exprState); yybegin(exprOpState);
return OP_CMP_LT_EQ; return STRINGLIT;
} }
// fall through // fall through
case 143: case 143:
break; break;
case 65: { case 64: {
yybegin(exprState); yybegin(exprOpState);
return OP_AR_SHIFT_L; return PC;
} }
// fall through // fall through
case 144: case 144:
break; break;
case 66: { case 65: {
yybegin(exprState); yybegin(exprState);
return OP_CMP_NOT_EQ; return OP_CMP_LT_EQ;
} }
// fall through // fall through
case 145: case 145:
break; break;
case 67: { case 66: {
yybegin(exprState); yybegin(exprState);
return OP_CMP_GT_EQ; return OP_AR_SHIFT_L;
} }
// fall through // fall through
case 146: case 146:
break; break;
case 68: { case 67: {
yybegin(exprState); yybegin(exprState);
return OP_AR_SHIFT_R; return OP_CMP_NOT_EQ;
} }
// fall through // fall through
case 147: case 147:
break; break;
case 69: { case 68: {
yybegin(exprState); yybegin(exprState);
return OP_LOGICAL_AND; return OP_CMP_GT_EQ;
} }
// fall through // fall through
case 148: case 148:
break; break;
case 70: { case 69: {
yybegin(exprState); yybegin(exprState);
return OP_LOGICAL_OR; return OP_AR_SHIFT_R;
} }
// fall through // fall through
case 149: case 149:
break; break;
case 70: {
yybegin(exprState);
return OP_LOGICAL_AND;
}
// fall through
case 150:
break;
case 71: { case 71: {
yybegin(exprState);
return OP_LOGICAL_OR;
}
// fall through
case 151:
break;
case 72: {
yybegin(INSTRPART); yybegin(INSTRPART);
yypushback(pushbackLabelColons(yytext())); yypushback(pushbackLabelColons(yytext()));
return LOCAL_LABEL_DEF; return LOCAL_LABEL_DEF;
} }
// fall through // fall through
case 150: case 152:
break; break;
case 72: { case 73: {
yybegin(exprOpState); yybegin(exprOpState);
return REG_USP; return REG_USP;
} }
// fall through // fall through
case 151: case 153:
break; break;
case 73: { case 74: {
yybegin(exprOpState); yybegin(exprOpState);
return REG_CCR; return REG_CCR;
} }
// fall through // fall through
case 152: case 154:
break; break;
case 74: { case 75: {
yybegin(exprOpState); yybegin(exprOpState);
return REG_VBR; return REG_VBR;
} }
// fall through // fall through
case 153: case 155:
break; break;
case 75: { case 76: {
startExpr(EXPR, EXPR_OP); startExpr(EXPR, EXPR_OP);
return EQU; return EQU;
} }
// fall through // fall through
case 154: case 156:
break; break;
case 76: { case 77: {
yybegin(MACRODEF); yybegin(MACRODEF);
return MACRO_TAG; return MACRO_TAG;
} }
// fall through // fall through
case 155: case 157:
break; break;
case 77: { case 78: {
return MACRO_TAG; return MACRO_TAG;
} }
// fall through // fall through
case 156: case 158:
break; break;
case 78: { case 79: {
yybegin(MACROTERMINATION); yybegin(MACROTERMINATION);
return MACRO_END_TAG; return MACRO_END_TAG;
} }
// fall through // fall through
case 157: case 159:
break; break;
case 79: { case 80: {
yybegin(MACRODEF); yybegin(MACRODEF);
yypushback(pushbackAfterFirstToken(yytext())); yypushback(pushbackAfterFirstToken(yytext()));
return MACRO_NAME; return MACRO_NAME;
} }
// fall through // fall through
case 158: case 160:
break; break;
default: default:
zzScanError(ZZ_NO_MATCH); zzScanError(ZZ_NO_MATCH);
@@ -817,14 +817,13 @@ public class M68kParser implements PsiParser, LightPsiParser {
} }
/* ********************************************************** */ /* ********************************************************** */
// Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE) // Label? PreprocessorKeyword PreprocessorOperands?
// PreprocessorOperands?
public static boolean PreprocessorDirective(PsiBuilder b, int l) { public static boolean PreprocessorDirective(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective")) return false; if (!recursion_guard_(b, l, "PreprocessorDirective")) return false;
boolean r; boolean r;
Marker m = enter_section_(b, l, _NONE_, PREPROCESSOR_DIRECTIVE, "<preprocessor directive>"); Marker m = enter_section_(b, l, _NONE_, PREPROCESSOR_DIRECTIVE, "<preprocessor directive>");
r = PreprocessorDirective_0(b, l + 1); r = PreprocessorDirective_0(b, l + 1);
r = r && PreprocessorDirective_1(b, l + 1); r = r && PreprocessorKeyword(b, l + 1);
r = r && PreprocessorDirective_2(b, l + 1); r = r && PreprocessorDirective_2(b, l + 1);
exit_section_(b, l, m, r, false, null); exit_section_(b, l, m, r, false, null);
return r; return r;
@@ -837,15 +836,6 @@ public class M68kParser implements PsiParser, LightPsiParser {
return true; return true;
} }
// DATA_DIRECTIVE | OTHER_DIRECTIVE
private static boolean PreprocessorDirective_1(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective_1")) return false;
boolean r;
r = consumeToken(b, DATA_DIRECTIVE);
if (!r) r = consumeToken(b, OTHER_DIRECTIVE);
return r;
}
// PreprocessorOperands? // PreprocessorOperands?
private static boolean PreprocessorDirective_2(PsiBuilder b, int l) { private static boolean PreprocessorDirective_2(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorDirective_2")) return false; if (!recursion_guard_(b, l, "PreprocessorDirective_2")) return false;
@@ -853,6 +843,19 @@ public class M68kParser implements PsiParser, LightPsiParser {
return true; return true;
} }
/* ********************************************************** */
// DATA_DIRECTIVE | OTHER_DIRECTIVE
public static boolean PreprocessorKeyword(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "PreprocessorKeyword")) return false;
if (!nextTokenIs(b, "<preprocessor keyword>", DATA_DIRECTIVE, OTHER_DIRECTIVE)) return false;
boolean r;
Marker m = enter_section_(b, l, _NONE_, PREPROCESSOR_KEYWORD, "<preprocessor keyword>");
r = consumeToken(b, DATA_DIRECTIVE);
if (!r) r = consumeToken(b, OTHER_DIRECTIVE);
exit_section_(b, l, m, r, false, null);
return r;
}
/* ********************************************************** */ /* ********************************************************** */
// expr // expr
static boolean PreprocessorOperand(PsiBuilder b, int l) { static boolean PreprocessorOperand(PsiBuilder b, int l) {
@@ -1236,11 +1239,21 @@ public class M68kParser implements PsiParser, LightPsiParser {
if (!recursion_guard_(b, l, "statement_recover")) return false; if (!recursion_guard_(b, l, "statement_recover")) return false;
boolean r; boolean r;
Marker m = enter_section_(b, l, _NOT_); Marker m = enter_section_(b, l, _NOT_);
r = !consumeToken(b, EOL); r = !statement_recover_0(b, l + 1);
exit_section_(b, l, m, r, false, null); exit_section_(b, l, m, r, false, null);
return r; return r;
} }
// (EOL)
private static boolean statement_recover_0(PsiBuilder b, int l) {
if (!recursion_guard_(b, l, "statement_recover_0")) return false;
boolean r;
Marker m = enter_section_(b);
r = consumeTokenFast(b, EOL);
exit_section_(b, m, null, r);
return r;
}
/* ********************************************************** */ /* ********************************************************** */
// Expression root: expr // Expression root: expr
// Operator priority table: // Operator priority table:
@@ -14,6 +14,9 @@ public interface M68kPreprocessorDirective extends M68kPsiElement {
@Nullable @Nullable
M68kLocalLabel getLocalLabel(); M68kLocalLabel getLocalLabel();
@NotNull
M68kPreprocessorKeyword getPreprocessorKeyword();
@NotNull @NotNull
List<M68kExpr> getExprList(); List<M68kExpr> getExprList();
@@ -0,0 +1,6 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi;
public interface M68kPreprocessorKeyword extends M68kPsiElement {
}
@@ -58,6 +58,7 @@ public interface M68kTypes {
IElementType OPERAND_SIZE = new M68kElementType("OPERAND_SIZE"); IElementType OPERAND_SIZE = new M68kElementType("OPERAND_SIZE");
IElementType PAREN_EXPR = new M68kElementType("PAREN_EXPR"); IElementType PAREN_EXPR = new M68kElementType("PAREN_EXPR");
IElementType PREPROCESSOR_DIRECTIVE = new M68kElementType("PREPROCESSOR_DIRECTIVE"); IElementType PREPROCESSOR_DIRECTIVE = new M68kElementType("PREPROCESSOR_DIRECTIVE");
IElementType PREPROCESSOR_KEYWORD = new M68kElementType("PREPROCESSOR_KEYWORD");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE"); IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE"); IElementType PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE");
IElementType PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE"); IElementType PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE = new M68kElementType("PROGRAM_COUNTER_INDIRECT_WITH_INDEX_NEW_ADDRESSING_MODE");
@@ -233,6 +234,8 @@ public interface M68kTypes {
return new M68kParenExprImpl(node); return new M68kParenExprImpl(node);
} else if (type == PREPROCESSOR_DIRECTIVE) { } else if (type == PREPROCESSOR_DIRECTIVE) {
return new M68kPreprocessorDirectiveImpl(node); return new M68kPreprocessorDirectiveImpl(node);
} else if (type == PREPROCESSOR_KEYWORD) {
return new M68kPreprocessorKeywordImpl(node);
} else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE) { } else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_NEW_ADDRESSING_MODE) {
return new M68kProgramCounterIndirectWithDisplacementNewAddressingModeImpl(node); return new M68kProgramCounterIndirectWithDisplacementNewAddressingModeImpl(node);
} else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE) { } else if (type == PROGRAM_COUNTER_INDIRECT_WITH_DISPLACEMENT_OLD_ADDRESSING_MODE) {
@@ -131,6 +131,10 @@ public class M68kVisitor extends PsiElementVisitor {
visitPsiElement(o); visitPsiElement(o);
} }
public void visitPreprocessorKeyword(@NotNull M68kPreprocessorKeyword o) {
visitPsiElement(o);
}
public void visitProgramCounterIndirectWithDisplacementNewAddressingMode(@NotNull M68kProgramCounterIndirectWithDisplacementNewAddressingMode o) { public void visitProgramCounterIndirectWithDisplacementNewAddressingMode(@NotNull M68kProgramCounterIndirectWithDisplacementNewAddressingMode o) {
visitAddressingMode(o); visitAddressingMode(o);
// visitWithDisplacement(o); // visitWithDisplacement(o);
@@ -1,7 +1,6 @@
// This is a generated file. Not intended for manual editing. // This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi.impl; package de.platon42.intellij.plugins.m68k.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
@@ -11,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
public class M68kPreprocessorDirectiveImpl extends ASTWrapperPsiElement implements M68kPreprocessorDirective { public class M68kPreprocessorDirectiveImpl extends M68kPreprocessorDirectiveMixin implements M68kPreprocessorDirective {
public M68kPreprocessorDirectiveImpl(@NotNull ASTNode node) { public M68kPreprocessorDirectiveImpl(@NotNull ASTNode node) {
super(node); super(node);
@@ -39,6 +38,12 @@ public class M68kPreprocessorDirectiveImpl extends ASTWrapperPsiElement implemen
return PsiTreeUtil.getChildOfType(this, M68kLocalLabel.class); return PsiTreeUtil.getChildOfType(this, M68kLocalLabel.class);
} }
@Override
@NotNull
public M68kPreprocessorKeyword getPreprocessorKeyword() {
return notNullChild(PsiTreeUtil.getChildOfType(this, M68kPreprocessorKeyword.class));
}
@Override @Override
@NotNull @NotNull
public List<M68kExpr> getExprList() { public List<M68kExpr> getExprList() {
@@ -0,0 +1,27 @@
// This is a generated file. Not intended for manual editing.
package de.platon42.intellij.plugins.m68k.psi.impl;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorKeyword;
import de.platon42.intellij.plugins.m68k.psi.M68kVisitor;
import org.jetbrains.annotations.NotNull;
public class M68kPreprocessorKeywordImpl extends ASTWrapperPsiElement implements M68kPreprocessorKeyword {
public M68kPreprocessorKeywordImpl(@NotNull ASTNode node) {
super(node);
}
public void accept(@NotNull M68kVisitor visitor) {
visitor.visitPreprocessorKeyword(this);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof M68kVisitor) accept((M68kVisitor) visitor);
else super.accept(visitor);
}
}
@@ -10,7 +10,7 @@ class M68kFileElementType private constructor() : ILightStubFileElementType<PsiF
@JvmField @JvmField
val INSTANCE = M68kFileElementType() val INSTANCE = M68kFileElementType()
const val STUB_VERSION = 5 const val STUB_VERSION = 6
const val STUB_EXTERNAL_ID_PREFIX = "MC68000." const val STUB_EXTERNAL_ID_PREFIX = "MC68000."
const val EXTERNAL_ID = STUB_EXTERNAL_ID_PREFIX + "FILE" const val EXTERNAL_ID = STUB_EXTERNAL_ID_PREFIX + "FILE"
} }
@@ -22,7 +22,15 @@ object AssemblerDirectives {
"rsset", "clrfo", "clrso", "setfo", "setso" "rsset", "clrfo", "clrso", "setfo", "setso"
) )
val otherDirective: Set<String> = setOf( val plainDirectives: Set<String> = setOf(
"incdir", "include", "incbin", "output", "idnt",
"printt", "echo", "fail",
"opt"
)
val otherDirectives: Set<String> = setOf(
"if", "if",
"ifeq", "ifne", "ifgt", "ifge", "iflt", "ifle", "ifb", "ifnb", "ifc", "ifnc", "ifeq", "ifne", "ifgt", "ifge", "iflt", "ifle", "ifb", "ifnb", "ifc", "ifnc",
"ifd", "ifnd", "ifmacrod", "ifmacrond", "ifd", "ifnd", "ifmacrod", "ifmacrond",
@@ -38,12 +46,10 @@ object AssemblerDirectives {
"reg", "equr", "equrl", "reg", "equr", "equrl",
"freg", "fequr", "fequrl", "freg", "fequr", "fequrl",
"incdir", "include", "incbin", "output",
"list", "nlist", "nolist", "llen", "nopage", "page", "spc", "list", "nlist", "nolist", "llen", "nopage", "page", "spc",
"org", "org",
"assert", "fail", "print", "printt", "printv", "echo", "assert", "printv",
"inline", "einline", "inline", "einline",
"rem", "erem", "rem", "erem",
@@ -53,6 +59,6 @@ object AssemblerDirectives {
"basereg", "endb", "far", "near", "initnear", "basereg", "endb", "far", "near", "initnear",
"opt" "end"
) )
} }
@@ -79,18 +79,12 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
) )
backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement, linesLimit) { backtrace.addAll(analyseFlow(register, missingBits, true, initialStatement, linesLimit) {
PsiTreeUtil.getPrevSiblingOfType( PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java)
it,
M68kStatement::class.java
)
}) })
backtrace.reverse() backtrace.reverse()
val traceBits = (cursorRwm or (cursorRwm ushr RWM_MODIFY_SHIFT) or (cursorRwm ushr RWM_READ_SHIFT)) and RWM_SIZE_MASK val traceBits = (cursorRwm or (cursorRwm ushr RWM_MODIFY_SHIFT) or (cursorRwm ushr RWM_READ_SHIFT)) and RWM_SIZE_MASK
backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) { backtrace.addAll(analyseFlow(register, traceBits, false, initialStatement, linesLimit) {
PsiTreeUtil.getNextSiblingOfType( PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java)
it,
M68kStatement::class.java
)
}) })
val statementRows = HtmlBuilder() val statementRows = HtmlBuilder()
@@ -113,7 +107,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
var currStatement = startingStatement var currStatement = startingStatement
val statementLines = ArrayList<HtmlChunk>() val statementLines = ArrayList<HtmlChunk>()
val rn = register.regname val rn = register.regname
var addAbrevDots = false var addAbbrevDots = false
var lines = 0 var lines = 0
while (missingBits > 0) { while (missingBits > 0) {
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java) val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
@@ -127,17 +121,17 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
currStatement = direction.invoke(currStatement) ?: break currStatement = direction.invoke(currStatement) ?: break
val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue val currAsmInstruction = PsiTreeUtil.getChildOfType(currStatement, M68kAsmInstruction::class.java) ?: continue
if (checkIfInstructionUsesRegister(currAsmInstruction, register)) { if (checkIfInstructionUsesRegister(currAsmInstruction, register)) {
if (addAbrevDots) { if (addAbbrevDots) {
++lines ++lines
statementLines.add(createAbbreviationDots()) statementLines.add(createAbbreviationDots())
} }
if (++lines > linesLimit) { if (++lines > linesLimit) {
if (!addAbrevDots) { if (!addAbbrevDots) {
statementLines.add(createAbbreviationDots()) statementLines.add(createAbbreviationDots())
} }
break break
} }
addAbrevDots = false addAbbrevDots = false
val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue val (_, currAdrMode) = findExactIsaDataAndAllowedAdrModeForInstruction(currAsmInstruction) ?: continue
val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "        " val localLabelName = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)?.name ?: "        "
@@ -163,7 +157,7 @@ class M68kRegisterFlowDocumentationProvider : AbstractDocumentationProvider() {
.children(lineBuilder.wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL)) .children(lineBuilder.wrapWith(DocumentationMarkup.SECTION_CONTENT_CELL))
) )
} else { } else {
addAbrevDots = true addAbbrevDots = true
} }
} }
return statementLines return statementLines
@@ -0,0 +1,83 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInspection.InspectionSuppressor
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.SuppressQuickFix
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.m68k.psi.M68kPsiElement
import de.platon42.intellij.plugins.m68k.psi.M68kStatement
class M68kInspectionSuppressor : InspectionSuppressor {
companion object {
const val MARKER = "suppress "
}
override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean {
if (element !is M68kPsiElement) return false
val statement = PsiTreeUtil.getParentOfType(element, M68kStatement::class.java) ?: return false
val nextToken = PsiTreeUtil.skipWhitespacesForward(statement)
return isSuppressedWithComment(nextToken, toolId) || isSuppressedWithComment(PsiTreeUtil.skipWhitespacesBackward(statement), toolId)
}
override fun getSuppressActions(element: PsiElement?, toolId: String): Array<SuppressQuickFix> {
return arrayOf(LineSuppressQuickFix(toolId))
}
private fun isSuppressedWithComment(nextToken: PsiElement?, toolId: String): Boolean {
return if (nextToken is PsiComment) {
val comment = nextToken.text
comment.contains(MARKER) && comment.contains(toolId)
} else {
false
}
}
class LineSuppressQuickFix(private val toolId: String) : SuppressQuickFix {
override fun getFamilyName() = "Suppress for statement"
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val statement = PsiTreeUtil.getParentOfType(descriptor.startElement, M68kStatement::class.java) ?: return
val document = PsiDocumentManager.getInstance(project).getDocument(statement.containingFile) ?: return
val lineNumber = document.getLineNumber(statement.textOffset)
var usePrevLine = false
val nextToken = PsiTreeUtil.skipWhitespacesForward(statement)
if (nextToken is PsiComment) {
val comment = nextToken.text
if (comment.contains(MARKER)) {
if (!comment.contains(toolId)) {
document.insertString(document.getLineEndOffset(lineNumber), " $toolId")
}
return
} else {
usePrevLine = true
}
}
val prevToken = PsiTreeUtil.skipWhitespacesBackward(statement)
if (prevToken is PsiComment) {
val comment = prevToken.text
if (comment.contains(MARKER)) {
if (!comment.contains(toolId)) {
document.insertString(document.getLineEndOffset(document.getLineNumber(prevToken.textOffset)), " $toolId")
}
return
}
}
if (usePrevLine) {
document.insertString(document.getLineStartOffset(lineNumber), "; $MARKER$toolId\n")
} else {
document.insertString(document.getLineEndOffset(lineNumber), "\t; $MARKER$toolId")
}
}
override fun isAvailable(project: Project, context: PsiElement) = true
override fun isSuppressAll() = false
}
}
@@ -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())
}
}
}
}
}
}
@@ -19,11 +19,17 @@ object LexerUtil {
&& text.dropLast(1).endsWith('.') && text.dropLast(1).endsWith('.')
&& mnemonics.contains(text.dropLast(2).toString().lowercase()) && mnemonics.contains(text.dropLast(2).toString().lowercase())
@JvmStatic
fun isEndDirective(text: CharSequence) = text.contentEquals("end", ignoreCase = true)
@JvmStatic @JvmStatic
fun isDataDirective(text: CharSequence) = AssemblerDirectives.dataDirectives.contains(text.toString().lowercase()) fun isDataDirective(text: CharSequence) = AssemblerDirectives.dataDirectives.contains(text.toString().lowercase())
@JvmStatic @JvmStatic
fun isOtherDirective(text: CharSequence) = AssemblerDirectives.otherDirective.contains(text.toString().lowercase()) fun isPlainDirective(text: CharSequence) = AssemblerDirectives.plainDirectives.contains(text.toString().lowercase())
@JvmStatic
fun isOtherDirective(text: CharSequence) = AssemblerDirectives.otherDirectives.contains(text.toString().lowercase())
@JvmStatic @JvmStatic
fun pushbackAssignment(text: CharSequence): Int { fun pushbackAssignment(text: CharSequence): Int {
@@ -52,7 +58,7 @@ object LexerUtil {
@JvmStatic @JvmStatic
fun handleMacroMode(lexer: _M68kLexer): IElementType { fun handleMacroMode(lexer: _M68kLexer): IElementType {
if (lexer.lexerPrefs.macroParametersUnparsed) { if (lexer.lexerPrefs.macroParametersUnparsed) {
lexer.yybegin(_M68kLexer.MACROCALL) lexer.yybegin(_M68kLexer.PLAINPARAMS)
} else { } else {
lexer.yybegin(_M68kLexer.ASMOPS) lexer.yybegin(_M68kLexer.ASMOPS)
} }
@@ -69,4 +75,13 @@ object LexerUtil {
lexer.yybegin(_M68kLexer.MACROLINE) lexer.yybegin(_M68kLexer.MACROLINE)
return TokenType.WHITE_SPACE return TokenType.WHITE_SPACE
} }
fun unquoteString(string: String) = string.run {
when {
startsWith('"') -> removeSurrounding("\"")
startsWith('\'') -> removeSurrounding("'")
startsWith('<') -> removeSurrounding("<", ">")
else -> this
}
}
} }
@@ -72,7 +72,7 @@ HASH_COMMENT=([#;*].*+)
SKIP_TO_EOL=[^\r\n]+ SKIP_TO_EOL=[^\r\n]+
PLAIN_MACRO_LINE=[^;\r\n]+ PLAIN_MACRO_LINE=[^;\r\n]+
%state NOSOL,INSTRPART,ASMINSTR,ASMOPS,ASMOPS_OP,ASSIGNMENT,EXPR,EXPR_OP,MACROCALL,WAITEOL,MACRODEF,MACROLINE,MACROTERMINATION,MACROWAITEOL %state NOSOL,INSTRPART,ASMINSTR,ASMOPS,ASMOPS_OP,ASSIGNMENT,EXPR,EXPR_OP,PLAINPARAMS,WAITEOL,MACRODEF,MACROLINE,MACROTERMINATION,MACROWAITEOL
%% %%
<YYINITIAL, NOSOL> <YYINITIAL, NOSOL>
@@ -81,7 +81,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{HASH_COMMENT} { yybegin(YYINITIAL); return COMMENT; } {HASH_COMMENT} { yybegin(YYINITIAL); return COMMENT; }
} }
<INSTRPART,ASMINSTR,MACROCALL,ASSIGNMENT,EXPR,EXPR_OP,ASMOPS,ASMOPS_OP> <INSTRPART,ASMINSTR,PLAINPARAMS,ASSIGNMENT,EXPR,EXPR_OP,ASMOPS,ASMOPS_OP>
{ {
{EOL} { yybegin(YYINITIAL); return EOL; } {EOL} { yybegin(YYINITIAL); return EOL; }
{COMMENT} { yybegin(WAITEOL); return COMMENT; } {COMMENT} { yybegin(WAITEOL); return COMMENT; }
@@ -92,7 +92,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{WHITE_SPACE} { return WHITE_SPACE; } {WHITE_SPACE} { return WHITE_SPACE; }
} }
<MACROCALL, EXPR, EXPR_OP, ASMOPS, ASMOPS_OP> { <PLAINPARAMS, EXPR, EXPR_OP, ASMOPS, ASMOPS_OP> {
{WHITE_SPACE} { return handleEolCommentWhitespace(this); } {WHITE_SPACE} { return handleEolCommentWhitespace(this); }
} }
@@ -102,6 +102,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{MACRO_DEF_LEFT} { yybegin(MACRODEF); yypushback(pushbackAfterFirstToken(yytext())); return MACRO_NAME; } {MACRO_DEF_LEFT} { yybegin(MACRODEF); yypushback(pushbackAfterFirstToken(yytext())); return MACRO_NAME; }
{LOCAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; } {LOCAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return LOCAL_LABEL_DEF; }
{GLOBAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return GLOBAL_LABEL_DEF; } {GLOBAL_LABEL} { yybegin(INSTRPART); eatOneWhitespace = false; yypushback(pushbackLabelColons(yytext())); return GLOBAL_LABEL_DEF; }
"." { yybegin(INSTRPART); eatOneWhitespace = false; return LOCAL_LABEL_DEF; }
} }
<NOSOL> { <NOSOL> {
@@ -116,7 +117,9 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{DIRECTIVE_KEYWORD} { {DIRECTIVE_KEYWORD} {
if(isAsmMnemonicWithSize(yytext())) { yybegin(ASMINSTR); yypushback(2); return MNEMONIC; } if(isAsmMnemonicWithSize(yytext())) { yybegin(ASMINSTR); yypushback(2); return MNEMONIC; }
if(isAsmMnemonic(yytext())) { yybegin(ASMINSTR); return MNEMONIC; } if(isAsmMnemonic(yytext())) { yybegin(ASMINSTR); return MNEMONIC; }
if(isEndDirective(yytext())) { yybegin(YYINITIAL); zzAtEOF = true; return null; }
if(isDataDirective(yytext())) { startExpr(EXPR, EXPR_OP); return DATA_DIRECTIVE; } if(isDataDirective(yytext())) { startExpr(EXPR, EXPR_OP); return DATA_DIRECTIVE; }
if(isPlainDirective(yytext())) { yybegin(PLAINPARAMS); return OTHER_DIRECTIVE; }
if(isOtherDirective(yytext())) { startExpr(EXPR, EXPR_OP); return OTHER_DIRECTIVE; } if(isOtherDirective(yytext())) { startExpr(EXPR, EXPR_OP); return OTHER_DIRECTIVE; }
return handleMacroMode(this); return handleMacroMode(this);
} }
@@ -136,7 +139,7 @@ PLAIN_MACRO_LINE=[^;\r\n]+
{OPSIZE_L} { return OPSIZE_L; } {OPSIZE_L} { return OPSIZE_L; }
} }
<MACROCALL> { <PLAINPARAMS> {
"," { return SEPARATOR; } "," { return SEPARATOR; }
{PLAINPARAM} { return STRINGLIT; } {PLAINPARAM} { return STRINGLIT; }
@@ -123,9 +123,9 @@ private line ::= !<<eof>> (MacroDefinition | statement) (<<eof>>|EOL)
statement ::= (Assignment statement ::= (Assignment
| PreprocessorDirective | PreprocessorDirective
| LabelInsts) | LabelInsts)
{pin=1 recoverWhile=statement_recover}; {pin = 1 recoverWhile = statement_recover}
private statement_recover ::= !(EOL) private statement_recover ::= !(EOL) { consumeTokenMethod = "consumeTokenFast" }
SymbolDefinition ::= SYMBOLDEF { SymbolDefinition ::= SYMBOLDEF {
implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement" implements = "de.platon42.intellij.plugins.m68k.psi.M68kNamedElement"
@@ -173,8 +173,11 @@ AsmOp ::= MNEMONIC OperandSize? {
methods = [getMnemonic getOpSize] methods = [getMnemonic getOpSize]
} }
PreprocessorDirective ::= Label? (DATA_DIRECTIVE | OTHER_DIRECTIVE) PreprocessorKeyword ::= (DATA_DIRECTIVE | OTHER_DIRECTIVE)
PreprocessorOperands?
PreprocessorDirective ::= Label? PreprocessorKeyword PreprocessorOperands? {
mixin = "de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirectiveMixin"
}
MacroPlainLine ::= MACRO_LINE MacroPlainLine ::= MACRO_LINE
MacroNameDefinition ::= MACRO_NAME MacroNameDefinition ::= MACRO_NAME
@@ -62,11 +62,4 @@ object M68kAddressModeUtil {
else -> throw IllegalArgumentException("Unknown addressing mode $addressingMode") else -> throw IllegalArgumentException("Unknown addressing mode $addressingMode")
} }
} }
fun mergeReadWriteModifyRegisters(regset: Set<Pair<Register, Int>>): Set<Pair<Register, Int>> {
if (regset.size <= 1) return regset
return regset.groupBy({ it.first }) { it.second }
.map { it.key to if (it.value.size == 1) it.value.single() else it.value.reduce(Int::or) }
.toSet()
}
} }
@@ -37,6 +37,11 @@ object M68kElementFactory {
return PsiTreeUtil.findChildOfType(file, M68kMacroCall::class.java)!! return PsiTreeUtil.findChildOfType(file, M68kMacroCall::class.java)!!
} }
fun createIncludeStatement(project: Project, path: String): M68kPreprocessorDirective {
val file = createFile(project, " include \"$path\"\n")
return PsiTreeUtil.findChildOfType(file, M68kPreprocessorDirective::class.java)!!
}
fun createFile(project: Project, content: String): M68kFile { fun createFile(project: Project, content: String): M68kFile {
return PsiFileFactory.getInstance(project).createFileFromText("dummy.m68k", INSTANCE, content) as M68kFile return PsiFileFactory.getInstance(project).createFileFromText("dummy.m68k", INSTANCE, content) as M68kFile
} }
@@ -2,21 +2,13 @@ package de.platon42.intellij.plugins.m68k.psi
import com.intellij.extapi.psi.ASTWrapperPsiElement import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
abstract class M68kLiteralExprMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLiteralExpr { abstract class M68kLiteralExprMixin(node: ASTNode) : ASTWrapperPsiElement(node), M68kLiteralExpr {
override fun getValue(): Any? { override fun getValue(): Any? {
val childNode = firstChild.node val childNode = firstChild.node
when (childNode.elementType) { when (childNode.elementType) {
M68kTypes.STRINGLIT -> { M68kTypes.STRINGLIT -> LexerUtil.unquoteString(childNode.text)
return text.run {
when {
startsWith('"') -> removeSurrounding("\"")
startsWith('\'') -> removeSurrounding("'")
startsWith('<') -> removeSurrounding(">")
else -> this
}
}
}
M68kTypes.DECIMAL -> { M68kTypes.DECIMAL -> {
try { try {
return childNode.text.toInt() return childNode.text.toInt()
@@ -46,6 +38,6 @@ abstract class M68kLiteralExprMixin(node: ASTNode) : ASTWrapperPsiElement(node),
} }
} }
} }
return text return childNode.text
} }
} }
@@ -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)
} }
@@ -0,0 +1,17 @@
package de.platon42.intellij.plugins.m68k.psi
import com.intellij.extapi.psi.ASTWrapperPsiElement
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiReference
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<PsiReference> {
return ReferenceProvidersRegistry.getReferencesFromProviders(this)
}
}
@@ -29,7 +29,7 @@ object M68kPsiImplUtil {
// Local Label // Local Label
@JvmStatic @JvmStatic
fun getName(element: M68kLocalLabel): String? = element.firstChild.text fun getName(element: M68kLocalLabel): String = element.firstChild?.text ?: ""
@JvmStatic @JvmStatic
fun setName(element: M68kLocalLabel, name: String): PsiElement { fun setName(element: M68kLocalLabel, name: String): PsiElement {
@@ -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)
} }
@@ -0,0 +1,46 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.ResolveCache
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import de.platon42.intellij.plugins.m68k.M68kFileType
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
class M68kIncludeFileReference(element: M68kPreprocessorDirective) : PsiPolyVariantReferenceBase<M68kPreprocessorDirective>(element, true) {
companion object {
val INSTANCE = Resolver()
}
class Resolver : ResolveCache.PolyVariantResolver<M68kIncludeFileReference> {
override fun resolve(ref: M68kIncludeFileReference, incompleteCode: Boolean): Array<ResolveResult> {
val project = ref.element.project
val allFiles = FileTypeIndex.getFiles(M68kFileType.INSTANCE, GlobalSearchScope.allScope(project))
val pathName = LexerUtil.unquoteString(ref.element.exprList.first().text).replace('\\', '/')
val fileMatched = ((allFiles.firstOrNull { it.path.equals(pathName, ignoreCase = true) }
?: allFiles.firstOrNull { it.path.endsWith(pathName.removePrefix("../"), ignoreCase = true) })
?: allFiles.firstOrNull { it.path.endsWith(pathName.substringAfterLast('/'), ignoreCase = true) }) ?: return emptyArray()
return PsiElementResolveResult.createResults(PsiManager.getInstance(project).findFile(fileMatched))
}
}
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> =
ResolveCache.getInstance(element.project)
.resolveWithCaching(this, INSTANCE, false, incompleteCode)
override fun resolve(): PsiElement? = multiResolve(false).singleOrNull()?.element
override fun getVariants(): Array<Any> = emptyArray()
// override fun getRangeInElement(): TextRange {
// val unquoted = LexerUtil.unquoteString(element.text)
//
// val path = element.exprList.first()
// return TextRange.from(
// path.textRange.startOffset + path.text.indexOf(unquoted), unquoted.length
// )
// }
}
@@ -0,0 +1,68 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ProcessingContext
import de.platon42.intellij.plugins.m68k.psi.*
class M68kLocalLabelDefCompletionContributor : CompletionContributor() {
init {
extend(
CompletionType.BASIC,
PlatformPatterns.or(PlatformPatterns.psiElement(M68kTypes.LOCAL_LABEL_DEF), PlatformPatterns.psiElement(M68kTypes.GLOBAL_LABEL_DEF)),
object : CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, resultSet: CompletionResultSet) {
var topLevelElement = parameters.originalFile.findElementAt(parameters.offset)
while (topLevelElement?.parent !is M68kFile) {
topLevelElement = topLevelElement?.parent ?: return
}
// TODO find out if we can cache this somehow
val affectedStatements = ArrayList<M68kStatement>()
val definedLocalLabels = HashSet<String>()
val referencedLocalLabels = HashSet<String>()
findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) {
PsiTreeUtil.getNextSiblingOfType(it, M68kStatement::class.java)
}
findUndefinedLocalLabels(topLevelElement, affectedStatements, definedLocalLabels, referencedLocalLabels) {
PsiTreeUtil.getPrevSiblingOfType(it, M68kStatement::class.java)
}
referencedLocalLabels.removeAll(definedLocalLabels)
resultSet.addAllElements(
if (parameters.originalPosition?.text == ".") {
referencedLocalLabels.map { LookupElementBuilder.create(it.removePrefix(".")) }
} else {
referencedLocalLabels.map(LookupElementBuilder::create)
}
)
}
private fun findUndefinedLocalLabels(
topLevelElement: PsiElement,
affectedStatements: MutableList<M68kStatement>,
definedLocalLabels: MutableSet<String>,
referencedLocalLabels: MutableSet<String>,
direction: (topLevelElement: PsiElement) -> M68kStatement?
) {
var currStatement = topLevelElement
while (true) {
currStatement = direction.invoke(currStatement) ?: break
val globalLabel = PsiTreeUtil.findChildOfType(currStatement, M68kGlobalLabel::class.java)
if (globalLabel != null) break
affectedStatements.add(currStatement)
val localLabel = PsiTreeUtil.findChildOfType(currStatement, M68kLocalLabel::class.java)
if (localLabel != null) definedLocalLabels.add(localLabel.name!!)
val symbolReferences = PsiTreeUtil.findChildrenOfAnyType(currStatement, M68kSymbolReference::class.java)
if (symbolReferences.isNotEmpty()) {
referencedLocalLabels.addAll(
symbolReferences.filter(M68kSymbolReference::isLocalLabelRef).map(M68kSymbolReference::getSymbolName)
)
}
}
}
})
}
}
@@ -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
@@ -0,0 +1,34 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.history.core.Paths
import com.intellij.openapi.util.TextRange
import com.intellij.psi.AbstractElementManipulator
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kElementFactory
import de.platon42.intellij.plugins.m68k.psi.M68kLiteralExpr
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
class M68kPreprocessorDirectiveElementManipulator : AbstractElementManipulator<M68kPreprocessorDirective>() {
@Throws(IncorrectOperationException::class)
override fun handleContentChange(element: M68kPreprocessorDirective, range: TextRange, newContent: String): M68kPreprocessorDirective {
val oldPath = LexerUtil.unquoteString(element.exprList.first().text)
.replace('\\', '/')
.substringBeforeLast('/', missingDelimiterValue = "")
val newIncludeStatement = M68kElementFactory.createIncludeStatement(element.project, Paths.appended(oldPath, newContent))
PsiTreeUtil.findChildOfType(element, M68kLiteralExpr::class.java)!!.replace(newIncludeStatement.exprList.first())
return element
}
override fun getRangeInElement(element: M68kPreprocessorDirective): TextRange {
val pathExpr = element.exprList.first()
val unquotedPath = LexerUtil.unquoteString(pathExpr.text)
return if (unquotedPath.length < pathExpr.textLength) {
TextRange.from(pathExpr.startOffsetInParent + 1, unquotedPath.length)
} else {
pathExpr.textRangeInParent
}
}
}
@@ -3,7 +3,9 @@ package de.platon42.intellij.plugins.m68k.refs
import com.intellij.patterns.PlatformPatterns import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import de.platon42.intellij.plugins.m68k.psi.M68kLiteralExpr
import de.platon42.intellij.plugins.m68k.psi.M68kMacroCall import de.platon42.intellij.plugins.m68k.psi.M68kMacroCall
import de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective
import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference import de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference
class M68kReferenceContributor : PsiReferenceContributor() { class M68kReferenceContributor : PsiReferenceContributor() {
@@ -12,12 +14,14 @@ class M68kReferenceContributor : PsiReferenceContributor() {
val localLabelReferenceProvider = LocalLabelReferenceProvider() val localLabelReferenceProvider = LocalLabelReferenceProvider()
val globalLabelReferenceProvider = GlobalLabelSymbolReferenceProvider() val globalLabelReferenceProvider = GlobalLabelSymbolReferenceProvider()
val macroReferenceProvider = MacroReferenceProvider() val macroReferenceProvider = MacroReferenceProvider()
val includeFileReferenceProvider = IncludeFileReferenceProvider()
} }
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider) registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), localLabelReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), globalLabelReferenceProvider) registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kSymbolReference::class.java), globalLabelReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kMacroCall::class.java), macroReferenceProvider) registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kMacroCall::class.java), macroReferenceProvider)
registrar.registerReferenceProvider(PlatformPatterns.psiElement(M68kPreprocessorDirective::class.java), includeFileReferenceProvider)
} }
class LocalLabelReferenceProvider : PsiReferenceProvider() { class LocalLabelReferenceProvider : PsiReferenceProvider() {
@@ -40,4 +44,16 @@ class M68kReferenceContributor : PsiReferenceContributor() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> = override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> =
arrayOf(M68kMacroReference(element as M68kMacroCall)) arrayOf(M68kMacroReference(element as M68kMacroCall))
} }
class IncludeFileReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<PsiReference> {
val directive = element as M68kPreprocessorDirective
if (directive.exprList.firstOrNull() !is M68kLiteralExpr) return emptyArray()
return if (directive.preprocessorKeyword.text.equals("include", ignoreCase = true)) {
arrayOf(M68kIncludeFileReference(directive))
} else {
emptyArray()
}
}
}
} }
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.scanner package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.lang.HelpID
import com.intellij.lang.cacheBuilder.DefaultWordsScanner import com.intellij.lang.cacheBuilder.DefaultWordsScanner
import com.intellij.lang.cacheBuilder.WordsScanner import com.intellij.lang.cacheBuilder.WordsScanner
import com.intellij.lang.findUsages.FindUsagesProvider import com.intellij.lang.findUsages.FindUsagesProvider
@@ -22,14 +23,19 @@ class M68kFindUsagesProvider : FindUsagesProvider {
TokenSet.EMPTY TokenSet.EMPTY
) )
override fun canFindUsagesFor(psiElement: PsiElement): Boolean = psiElement is M68kNamedElement override fun canFindUsagesFor(psiElement: PsiElement): Boolean =
when (psiElement) {
is M68kNamedElement, is M68kSymbolReference, is M68kMacroCall, is M68kFile, is M68kRegister -> true
else -> false
}
override fun getHelpId(psiElement: PsiElement): @NonNls String? = null override fun getHelpId(psiElement: PsiElement): @NonNls String = HelpID.FIND_OTHER_USAGES
override fun getType(element: PsiElement): @Nls String { override fun getType(element: PsiElement): @Nls String {
return when (element) { return when (element) {
is M68kGlobalLabel -> "global label" is M68kGlobalLabel -> "global label"
is M68kLocalLabel -> "local label" is M68kLocalLabel -> "local label"
is M68kPreprocessorDirective -> "preprocessor directive"
is M68kSymbolDefinition -> "symbol definition" is M68kSymbolDefinition -> "symbol definition"
is M68kSymbolReference -> "symbol reference" is M68kSymbolReference -> "symbol reference"
is M68kMacroDefinition -> "macro definition" is M68kMacroDefinition -> "macro definition"
@@ -44,6 +50,7 @@ class M68kFindUsagesProvider : FindUsagesProvider {
return when (element) { return when (element) {
is M68kGlobalLabel -> element.name!! is M68kGlobalLabel -> element.name!!
is M68kLocalLabel -> element.name!! is M68kLocalLabel -> element.name!!
is M68kPreprocessorDirective -> element.text
is M68kSymbolDefinition -> element.parent.text is M68kSymbolDefinition -> element.parent.text
is M68kSymbolReference -> element.symbolName is M68kSymbolReference -> element.symbolName
is M68kMacroDefinition -> element.macroNameDefinition.text is M68kMacroDefinition -> element.macroNameDefinition.text
@@ -0,0 +1,59 @@
package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.include.FileIncludeInfo
import com.intellij.psi.impl.include.FileIncludeProvider
import com.intellij.psi.impl.source.tree.LightTreeUtil
import com.intellij.util.Consumer
import com.intellij.util.PathUtilRt
import com.intellij.util.indexing.FileContent
import com.intellij.util.indexing.PsiDependentFileContent
import de.platon42.intellij.plugins.m68k.M68kFileType.Companion.INSTANCE
import de.platon42.intellij.plugins.m68k.lexer.LexerUtil
import de.platon42.intellij.plugins.m68k.psi.M68kTypes
class M68kIncludeFileProvider : FileIncludeProvider() {
override fun getId(): String {
return "mc68000_include"
}
override fun acceptFile(file: VirtualFile): Boolean {
return FileTypeRegistry.getInstance().isFileOfType(file, INSTANCE)
}
override fun registerFileTypesUsedForIndexing(fileTypeSink: Consumer<in FileType?>) {
fileTypeSink.consume(INSTANCE)
}
override fun getIncludeInfos(content: FileContent): Array<FileIncludeInfo> {
if (content.contentAsText.indexOfAny(listOf("include", "incbin"), ignoreCase = true) < 0) return emptyArray()
val tree = (content as PsiDependentFileContent).lighterAST
val statements = LightTreeUtil.getChildrenOfType(tree, tree.root, M68kTypes.STATEMENT).asSequence()
.mapNotNull { LightTreeUtil.firstChildOfType(tree, it, M68kTypes.PREPROCESSOR_DIRECTIVE) }
.mapNotNull {
val keywordNode = LightTreeUtil.firstChildOfType(tree, it, M68kTypes.PREPROCESSOR_KEYWORD) ?: return@mapNotNull null
val keyword = LightTreeUtil.toFilteredString(tree, keywordNode, null)
if (keyword.equals("include", true) || keyword.equals("incbin", true)) {
keyword to it
} else {
null
}
}
.mapNotNull {
val pathNode = LightTreeUtil.firstChildOfType(tree, it.second, M68kTypes.LITERAL_EXPR) ?: return@mapNotNull null
val path = LexerUtil.unquoteString(LightTreeUtil.toFilteredString(tree, pathNode, null)).replace('\\', '/')
if (it.first.equals("include", true)) {
FileIncludeInfo(path)
} else {
FileIncludeInfo(PathUtilRt.getFileName(path), path, 0, true)
}
}
.toList()
return statements.toTypedArray()
}
}
+8
View File
@@ -24,15 +24,19 @@
<colorSettingsPage implementation="de.platon42.intellij.plugins.m68k.syntax.M68kColorSettingsPage"/> <colorSettingsPage implementation="de.platon42.intellij.plugins.m68k.syntax.M68kColorSettingsPage"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.asm.M68kMnemonicCompletionContributor"/> <completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.asm.M68kMnemonicCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kGlobalLabelSymbolCompletionContributor"/> <completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kGlobalLabelSymbolCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kLocalLabelDefCompletionContributor"/>
<completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallCompletionContributor"/> <completion.contributor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallCompletionContributor"/>
<lang.braceMatcher language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.syntax.M68kPairedBraceMatcher"/> <lang.braceMatcher language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.syntax.M68kPairedBraceMatcher"/>
<lang.quoteHandler language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.M68kStringQuoteHandler"/> <lang.quoteHandler language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.M68kStringQuoteHandler"/>
<lang.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/> <lang.findUsagesProvider language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.scanner.M68kFindUsagesProvider"/>
<lang.psiStructureViewFactory language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.structureview.M68kStructureViewFactory"/> <lang.psiStructureViewFactory language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.structureview.M68kStructureViewFactory"/>
<lang.inspectionSuppressor language="MC68000" implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kInspectionSuppressor"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference" <lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kSymbolReference"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/> implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kSymbolReferenceElementManipulator"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall" <lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kMacroCall"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallElementManipulator"/> implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kMacroCallElementManipulator"/>
<lang.elementManipulator forClass="de.platon42.intellij.plugins.m68k.psi.M68kPreprocessorDirective"
implementationClass="de.platon42.intellij.plugins.m68k.refs.M68kPreprocessorDirectiveElementManipulator"/>
<lang.documentationProvider language="MC68000" <lang.documentationProvider language="MC68000"
implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kSymbolDefinitionDocumentationProvider"/> implementationClass="de.platon42.intellij.plugins.m68k.documentation.M68kSymbolDefinitionDocumentationProvider"/>
<lang.documentationProvider language="MC68000" <lang.documentationProvider language="MC68000"
@@ -46,6 +50,7 @@
<psi.referenceContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/> <psi.referenceContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kReferenceContributor"/>
<gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/> <gotoSymbolContributor implementation="de.platon42.intellij.plugins.m68k.refs.M68kChooseByNameContributor"/>
<renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/> <renameInputValidator implementation="de.platon42.intellij.plugins.m68k.psi.M68kRenameInputValidator"/>
<include.provider implementation="de.platon42.intellij.plugins.m68k.scanner.M68kIncludeFileProvider"/>
<localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection" <localInspection implementationClass="de.platon42.intellij.plugins.m68k.inspections.M68kSyntaxInspection"
displayName="Assembly instruction validity" groupName="M68k" displayName="Assembly instruction validity" groupName="M68k"
@@ -56,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>
@@ -1,5 +1,6 @@
package de.platon42.intellij.plugins.m68k.inspections package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.TestDataPath import de.platon42.intellij.jupiter.TestDataPath
@@ -16,4 +17,16 @@ abstract class AbstractInspectionTest : AbstractM68kTest() {
assertThat(myFixture.doHighlighting()) assertThat(myFixture.doHighlighting())
.areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing")) .areExactly(count, Condition({ it.description?.contains(snippet) ?: false }, "containing"))
} }
protected fun executeQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int) {
val quickfixes = getQuickFixes(myFixture, regex, expectedFixes)
assertThat(quickfixes.groupBy { it.familyName }).hasSize(1)
quickfixes.forEach(myFixture::launchAction)
}
protected fun getQuickFixes(myFixture: CodeInsightTestFixture, regex: Regex, expectedFixes: Int): List<IntentionAction> {
val quickfixes = myFixture.getAllQuickFixes().filter { it.text.matches(regex) }
assertThat(quickfixes).`as`("Fixes matched by $regex: ${myFixture.getAllQuickFixes().map { it.text }}").hasSize(expectedFixes)
return quickfixes
}
} }
@@ -0,0 +1,70 @@
package de.platon42.intellij.plugins.m68k.inspections
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
internal class M68kInspectionSuppressorTest : AbstractInspectionTest() {
companion object {
private const val SUPPRESS_FOR_STATEMENT = "Suppress for statement"
}
@Test
internal fun suppresses_inspection_with_end_of_line_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts d0 ; suppress M68kSyntax")
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun suppresses_inspection_with_full_line_comment_on_line_before(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", "; suppress M68kSyntax\n rts d0")
assertThat(myFixture.doHighlighting()).isEmpty()
}
@Test
internal fun suppression_works_without_prior_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult(" rts d0\t; suppress M68kSyntax")
}
@Test
internal fun suppression_works_with_prior_suppression_comment(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0; suppress foobar")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult(" rts d0; suppress foobar M68kSyntax")
}
@Test
internal fun suppression_works_with_prior_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", "; suppress foobar\n rts<caret> d0")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult("; suppress foobar M68kSyntax\n rts d0")
}
@Test
internal fun suppression_works_by_adding_suppression_comment_in_line_above(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.enableInspections(M68kSyntaxInspection::class.java)
myFixture.configureByText("syntax.asm", " rts<caret> d0 ; unrelated comment")
executeSuppressAction(myFixture, SUPPRESS_FOR_STATEMENT)
myFixture.checkResult("; suppress M68kSyntax\n rts d0 ; unrelated comment")
}
private fun executeSuppressAction(myFixture: CodeInsightTestFixture, suppressAction: String) {
val highlightInfos = myFixture.doHighlighting()
assertThat(highlightInfos).hasSize(1)
val quickFixPair = highlightInfos[0].quickFixActionRanges[0]
val intentionActionDescriptor = quickFixPair.first
val element = myFixture.file.findElementAt(quickFixPair.second.startOffset)!!
val suppressQuickFix = intentionActionDescriptor.getOptions(element, myFixture.editor)!!
.first { it.text == suppressAction }
myFixture.launchAction(suppressQuickFix)
}
}
@@ -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'")
}
}
@@ -0,0 +1,22 @@
package de.platon42.intellij.plugins.m68k.parser
import de.platon42.intellij.jupiter.MyTestCase
import de.platon42.intellij.jupiter.ParsingTestExtension
import de.platon42.intellij.jupiter.TestDataSubPath
import org.junit.jupiter.api.Test
@TestDataSubPath("directives")
internal class EndDirectiveTest : AbstractParsingTest() {
@Test
internal fun end_directive(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(
testCase, """
moveq.l #0,d0
rts
END narf
äöö#öä235r2352ou485742q3535p3ü
"""
)
}
}
@@ -22,4 +22,14 @@ internal class OtherDirectivesTest : AbstractParsingTest() {
internal fun if_defined_block(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) { internal fun if_defined_block(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, "\tIFD DEBUG ; cause a crash\n illegal\n ENDC\n") testGoodSyntax(testCase, "\tIFD DEBUG ; cause a crash\n illegal\n ENDC\n")
} }
@Test
internal fun kalms_include_file_special_quote(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " include\t<Engine/A500/System/Copper.i>")
}
@Test
internal fun opt_directive(@MyTestCase testCase: ParsingTestExtension.IParsingTestCase) {
testGoodSyntax(testCase, " opt o+,w-")
}
} }
@@ -0,0 +1,57 @@
package de.platon42.intellij.plugins.m68k.refs
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataPath
import de.platon42.intellij.jupiter.TestDataSubPath
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@TestDataPath("src/test/resources/references")
@TestDataSubPath("completion")
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kLocalLabelDefCompletionContributorTest {
@Test
internal fun completion_shows_undefined_local_labels_without_dot_prefix(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"completeme.asm", """
main
.loop
dbra d0,.loop
dbra d1,.loop2
.<caret>
bra.s .foo
rts
coolsubroutine
bra.s .narf
"""
)
myFixture.completeBasic()
assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder("loop2", "foo")
}
@Test
internal fun completion_shows_undefined_local_labels_even_without_dot(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.configureByText(
"completeme.asm", """
main
.loop
dbra d0,.loop
dbra d1,.loop2
<caret>
bra.s .foo
bra.s bar$
rts
coolsubroutine
bra.s .narf
"""
)
myFixture.completeBasic()
assertThat(myFixture.lookupElementStrings).containsExactlyInAnyOrder(".loop2", ".foo", "bar$")
}
}
@@ -2,6 +2,8 @@ package de.platon42.intellij.plugins.m68k.refs
import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture import de.platon42.intellij.jupiter.MyFixture
@@ -86,4 +88,44 @@ internal class M68kReferenceContributorTest : AbstractM68kTest() {
myFixture.checkResultByFile("macros_after_rename.asm") myFixture.checkResultByFile("macros_after_rename.asm")
} }
@Test
internal fun reference_to_file_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("otherfile.asm", "; oh no!")
val file = myFixture.configureByText(
"fileref.asm", """
include "otherfil<caret>e.asm"
"""
)
val reference = file.findReferenceAt(myFixture.editor.caretModel.offset)
assertThat(reference).isInstanceOf(M68kIncludeFileReference::class.java)
val otherfile = reference!!.resolve() as M68kFile
assertThat(otherfile.text).isEqualTo("; oh no!")
myFixture.renameElementAtCaret("foobar.asm")
val files = FilenameIndex.getFilesByName(myFixture.project, "foobar.asm", GlobalSearchScope.allScope(myFixture.project))
assertThat(files).hasSize(1)
assertThat(file.text).isEqualToIgnoringWhitespace("include \"foobar.asm\"")
}
@Test
internal fun reference_to_file_in_a_subdirectory_can_be_renamed(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("foobar/otherfile.asm", "; oh no!")
val file = myFixture.configureByText(
"fileref.asm", """
include "foobar/otherfil<caret>e.asm"
"""
)
val reference = file.findReferenceAt(myFixture.editor.caretModel.offset)
assertThat(reference).isInstanceOf(M68kIncludeFileReference::class.java)
val otherfile = reference!!.resolve() as M68kFile
assertThat(otherfile.text).isEqualTo("; oh no!")
myFixture.renameElementAtCaret("foobar.asm")
val files = FilenameIndex.getFilesByName(myFixture.project, "foobar.asm", GlobalSearchScope.allScope(myFixture.project))
assertThat(files).hasSize(1)
assertThat(file.text).isEqualToIgnoringWhitespace("include \"foobar/foobar.asm\"")
}
} }
@@ -0,0 +1,37 @@
package de.platon42.intellij.plugins.m68k.scanner
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.include.FileIncludeManager
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import de.platon42.intellij.jupiter.LightCodeInsightExtension
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.plugins.m68k.AbstractM68kTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@ExtendWith(LightCodeInsightExtension::class)
internal class M68kIncludeFileProviderTest : AbstractM68kTest() {
@Test
internal fun accepts_a_couple_of_include_statements(@MyFixture myFixture: CodeInsightTestFixture) {
myFixture.addFileToProject("macros.i", "PUSHM MACRO\n ENDM")
myFixture.addFileToProject("exec/execbase.i", "; Commodore include")
myFixture.addFileToProject("exec/exec.i", " include execbase.i")
myFixture.addFileToProject("includes/hardware/custom.i", "; Another Commodore include")
myFixture.addFileToProject("data/ease_x.bin", "binary file")
myFixture.addFileToProject("fx/fx_angle_only.S", "; Nosfe!!")
myFixture.configureByText(
"completeme.asm", """
include macros.i
include "exec/exec.i"
include <../src/includes/hardware/custom.i>
incbin "data/ease_x.bin"
include fx\fx_angle_only.S
"""
)
val includedFiles = FileIncludeManager.getManager(myFixture.project).getIncludedFiles(myFixture.file.virtualFile, true, true)
assertThat(includedFiles).extracting<String>(VirtualFile::getName)
.containsExactlyInAnyOrder("completeme.asm", "macros.i", "fx_angle_only.S", "custom.i", "exec.i", "execbase.i")
}
}
@@ -5,7 +5,8 @@ Assembly File: a.asm
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('foo') PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('foo')
PsiElement(M68kTokenType.COLON)(':') PsiElement(M68kTokenType.COLON)(':')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.b') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.b')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR) M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)(''It could be good!'') PsiElement(M68kTokenType.STRINGLIT)(''It could be good!'')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR) M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('0') PsiElement(M68kTokenType.DECIMAL)('0')
@@ -0,0 +1,26 @@
Assembly File: a.asm
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('moveq')
M68kOperandSizeImpl(OPERAND_SIZE)
PsiElement(M68kTokenType.OPSIZE_L)('.l')
PsiWhiteSpace(' ')
M68kImmediateDataImpl(IMMEDIATE_DATA)
PsiElement(M68kTokenType.HASH)('#')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.DECIMAL)('0')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kDataRegisterDirectAddressingModeImpl(DATA_REGISTER_DIRECT_ADDRESSING_MODE)
M68kDataRegisterImpl(DATA_REGISTER)
PsiElement(M68kTokenType.DREG)('d0')
PsiElement(M68kTokenType.EOL)('\n')
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kAsmInstructionImpl(ASM_INSTRUCTION)
M68kAsmOpImpl(ASM_OP)
PsiElement(M68kTokenType.MNEMONIC)('rts')
PsiElement(M68kTokenType.EOL)('\n')
PsiWhiteSpace(' END narf\n äöö#öä235r2352ou485742q3535p3ü\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace('\t') PsiWhiteSpace('\t')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('even') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('even')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
PsiComment(M68kTokenType.COMMENT)('; align to even address') PsiComment(M68kTokenType.COMMENT)('; align to even address')
PsiElement(M68kTokenType.EOL)('\n') PsiElement(M68kTokenType.EOL)('\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace('\t') PsiWhiteSpace('\t')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IFD') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IFD')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kRefExprImpl(REF_EXPR) M68kRefExprImpl(REF_EXPR)
M68kSymbolReferenceImpl(SYMBOL_REFERENCE) M68kSymbolReferenceImpl(SYMBOL_REFERENCE)
@@ -19,5 +20,6 @@ Assembly File: a.asm
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('ENDC') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('ENDC')
PsiElement(M68kTokenType.EOL)('\n') PsiElement(M68kTokenType.EOL)('\n')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR) M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('"exec/execbase.i"') PsiElement(M68kTokenType.STRINGLIT)('"exec/execbase.i"')
@@ -4,9 +4,9 @@ Assembly File: a.asm
M68kGlobalLabelImpl(GLOBAL_LABEL) M68kGlobalLabelImpl(GLOBAL_LABEL)
PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('howto') PsiElement(M68kTokenType.GLOBAL_LABEL_DEF)('howto')
PsiElement(M68kTokenType.COLON)(':') PsiElement(M68kTokenType.COLON)(':')
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('incbin') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('incbin')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kRefExprImpl(REF_EXPR) M68kLiteralExprImpl(LITERAL_EXPR)
M68kSymbolReferenceImpl(SYMBOL_REFERENCE) PsiElement(M68kTokenType.STRINGLIT)('Convertedassets\scroller_howto.raw.BPL')
PsiElement(M68kTokenType.SYMBOL)('Convertedassets\scroller_howto.raw.BPL')
PsiElement(M68kTokenType.EOL)('\n') PsiElement(M68kTokenType.EOL)('\n')
@@ -0,0 +1,9 @@
Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('include')
PsiWhiteSpace('\t')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('<Engine/A500/System/Copper.i>')
@@ -0,0 +1,12 @@
Assembly File: a.asm
PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('opt')
PsiWhiteSpace(' ')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('o+')
PsiElement(M68kTokenType.SEPARATOR)(',')
M68kLiteralExprImpl(LITERAL_EXPR)
PsiElement(M68kTokenType.STRINGLIT)('w-')
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.DATA_DIRECTIVE)('dc.w')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kBinarySubExprImpl(BINARY_SUB_EXPR) M68kBinarySubExprImpl(BINARY_SUB_EXPR)
M68kRefExprImpl(REF_EXPR) M68kRefExprImpl(REF_EXPR)
@@ -2,7 +2,8 @@ Assembly File: a.asm
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kStatementImpl(STATEMENT) M68kStatementImpl(STATEMENT)
M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE) M68kPreprocessorDirectiveImpl(PREPROCESSOR_DIRECTIVE)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IF') M68kPreprocessorKeywordImpl(PREPROCESSOR_KEYWORD)
PsiElement(M68kTokenType.OTHER_DIRECTIVE)('IF')
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
M68kBinaryCmpEqExprImpl(BINARY_CMP_EQ_EXPR) M68kBinaryCmpEqExprImpl(BINARY_CMP_EQ_EXPR)
M68kRefExprImpl(REF_EXPR) M68kRefExprImpl(REF_EXPR)