Skip to content

Commit

Permalink
Merge pull request #215 from xonixx/func_reference_prevent_unused_fun…
Browse files Browse the repository at this point in the history
…c_inspection

Func reference prevent unused func inspection
  • Loading branch information
xonixx authored Apr 17, 2024
2 parents eb69f81 + 27cd90a commit 5bf3104
Show file tree
Hide file tree
Showing 26 changed files with 230 additions and 12 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ task parser(type: org.jetbrains.grammarkit.tasks.GenerateParser) {

// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
downloadSources = true // https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#ide-configuration
version = '2021.1.3'
plugins = ['com.intellij.java']
}
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/intellij_awk/Awk.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ non_unary_expr ::= (
| NOT expr
| simple_get (LT expr)?
| NUMBER
| STRING
| str
| ERE
| INCR lvalue
| DECR lvalue
Expand Down Expand Up @@ -391,6 +391,12 @@ function_call_name ::= FUNC_NAME
implements="intellij_awk.psi.AwkNamedElement"
}

str ::= STRING // we need this as PSI to enable GAWK's function references `f="fname"; @f()`
{
mixin="intellij_awk.psi.AwkStringMixin"
implements="intellij_awk.psi.AwkNamedElement"
}

private non_unary_expr1 ::= [
( POW expr
| MUL expr
Expand Down Expand Up @@ -449,7 +455,7 @@ private non_unary_print_expr ::= (
| LPAREN expr_lst RPAREN IN gawk_var_name
| simple_get
| NUMBER
| STRING
| str
| ERE
| INCR lvalue
| DECR lvalue
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/intellij_awk/AwkCompletionContributorBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@
abstract class AwkCompletionContributorBase extends CompletionContributor {
static final PsiElementPattern.Capture<PsiElement> INSIDE_STRING = psiElement(AwkTypes.STRING);
private static final ElementPattern<PsiElement> notInsideERE =
not(or(psiElement(AwkTypes.ERE), psiElement(AwkTypes.TYPED_ERE)));
not(or(psiElement(AwkTypes.ERE), psiElement(AwkTypes.TYPED_ERE)));

private static final ObjectPattern.Capture<PsiElement> notInsideStringEre =
not(INSIDE_STRING).and(notInsideERE);
not(INSIDE_STRING).and(notInsideERE);

static ElementPattern<? extends PsiElement> notInsideStringERE(
ElementPattern<? extends PsiElement> pattern) {
ElementPattern<? extends PsiElement> pattern) {
return and(notInsideStringEre, pattern);
}

static ElementPattern<? extends PsiElement> notInsideERE(
ElementPattern<? extends PsiElement> pattern) {
ElementPattern<? extends PsiElement> pattern) {
return and(notInsideERE, pattern);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile file)
}
}
if (name == null) {
PsiElement string = nonUnaryExpr.getString();
PsiElement string = nonUnaryExpr.getStr();
if (string != null && string.getText().equals(nonUnaryExpr.getText())) {
String text = string.getText();
name = text.substring(1, text.length() - 1);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/intellij_awk/AwkParserDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class AwkParserDefinition implements ParserDefinition {
new IStubFileElementType<>(AwkLanguage.INSTANCE) {
@Override
public int getStubVersion() {
return 15;
return 16;
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/intellij_awk/psi/AwkElementFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public static AwkFunctionName createFunctionName(Project project, String name) {
return createAwkPsiElement(project, "function " + name + "(){}", AwkFunctionName.class);
}

public static AwkStr createString(Project project, String str) {
return createAwkPsiElement(
project, "{ a = \"" + str.replace("\"", "\\\"") + "\" }", AwkStr.class);
}

public static AwkItem createFunctionItem(Project project, String name) {
return createAwkPsiElement(project, "function " + name + "(){}", AwkItem.class);
}
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/intellij_awk/psi/AwkStringMixin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package intellij_awk.psi;

import static com.intellij.openapi.util.text.StringUtil.unquoteString;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import intellij_awk.AwkReferenceFunction;
import intellij_awk.AwkUtil;
import java.util.regex.Pattern;
import javax.swing.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public abstract class AwkStringMixin extends AwkNamedElementImpl {
public AwkStringMixin(@NotNull ASTNode node) {
super(node);
}

private static final Pattern stringThatCanBeFunctionName =
Pattern.compile("\"[A-Za-z_][A-Za-z0-9_]*\"");

@Override
public String getName() {
return getPossibleFunctionName();
}

@Nullable
private String getPossibleFunctionName() {
String str = getText();
return canBeFunctionName(str) ? unquoteString(str) : null;
}

static boolean canBeFunctionName(String s) {
return stringThatCanBeFunctionName.matcher(s).matches();
}

public PsiElement setName(String newName) {
return replaceNameNode(AwkElementFactory.createString(getProject(), newName));
}

@Override
public PsiReference getReference() {
return getPossibleFunctionName() != null && canBeFunctionReference()
? new AwkReferenceFunction(this, TextRange.from(1, getTextLength() - 2))
: null;
}

private boolean canBeFunctionReference() {
return isRhsOfAssignment() || isUserFunctionArgument();
}

/** `f("fname")` but not `substr("fname")` (built-in) */
private boolean isUserFunctionArgument() {
AwkGawkFuncCallList callList = AwkUtil.findParent(this, AwkGawkFuncCallList.class);
return callList != null && callList.getParent() instanceof AwkFunctionCallUser;
}

/** `a = "fname"` */
private boolean isRhsOfAssignment() {
return AwkUtil.isType(AwkUtil.getPrevNotWhitespace(getParent()), AwkTypes.ASSIGN);
}

@Override
public @Nullable Icon getIcon(int flags) {
return null;
}
}
37 changes: 35 additions & 2 deletions src/test/java/intellij_awk/AwkInspectionTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,21 @@ public void testIssue203NoUnusedFunctionErrForUsageInOtherFile() {
myFixture.addFileToProject("c.awk", "BEGIN { f() } ");
assertNoInspectionAtCaret(unusedFunction);
}

public void testIssue203NoUnusedFunctionErrForRepeatingDefinitionInSeparateFiles() {
myFixture.configureByText("a.awk", "function <caret>f(){}");
myFixture.addFileToProject("b.awk", "function f() {}");
myFixture.addFileToProject("c.awk", "BEGIN { f() } ");
assertNoInspectionAtCaret(unusedFunction);
}

public void testIssue203UnusedFunctionErrForRepeatingDefinitionWhenFunctionIsUsedInSameFileAsDefined() {
public void
testIssue203UnusedFunctionErrForRepeatingDefinitionWhenFunctionIsUsedInSameFileAsDefined() {
myFixture.configureByText("a.awk", "function <caret>f(){}");
myFixture.addFileToProject("c.awk", "BEGIN { f() } function f() {}");
assertInspectionIsShown(unusedFunction);
}


public void testUsedVarInFileOutsideProject() {
checkByFileNoProblemAtCaret(unusedGlobalVariable, true);
}
Expand Down Expand Up @@ -299,6 +300,38 @@ public void testUnnecessarySemicolonNecessary4() {
checkByFileNoProblemAtCaret(unnecessarySemicolon);
}

public void testFuncRefToPreventUnusedFunc1() {
checkByFileNoProblemAtCaret(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc1_1() {
checkByFile(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc2() {
checkByFileNoProblemAtCaret(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc2_1() {
checkByFile(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc3() {
checkByFileNoProblemAtCaret(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc3_1() {
checkByFile(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc4() {
checkByFileNoProblemAtCaret(unusedFunction);
}

public void testFuncRefToPreventUnusedFunc4_1() {
checkByFile(unusedFunction);
}

@Override
protected String getTestDataPath() {
return "src/test/testData/inspection";
Expand Down
1 change: 1 addition & 0 deletions src/test/java/intellij_awk/AwkRenameTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class AwkRenameTests extends BasePlatformTestCase {
public void testVars6() { checkByFile(); }
public void testVars7() { checkByFile(); }
public void testFunc1() { checkByFile(); }
public void testFuncRef() { checkByFile(); }
public void testIndirect1() { checkByFile(); }

@Override
Expand Down
23 changes: 23 additions & 0 deletions src/test/java/intellij_awk/psi/AwkStringMixinTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package intellij_awk.psi;

import junit.framework.TestCase;
import org.junit.Assert;

public class AwkStringMixinTests extends TestCase {
public void testTrue() {
Assert.assertTrue(AwkStringMixin.canBeFunctionName("\"aaa\""));
Assert.assertTrue(AwkStringMixin.canBeFunctionName("\"_aaa01\""));
Assert.assertTrue(AwkStringMixin.canBeFunctionName("\"Xxx123\""));
Assert.assertTrue(AwkStringMixin.canBeFunctionName("\"_\""));
Assert.assertTrue(AwkStringMixin.canBeFunctionName("\"X\""));
}
public void testFalse() {
Assert.assertFalse(AwkStringMixin.canBeFunctionName(" \"aaa\" "));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"111\""));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"1aaa\""));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"_aa a01\""));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"Xxx'123\""));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"-\""));
Assert.assertFalse(AwkStringMixin.canBeFunctionName("\"aaaa\\\"bbb\""));
}
}
5 changes: 5 additions & 0 deletions src/test/testData/inspection/funcRefToPreventUnusedFunc1.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
PROCINFO["sorted_in"] = "compare"
}

function <caret>compare() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
PROCINFO["sorted_in"] = "compare1"
}

function <caret>compare() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN {
PROCINFO["sorted_in"] = "compare1"
}

6 changes: 6 additions & 0 deletions src/test/testData/inspection/funcRefToPreventUnusedFunc2.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN {
F = "fname"
@F()
}

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN {
F = "aaa fname bbb"
@F()
}

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
F = "aaa fname bbb"
@F()
}

5 changes: 5 additions & 0 deletions src/test/testData/inspection/funcRefToPreventUnusedFunc3.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
print a = "fname"
}

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
print "fname"
}

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BEGIN {
print "fname"
}

7 changes: 7 additions & 0 deletions src/test/testData/inspection/funcRefToPreventUnusedFunc4.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
BEGIN {
b("fname")
}

function b(f) { @f() }

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
length("fname")
}

function <caret>fname() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BEGIN {
length("fname")
}

<caret>
3 changes: 2 additions & 1 deletion src/test/testData/parser/BuiltInFuncSpace.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ AWK File
PsiElement(AwkTokenType.()('(')
AwkGawkFuncCallListImpl(GAWK_FUNC_CALL_LIST)
AwkNonUnaryExprImpl(NON_UNARY_EXPR)
PsiElement(AwkTokenType.STRING)('"abc"')
AwkStrImpl(STR)
PsiElement(AwkTokenType.STRING)('"abc"')
PsiElement(AwkTokenType.,)(',')
AwkNonUnaryExprImpl(NON_UNARY_EXPR)
PsiElement(AwkTokenType.NUMBER)('2')
Expand Down
3 changes: 2 additions & 1 deletion src/test/testData/parser/UserFuncSpace.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ AWK File
AwkNonUnaryExprImpl(NON_UNARY_EXPR)
PsiElement(AwkTokenType.()('(')
AwkNonUnaryExprImpl(NON_UNARY_EXPR)
PsiElement(AwkTokenType.STRING)('"A"')
AwkStrImpl(STR)
PsiElement(AwkTokenType.STRING)('"A"')
PsiElement(AwkTokenType.))(')')
PsiWhiteSpace(' ')
PsiElement(AwkTokenType.})('}')
8 changes: 8 additions & 0 deletions src/test/testData/rename/funcRef.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BEGIN {
a = "fname"
b("fname")
substr("fname")
print "fname"
}
function b() {}
function fname<caret>() {}
8 changes: 8 additions & 0 deletions src/test/testData/rename/funcRefAfter.awk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BEGIN {
a = "newName"
b("newName")
substr("fname")
print "fname"
}
function b() {}
function newName<caret>() {}

0 comments on commit 5bf3104

Please sign in to comment.