Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More inspections #22

Merged
merged 18 commits into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/main/grammars/LalrpopParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,14 @@ nonterminal ::= annotation* visibility? nonterminal_name (COLON type_ref)? EQUAL
mixin = "com.mdrobnak.lalrpop.psi.ext.LpNonterminalMixin"
}

annotation_arg ::= LPAREN id EQUALS STR_LITERAL RPAREN
annotation_arg ::= LPAREN annotation_arg_name EQUALS annotation_arg_value RPAREN

annotation ::= POUND LBRACKET ID annotation_arg? RBRACKET
annotation_arg_name ::= id
annotation_arg_value ::= STR_LITERAL

annotation ::= POUND LBRACKET annotation_name annotation_arg? RBRACKET

annotation_name ::= ID

nonterminal_name ::= ID nonterminal_params? {
implements = "com.mdrobnak.lalrpop.psi.LpNamedElement"
Expand All @@ -87,7 +92,7 @@ alternatives ::= alternative SEMICOLON | LBRACE <<comma alternative>> RBRACE SEM
mixin = "com.mdrobnak.lalrpop.psi.ext.LpAlternativesMixin"
}

alternative ::= symbol+ (IF cond)? action?
alternative ::= annotation* symbol+ (IF cond)? action?
| (IF cond)? action {
implements = "com.mdrobnak.lalrpop.psi.LpResolveType"
mixin = "com.mdrobnak.lalrpop.psi.ext.LpAlternativeMixin"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.mdrobnak.lalrpop.inspections

import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import com.mdrobnak.lalrpop.psi.LpAlternative
import com.mdrobnak.lalrpop.psi.LpAlternatives
import com.mdrobnak.lalrpop.psi.LpAnnotation
import com.mdrobnak.lalrpop.psi.LpVisitor

object PrecedenceInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : LpVisitor() {
override fun visitAlternatives(alternatives: LpAlternatives) {
val alternativesWithoutPrecedenceAnnotation = mutableListOf<LpAlternative>()
var prevPrecedenceAnnotation: LpAnnotation? = null
alternatives.alternativeList.forEach {
val precedenceAnnotation =
it.annotationList.find { annotation -> annotation.annotationName.text == "precedence" }
val assocAnnotation =
it.annotationList.find { annotation -> annotation.annotationName.text == "assoc" }

if (precedenceAnnotation != null) {
if (alternativesWithoutPrecedenceAnnotation.isNotEmpty()) {
alternativesWithoutPrecedenceAnnotation.forEach { alternative ->
holder.registerProblem(
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved
alternative,
"Alternative without #[precedence] in a nonterminal that has a #[precedence] alternative"
)
}
}


val arg = precedenceAnnotation.annotationArg

if (arg == null || arg.annotationArgName.text != "level") {
holder.registerProblem(precedenceAnnotation, "Missing `level` arg on #[precedence]")
} else {
val levelText = arg.annotationArgValue.text

val level = levelText?.substring(1, levelText.length - 1) // remove the "
?.toIntOrNull()

if (level == null) {
holder.registerProblem(arg.annotationArgValue, "Level value is not an int")
}

if (level == 1) {
if (assocAnnotation != null) {
holder.registerProblem(
assocAnnotation,
"#[assoc] used on alternative with #[precedence(level=\"1\")]"
)
}
}
}

prevPrecedenceAnnotation = precedenceAnnotation
}


if (assocAnnotation != null) {
val assocAnnotationArg = assocAnnotation.annotationArg

if (assocAnnotationArg != null && assocAnnotationArg.annotationArgName.text != "side") {
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved
holder.registerProblem(assocAnnotation, "Missing side=\"...\" on #[assoc] annotation")
}
}

if (prevPrecedenceAnnotation == null) {
alternativesWithoutPrecedenceAnnotation.add(it)
}
}
}
}
}
}
6 changes: 6 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,11 @@
enabledByDefault="true"
level="WARNING"/>

<localInspection language="LALRPOP" id="Precedence" groupName="LALRPOP"
displayName="Precedence validation"
implementationClass="com.mdrobnak.lalrpop.inspections.PrecedenceInspection"
enabledByDefault="true"
level="ERROR"/>

</extensions>
</idea-plugin>
23 changes: 23 additions & 0 deletions src/main/resources/inspectionDescriptions/Precedence.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<body>
<p>Detects invalid usage of precedence annotations on alternatives
</p>
<!-- tooltip end -->
<p>
Here's a list of the problems this inspections tries to detect:
</p>
<ul>
<li>
if there's at least one alternative with precedence annotations in a rule, then all the alternatives must be
annotated
</li>
<li>
syntax (precedence annotations have a level argument that is an integer, associativity annotations have a side
argument)
</li>
<li>
the first level cannot have associativity annotations.
</li>
</ul>
</body>
</html>