From 4cee6b1d04fb639e8470173c36bd4be80fb7c3a4 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 01:55:03 -0500 Subject: [PATCH 01/25] Added checkerframework nullness testing --- build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.gradle b/build.gradle index 3a9b9c0..f3b1f99 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,11 @@ plugins { id 'application' id("com.diffplug.spotless") version "7.0.4" id 'jacoco' + id("org.checkerframework").version("0.6.61") } +apply plugin: "org.checkerframework" + group = 'com.example' version = '1.0' @@ -49,6 +52,12 @@ dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-layout-template-json' } +checkerFramework { +// Define which checkers to run +checkers = [ + "org.checkerframework.checker.nullness.NullnessChecker", +] +} spotless { From 41983775eb48dfb4e801df82b98d158f4256bc7e Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 02:40:16 -0500 Subject: [PATCH 02/25] Added type annotations and null checks --- src/main/java/NestedNullRefactoring.java | 41 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/NestedNullRefactoring.java b/src/main/java/NestedNullRefactoring.java index ee9c141..0ccee78 100644 --- a/src/main/java/NestedNullRefactoring.java +++ b/src/main/java/NestedNullRefactoring.java @@ -2,7 +2,8 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; - +import org.checkerframework.checker.nullness.qual.*; +import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Block; @@ -32,14 +33,14 @@ public class NestedNullRefactoring extends Refactoring { public static final String NAME = "NestedNullRefactoring"; - private final Dictionary applicableMethods; + private final Dictionary applicableMethods; public NestedNullRefactoring() { applicableMethods = new Hashtable<>(); } @Override - public boolean isApplicable(ASTNode node) { + public boolean isApplicable(@NonNull ASTNode node) { if (node instanceof MethodInvocation invocation) { return isApplicableImpl(invocation); } @@ -55,7 +56,7 @@ public boolean isApplicable(ASTNode node) { * Returns true iff the provided invocation is of a registered one-line method * that returns the result of a null check */ - private boolean isApplicableImpl(MethodInvocation invocation) { + private boolean isApplicableImpl(@NonNull MethodInvocation invocation) { if (applicableMethods.get(invocation.resolveMethodBinding()) != null) { System.out.println("[DEBUG] Invocation of applicable method found"); return true; @@ -67,7 +68,7 @@ private boolean isApplicableImpl(MethodInvocation invocation) { * Returns true iff Node is a one-line private method that returns the result of * a null check */ - private boolean isApplicableImpl(MethodDeclaration declaration) { + private boolean isApplicableImpl(@NonNull MethodDeclaration declaration) { // getReturnType() is deprecated and replaced by getReturnType2() Type retType = declaration.getReturnType2(); boolean returnsBoolean = (retType.isPrimitiveType() @@ -90,7 +91,12 @@ private boolean isApplicableImpl(MethodDeclaration declaration) { } Block body = declaration.getBody(); - List stmts = body.statements(); + if (body == null) { + return false; + } + + @SuppressWarnings("unchecked") // Silence type warnings; statements() documentation guarantees type is valid. + List stmts = (List) body.statements(); boolean isOneLine = stmts.size() == 1; if (!isOneLine) { @@ -104,7 +110,7 @@ private boolean isApplicableImpl(MethodDeclaration declaration) { // Checks that the return statement is of a single equality check Expression retExpr = ((ReturnStatement) stmt).getExpression(); - if (!(retExpr instanceof InfixExpression)) { + if (retExpr == null || !(retExpr instanceof InfixExpression)) { return false; } @@ -118,7 +124,11 @@ private boolean isApplicableImpl(MethodDeclaration declaration) { if ((isValidOperand(leftOperand) && rightOperand instanceof NullLiteral) || (isValidOperand(rightOperand) && leftOperand instanceof NullLiteral)) { System.out.println("[DEBUG] Found one line null check method: " + declaration.getName()); - applicableMethods.put((declaration.resolveBinding()), retExpr); + IMethodBinding binding = declaration.resolveBinding(); + if (binding == null) { + return false; + } + applicableMethods.put((binding), retExpr); } } return false; @@ -128,12 +138,12 @@ private boolean isApplicableImpl(MethodDeclaration declaration) { * Returns true iff the provided expression can be on one side of a refactorable * null equality check, i.e. it represents a valid variable or constant. */ - private boolean isValidOperand(Expression operand) { + private boolean isValidOperand(@NonNull Expression operand) { return (operand instanceof SimpleName || operand instanceof FieldAccess || operand instanceof QualifiedName); } @Override - public void apply(ASTNode node, ASTRewrite rewriter) { + public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { // Check if Method Invocation is in applicableMethods if (node instanceof MethodInvocation invocation) { replace(node, rewriter, invocation); @@ -143,8 +153,13 @@ public void apply(ASTNode node, ASTRewrite rewriter) { } } - private void replace(ASTNode node, ASTRewrite rewriter, MethodInvocation invocation) { - Expression expr = (applicableMethods.get((invocation.resolveMethodBinding()))); + private void replace(@NonNull ASTNode node, @NonNull ASTRewrite rewriter, @NonNull MethodInvocation invocation) { + IMethodBinding binding = invocation.resolveMethodBinding(); + if (binding == null) { + return; + } + + Expression expr = (applicableMethods.get(binding)); if (expr == null) { System.err.println("Cannot find applicable method for refactoring. "); return; @@ -158,7 +173,7 @@ private void replace(ASTNode node, ASTRewrite rewriter, MethodInvocation invocat pExpr.setExpression(copiedExpression); System.out.println("Refactoring " + node + "\n\tReplacing \n\t" + invocation + "\n\tWith \n\t" + pExpr); - rewriter.replace(invocation, pExpr, null); + rewriter.replace(invocation, pExpr, new TextEditGroup("")); } From 0949387f27cc0580e748673ab614244872397301 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 05:02:26 -0500 Subject: [PATCH 03/25] Added type annotations and null checks --- src/main/java/SentinelRefactoring.java | 62 +++++++++++++++----------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index 061780d..d83bee8 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.core.dom.NullLiteral; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.VariableDeclaration; @@ -26,6 +27,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.*; /** * This class represents a refactoring in which integer variables whose values @@ -44,7 +46,7 @@ public class SentinelRefactoring extends Refactoring { * the key, ensuring global uniqueness. Two variables who have the same name but * have different scopes will have different IBinding instances. */ - private final Map sentinelCandidates; + private final Map sentinelCandidates; /** * Set of all sentinel assignments which have already been parsed; Used to * prevent repeated parsing of same sentinel assignment. @@ -59,17 +61,19 @@ private class Sentinel { /** * The original assignment statement setting the sentinel's value. */ - public Assignment sentinel_assignment; + public @Nullable Assignment sentinel_assignment; /** * The conditional expression used to decide the value of the sentinel. */ - public InfixExpression null_check; + public @Nullable InfixExpression null_check; /** - * The last value assigned to the sentinel; Used for validity tracking. + * The last value assigned to the sentinel; Used for validity tracking. A null + * value represents an unknown previous value. */ - public Object lastValue; + public @Nullable Object lastValue; - public Sentinel(Assignment sentinel_assignment, InfixExpression null_check, Object lastValue) { + public Sentinel(@Nullable Assignment sentinel_assignment, @Nullable InfixExpression null_check, + @Nullable Object lastValue) { this.sentinel_assignment = sentinel_assignment; this.null_check = null_check; this.lastValue = lastValue; @@ -91,7 +95,7 @@ public SentinelRefactoring() { * Detects reassignments of existing sentinels If reassignment is detected, * removes the sentinel from the list of valid sentinels. */ - private void detectReassignment(Assignment assignmentNode) { + private void detectReassignment(@NonNull Assignment assignmentNode) { // Skip assignments that initially define a sentinel. if (sentinelAssignments.contains(assignmentNode)) { return; @@ -109,7 +113,7 @@ private void detectReassignment(Assignment assignmentNode) { /* * Detects sentinels which are shadowed by new local variables and removes them. */ - private void detectShadowing(VariableDeclarationStatement declaration) { + private void detectShadowing(@NonNull VariableDeclarationStatement declaration) { @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees type is // valid. List fragments = declaration.fragments(); @@ -135,7 +139,8 @@ private void detectShadowing(VariableDeclarationStatement declaration) { * The value assigned to the sentinel when the null_check condition * is true */ - private boolean isValidSentinel(Assignment sentinel_assignment, Expression null_check, Object newValue) { + private boolean isValidSentinel(@NonNull Assignment sentinel_assignment, @NonNull Expression null_check, + @NonNull Object newValue) { LOGGER.debug("Parsing Sentinel: %s, %s, %s", sentinel_assignment, null_check, newValue); @@ -169,7 +174,7 @@ private boolean isValidSentinel(Assignment sentinel_assignment, Expression null_ } - private void updateSentinel(ASTNode node) { + private void updateSentinel(@NonNull ASTNode node) { if (node instanceof VariableDeclaration declaration) { updateSentinel(declaration); } else if (node instanceof Assignment assign) { @@ -180,18 +185,20 @@ private void updateSentinel(ASTNode node) { for (Map.Entry entry : sentinelCandidates.entrySet()) { IBinding key = entry.getKey(); Sentinel sentinel = sentinelCandidates.get(key); - sentinel.lastValue = null; + if (sentinel != null) { + sentinel.lastValue = null; + } } } } - private void updateSentinel(VariableDeclaration declaration) { + private void updateSentinel(@NonNull VariableDeclaration declaration) { IBinding key = declaration.getName().resolveBinding(); Object newValue = declaration.getInitializer().resolveConstantExpressionValue(); updateSentinel(key, newValue); } - private void updateSentinel(Assignment statement) { + private void updateSentinel(@NonNull Assignment statement) { if (!(statement.getLeftHandSide() instanceof SimpleName varName)) { return; } @@ -200,7 +207,7 @@ private void updateSentinel(Assignment statement) { updateSentinel(key, newValue); } - private void updateSentinel(IBinding key, Object newValue) { + private void updateSentinel(@NonNull IBinding key, @NonNull Object newValue) { Sentinel sentinel = sentinelCandidates.get(key); if (sentinel == null) { sentinelCandidates.put(key, new Sentinel(null, null, newValue)); @@ -210,7 +217,7 @@ private void updateSentinel(IBinding key, Object newValue) { } @Override - public boolean isApplicable(ASTNode node) { + public boolean isApplicable(@NonNull ASTNode node) { updateSentinel(node); if (node instanceof IfStatement ifStmt && isApplicable(ifStmt)) { return true; @@ -230,7 +237,7 @@ public boolean isApplicable(ASTNode node) { * @param ifStmt * The node to parse */ - public boolean isApplicable(IfStatement ifStmt) { + public boolean isApplicable(@NonNull IfStatement ifStmt) { // Parse IfStatement block for declarations of sentinel candidates. detectSentinels(ifStmt); @@ -250,7 +257,7 @@ public boolean isApplicable(IfStatement ifStmt) { * @param ifStmt * The node to parse */ - public boolean isApplicable(InfixExpression infix) { + public boolean isApplicable(@NonNull InfixExpression infix) { Expression leftOperand = infix.getLeftOperand(); Expression rightOperand = infix.getRightOperand(); InfixExpression.Operator operator = infix.getOperator(); @@ -265,7 +272,7 @@ public boolean isApplicable(InfixExpression infix) { * @param expr * the Expression to parse */ - private boolean isEqualityCheck(InfixExpression.Operator operator) { + private boolean isEqualityCheck(@Nullable Operator operator) { return ((operator == InfixExpression.Operator.NOT_EQUALS || operator == InfixExpression.Operator.EQUALS)); } @@ -275,7 +282,7 @@ private boolean isEqualityCheck(InfixExpression.Operator operator) { * @param expr * the Expression to parse */ - private boolean usesSentinel(Expression expr) { + private boolean usesSentinel(@NonNull Expression expr) { if (!(expr instanceof SimpleName sentinel_name)) { return false; } @@ -289,7 +296,7 @@ private boolean usesSentinel(Expression expr) { * @param ifStmt * The node to parse */ - public void detectSentinels(IfStatement ifStmt) { + public void detectSentinels(@NonNull IfStatement ifStmt) { // Check if IfStatement conditonal utilizes a null check. InfixExpression null_check = parseNullCheck(Refactoring.getSubExpressions(ifStmt.getExpression())); if (null_check == null) { @@ -336,7 +343,7 @@ public void detectSentinels(IfStatement ifStmt) { } @Override - public void apply(ASTNode node, ASTRewrite rewriter) { + public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -378,6 +385,9 @@ public void apply(ASTNode node, ASTRewrite rewriter) { Expression sent_val = sentinel_assignment.getRightHandSide(); InfixExpression null_check = sentinel.null_check; + if (null_check == null) { + continue; + } InfixExpression.Operator null_check_op = null_check.getOperator(); AST ast = node.getAST(); @@ -385,7 +395,7 @@ public void apply(ASTNode node, ASTRewrite rewriter) { boolean originalValueMatch = sent_val.resolveConstantExpressionValue() .equals(cond_val.resolveConstantExpressionValue()); replacement.setOperator(getRefactoredOperator(null_check_op, cond_op, originalValueMatch)); - rewriter.replace(expression, replacement, null); + rewriter.replace(expression, replacement, new TextEditGroup("")); } } @@ -393,19 +403,19 @@ public void apply(ASTNode node, ASTRewrite rewriter) { /** * Returns the opposite of the given InfixExpression equality operator. */ - private InfixExpression.Operator reverseOperator(InfixExpression.Operator op) { + private @NonNull Operator reverseOperator(@NonNull Operator op) { if (op == InfixExpression.Operator.EQUALS) { return InfixExpression.Operator.NOT_EQUALS; } else if (op == InfixExpression.Operator.NOT_EQUALS) { return InfixExpression.Operator.EQUALS; } - return null; + return op; } /** * Returns the conditonal operator to use in a refactored null check. */ - public InfixExpression.Operator getRefactoredOperator(Operator null_check_op, Operator sentinel_check_op, + public @NonNull Operator getRefactoredOperator(@NonNull Operator null_check_op, @NonNull Operator sentinel_check_op, boolean originalValueMatch) { Operator refactoredOperator = originalValueMatch ? null_check_op : reverseOperator(null_check_op); @@ -420,7 +430,7 @@ public InfixExpression.Operator getRefactoredOperator(Operator null_check_op, Op * @param exprs * A list of expressions to parse */ - public InfixExpression parseNullCheck(List exprs) { + public @Nullable InfixExpression parseNullCheck(@NonNull List exprs) { for (Expression expr : exprs) { if (expr instanceof InfixExpression null_check_candidate) { Expression leftOperand = null_check_candidate.getLeftOperand(); From 244654cb74038864dcc58a3c0d44f587a8414aec Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 05:09:59 -0500 Subject: [PATCH 04/25] Added type annotations and null checks --- src/main/java/BooleanFlagRefactoring.java | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 891d89c..4b81d40 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Map; +import org.checkerframework.checker.nullness.qual.*; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Assignment; @@ -18,6 +19,7 @@ import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.text.edits.TextEditGroup; /** * This class represents a refactoring in which boolean flags are replaced with @@ -30,7 +32,7 @@ public class BooleanFlagRefactoring extends Refactoring { * List of variable names identified as boolean flags, along with their * corresponding initializer expression */ - private final Map flagExpressions; + private final Map flagExpressions; /** Default constructor (for RefactoringEngine integration) */ public BooleanFlagRefactoring() { @@ -39,7 +41,7 @@ public BooleanFlagRefactoring() { } @Override - public boolean isApplicable(ASTNode node) { + public boolean isApplicable(@NonNull ASTNode node) { if (node instanceof VariableDeclarationStatement stmt) { return isApplicable(stmt); } else if (node instanceof IfStatement ifStmt) { @@ -54,7 +56,7 @@ public boolean isApplicable(ASTNode node) { * Checks to see if a VariableDeclarationStatement defines a boolean flag that * represents another variable's nullness */ - private boolean isApplicable(VariableDeclarationStatement stmt) { + private boolean isApplicable(@NonNull VariableDeclarationStatement stmt) { boolean isBooleanDeclaration = (stmt.getType() instanceof PrimitiveType pType && pType.getPrimitiveTypeCode() == PrimitiveType.BOOLEAN); @@ -66,7 +68,10 @@ private boolean isApplicable(VariableDeclarationStatement stmt) { AST ast = stmt.getAST(); // Search through all declared variables in declaration node for a booleanflag - for (VariableDeclarationFragment frag : (List) stmt.fragments()) { + @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees type is + // valid. + List fragments = (List) stmt.fragments(); + for (VariableDeclarationFragment frag : fragments) { Expression varInitializer = frag.getInitializer(); if (varInitializer == null) { continue; @@ -92,7 +97,7 @@ && getNullComparisonVariable(infix) != null) { * Analyzes an IfStatement to see if it contains a check utilizing an identified * boolean flag */ - private boolean isApplicable(IfStatement ifStmt) { + private boolean isApplicable(@NonNull IfStatement ifStmt) { List exprFragments = Refactoring.getSubExpressions(ifStmt.getExpression()); for (Expression expr : exprFragments) { if (expr instanceof InfixExpression infix && isEqualityOperator(infix.getOperator())) { @@ -111,15 +116,15 @@ private boolean isApplicable(IfStatement ifStmt) { return false; } - private boolean isFlag(SimpleName potentialFlag) { + private boolean isFlag(@NonNull SimpleName potentialFlag) { return flagExpressions.get(potentialFlag.resolveBinding()) != null; } - private boolean isEqualityOperator(Operator op) { + private boolean isEqualityOperator(@NonNull Operator op) { return (op == Operator.NOT_EQUALS || op == Operator.EQUALS); } - private SimpleName getNullComparisonVariable(InfixExpression infix) { + private @Nullable SimpleName getNullComparisonVariable(@NonNull InfixExpression infix) { Expression leftOperand = infix.getLeftOperand(); Expression rightOperand = infix.getRightOperand(); if (leftOperand instanceof SimpleName varName && rightOperand instanceof NullLiteral) { @@ -132,7 +137,7 @@ private SimpleName getNullComparisonVariable(InfixExpression infix) { } @Override - public void apply(ASTNode node, ASTRewrite rewriter) { + public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -148,13 +153,13 @@ public void apply(ASTNode node, ASTRewrite rewriter) { } } - private void apply(ASTRewrite rewriter, SimpleName flagName) { + private void apply(@NonNull ASTRewrite rewriter, @Nullable SimpleName flagName) { if (flagName == null || !isFlag(flagName)) { return; } Expression newExpr = flagExpressions.get(flagName.resolveBinding()); if (newExpr != null) { - rewriter.replace(flagName, newExpr, null); + rewriter.replace(flagName, newExpr, new TextEditGroup("")); } } @@ -163,7 +168,7 @@ private void apply(ASTRewrite rewriter, SimpleName flagName) { * Checks Assignment node to see if it re-assigns an existing boolean flag, and * if so removes the flag from flagExpressions */ - private void checkReassignment(Assignment assignmentNode) { + private void checkReassignment(@NonNull Assignment assignmentNode) { Expression lhs = assignmentNode.getLeftHandSide(); if (!(lhs instanceof SimpleName varName)) { return; From 5e66560221a145164c9c55ccf38bded631caa5e4 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 05:33:31 -0500 Subject: [PATCH 05/25] Added null checks and annotations --- src/main/java/VGRTool.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index 93096e2..c8848ac 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -18,6 +18,7 @@ import org.eclipse.jdt.core.dom.MethodInvocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.*; /** * Program entrypoint class; Runs the refactoring engine on given source code @@ -72,7 +73,7 @@ public static void main(String[] args) { * @param directory * Filepath of directory to search through (non-recursive) */ - private static List getJavaFiles(String directory) throws IOException { + private static @NonNull List getJavaFiles(@NonNull String directory) throws IOException { List javaFiles = new ArrayList<>(); Files.walk(Paths.get(directory)).filter(path -> path.toString().endsWith(".java")) @@ -88,7 +89,10 @@ private static List getJavaFiles(String directory) throws IOException { * @param refactoringModule * the refactoring to apply to the file */ - private static void processFile(File file, String refactoringModule) { + @SuppressWarnings("argument") // Supress warnings for passing null values as parameters to + // parser.setEnvironment(). Passing null values is explictly defined in the + // method documentation as acceptable + private static void processFile(@NonNull File file, @NonNull String refactoringModule) { try { // Step 3: Read the file content String content = Files.readString(file.toPath()); From ad831bb5c25e38bec33db62ed79425b7e86a9cb3 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 05:39:09 -0500 Subject: [PATCH 06/25] Added null checks and annotations --- ...NullCheckBeforeDereferenceRefactoring.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java index 364fd52..40f8c3a 100644 --- a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java +++ b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java @@ -16,8 +16,10 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.text.edits.TextEditGroup; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.*; /** * A refactoring module that replaces checks on variables whose nullness is @@ -51,7 +53,7 @@ public class AddNullCheckBeforeDereferenceRefactoring extends Refactoring { * key, ensuring global uniqueness. Two variables who have the same name but * have different scopes will have different IBinding instances. */ - private final Map validRefactors; + private final Map<@NonNull IBinding, @NonNull Expression> validRefactors; /** Default constructor (for RefactoringEngine integration) */ public AddNullCheckBeforeDereferenceRefactoring() { @@ -60,7 +62,7 @@ public AddNullCheckBeforeDereferenceRefactoring() { } @Override - public boolean isApplicable(ASTNode node) { + public boolean isApplicable(@NonNull ASTNode node) { if (node instanceof VariableDeclarationFragment varFrag) { return isApplicable(varFrag); } @@ -77,7 +79,7 @@ public boolean isApplicable(ASTNode node) { return false; } - private boolean isApplicable(VariableDeclarationFragment var) { + private boolean isApplicable(@NonNull VariableDeclarationFragment var) { Expression initializer = var.getInitializer(); if (initializer == null) return false; @@ -111,7 +113,7 @@ private boolean isApplicable(VariableDeclarationFragment var) { return false; } - private boolean isApplicable(IfStatement ifStmt) { + private boolean isApplicable(@NonNull IfStatement ifStmt) { Expression ifStmtCondition = ifStmt.getExpression(); LOGGER.debug("Analyzing if-statement: %s", ifStmtCondition); List conditionFragments = Refactoring.getSubExpressions(ifStmtCondition); @@ -145,7 +147,7 @@ private boolean isApplicable(IfStatement ifStmt) { } @Override - public void apply(ASTNode node, ASTRewrite rewriter) { + public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -173,15 +175,22 @@ public void apply(ASTNode node, ASTRewrite rewriter) { } Expression ternary = validRefactors.get(varName.resolveBinding()); + if (ternary == null) { + continue; + } AST ast = node.getAST(); ParenthesizedExpression pExpression = ast.newParenthesizedExpression(); - pExpression.setExpression((Expression) ASTNode.copySubtree(ast, ternary)); + Expression expr = (Expression) ASTNode.copySubtree(ast, ternary); + if (expr == null) { + continue; + } + pExpression.setExpression(expr); LOGGER.debug("[DEBUG] Replacing Variable: " + varName); LOGGER.debug("[DEBUG] New Value: " + pExpression); - rewriter.replace(condition, pExpression, null); + rewriter.replace(condition, pExpression, new TextEditGroup("")); } } @@ -190,7 +199,7 @@ public void apply(ASTNode node, ASTRewrite rewriter) { * Checks Assignment node to see if it re-assigns an existing valid refactoring, * and if so removes it from validRefactors */ - private void verifyRefactors(Assignment assignmentNode) { + private void verifyRefactors(@NonNull Assignment assignmentNode) { Expression lhs = assignmentNode.getLeftHandSide(); if (!(lhs instanceof SimpleName varName)) { return; From 2dab037b8bd2f1c8b0c176bafd9d39619708583d Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 19 Jan 2026 05:51:00 -0500 Subject: [PATCH 07/25] Added null checks and annotations --- src/main/java/RefactoringEngine.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/RefactoringEngine.java b/src/main/java/RefactoringEngine.java index d8c67c2..dc6cbb2 100644 --- a/src/main/java/RefactoringEngine.java +++ b/src/main/java/RefactoringEngine.java @@ -2,7 +2,9 @@ import java.util.List; import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.*; import org.apache.logging.log4j.LogManager; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; @@ -23,7 +25,7 @@ public class RefactoringEngine { */ private final List refactorings; - public RefactoringEngine(List refactoringNames) { + public RefactoringEngine(@NonNull List refactoringNames) { refactorings = new ArrayList<>(); for (String name : refactoringNames) { @@ -47,11 +49,12 @@ public RefactoringEngine(List refactoringNames) { * Applies all refactorings in {@value refactorings} to a given source file * * @param cu - * The compilation unit to use + * The compilation unit to use * @param sourceCode - * A string representing the filepath of the source code to refactor + * A string representing the filepath of the source code to + * refactor */ - public String applyRefactorings(CompilationUnit cu, String sourceCode) { + public @NonNull String applyRefactorings(@NonNull CompilationUnit cu, @NonNull String sourceCode) { AST ast = cu.getAST(); ASTRewrite rewriter = ASTRewrite.create(ast); @@ -70,7 +73,8 @@ public void preVisit(ASTNode node) { } Document document = new Document(sourceCode); - TextEdit edits = rewriter.rewriteAST(document, null); + // JavaCore.getOptions() is the default set of options used by rewriteAST() + TextEdit edits = rewriter.rewriteAST(document, JavaCore.getOptions()); try { edits.apply(document); } catch (MalformedTreeException | org.eclipse.jface.text.BadLocationException e) { From 7c067b925c0abbd1b66e878fb0915865cbd08c9a Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Wed, 4 Feb 2026 00:49:57 +0900 Subject: [PATCH 08/25] Explictly defined checker framework version --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index f3b1f99..c6444f5 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,11 @@ dependencies { implementation 'org.checkerframework:checker:3.21.0' annotationProcessor 'org.checkerframework:checker:3.21.0' + // Specify Checker Framework v3.52.0 + compileOnly("org.checkerframework:checker-qual:3.52.0") + testCompileOnly("org.checkerframework:checker-qual:3.52.0") + checkerFramework("org.checkerframework:checker:3.52.0") + // Apache Commons implementation 'commons-io:commons-io:2.16.1' From e58bb8f62365f6dce0b4d2e8607e10e788789130 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Thu, 5 Feb 2026 01:04:41 +0900 Subject: [PATCH 09/25] Spotless Applied --- src/main/java/RefactoringEngine.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/RefactoringEngine.java b/src/main/java/RefactoringEngine.java index dc6cbb2..bf0b488 100644 --- a/src/main/java/RefactoringEngine.java +++ b/src/main/java/RefactoringEngine.java @@ -49,10 +49,9 @@ public RefactoringEngine(@NonNull List refactoringNames) { * Applies all refactorings in {@value refactorings} to a given source file * * @param cu - * The compilation unit to use + * The compilation unit to use * @param sourceCode - * A string representing the filepath of the source code to - * refactor + * A string representing the filepath of the source code to refactor */ public @NonNull String applyRefactorings(@NonNull CompilationUnit cu, @NonNull String sourceCode) { AST ast = cu.getAST(); From e27ea5ea340bff6d035d19f7c1987fa19adb1c89 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Thu, 5 Feb 2026 01:12:16 +0900 Subject: [PATCH 10/25] Updated comments --- src/main/java/VGRTool.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index c8848ac..dd5beee 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -90,8 +90,9 @@ public static void main(String[] args) { * the refactoring to apply to the file */ @SuppressWarnings("argument") // Supress warnings for passing null values as parameters to - // parser.setEnvironment(). Passing null values is explictly defined in the - // method documentation as acceptable + // parser.setEnvironment() and parser.createAST(). Passing null values is + // explictly defined in the + // method documentations as acceptable private static void processFile(@NonNull File file, @NonNull String refactoringModule) { try { // Step 3: Read the file content From 6235356de0d77ccd6662cde8a5442f38e18b69c9 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Thu, 5 Feb 2026 01:34:48 +0900 Subject: [PATCH 11/25] Updated testing engine to remove null errors and applied Spotless --- src/test/java/TestingEngine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/TestingEngine.java b/src/test/java/TestingEngine.java index 6036649..8ab5cb4 100644 --- a/src/test/java/TestingEngine.java +++ b/src/test/java/TestingEngine.java @@ -35,6 +35,10 @@ public static void testSingleRefactoring(String input, String expectedOutput, St runTest(input, expectedOutput, new RefactoringEngine(Collections.singletonList(refactoring))); } + @SuppressWarnings("argument") // Supress warnings for passing null values as parameters to + // parser.setEnvironment() and parser.createAST(). Passing null values is + // explictly defined in the + // method documentations as acceptable private static void runTest(String input, String expectedOutput, RefactoringEngine engine) { // Set parser source code parser.setSource(input.toCharArray()); From 8e0e8ef010561a6f4b8e5e78a78db3f9de2438a4 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:33:05 -0500 Subject: [PATCH 12/25] Removed wildcard inputs --- src/main/java/AddNullCheckBeforeDereferenceRefactoring.java | 2 +- src/main/java/BooleanFlagRefactoring.java | 3 ++- src/main/java/NestedNullRefactoring.java | 2 +- src/main/java/RefactoringEngine.java | 2 +- src/main/java/SentinelRefactoring.java | 3 ++- src/main/java/VGRTool.java | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java index 40f8c3a..f90d14f 100644 --- a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java +++ b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java @@ -19,7 +19,7 @@ import org.eclipse.text.edits.TextEditGroup; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; /** * A refactoring module that replaces checks on variables whose nullness is diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 4b81d40..577397a 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -2,7 +2,8 @@ import java.util.List; import java.util.Map; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Assignment; diff --git a/src/main/java/NestedNullRefactoring.java b/src/main/java/NestedNullRefactoring.java index 0ccee78..09cb569 100644 --- a/src/main/java/NestedNullRefactoring.java +++ b/src/main/java/NestedNullRefactoring.java @@ -2,7 +2,7 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; diff --git a/src/main/java/RefactoringEngine.java b/src/main/java/RefactoringEngine.java index bf0b488..cdb422b 100644 --- a/src/main/java/RefactoringEngine.java +++ b/src/main/java/RefactoringEngine.java @@ -2,7 +2,7 @@ import java.util.List; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; import org.apache.logging.log4j.LogManager; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index d83bee8..19e4131 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -27,7 +27,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; /** * This class represents a refactoring in which integer variables whose values diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index dd5beee..b35e9de 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.core.dom.MethodInvocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.*; +import org.checkerframework.checker.nullness.qual.NonNull; /** * Program entrypoint class; Runs the refactoring engine on given source code From 7721582d46602d390f333a678265e7fb0b480712 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:38:49 -0500 Subject: [PATCH 13/25] Removed NonNull annotations on parameter types for Boolean-Flag --- src/main/java/BooleanFlagRefactoring.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 577397a..eff703e 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -33,7 +33,7 @@ public class BooleanFlagRefactoring extends Refactoring { * List of variable names identified as boolean flags, along with their * corresponding initializer expression */ - private final Map flagExpressions; + private final Map flagExpressions; /** Default constructor (for RefactoringEngine integration) */ public BooleanFlagRefactoring() { @@ -42,7 +42,7 @@ public BooleanFlagRefactoring() { } @Override - public boolean isApplicable(@NonNull ASTNode node) { + public boolean isApplicable(ASTNode node) { if (node instanceof VariableDeclarationStatement stmt) { return isApplicable(stmt); } else if (node instanceof IfStatement ifStmt) { @@ -57,7 +57,7 @@ public boolean isApplicable(@NonNull ASTNode node) { * Checks to see if a VariableDeclarationStatement defines a boolean flag that * represents another variable's nullness */ - private boolean isApplicable(@NonNull VariableDeclarationStatement stmt) { + private boolean isApplicable(VariableDeclarationStatement stmt) { boolean isBooleanDeclaration = (stmt.getType() instanceof PrimitiveType pType && pType.getPrimitiveTypeCode() == PrimitiveType.BOOLEAN); @@ -98,7 +98,7 @@ && getNullComparisonVariable(infix) != null) { * Analyzes an IfStatement to see if it contains a check utilizing an identified * boolean flag */ - private boolean isApplicable(@NonNull IfStatement ifStmt) { + private boolean isApplicable(IfStatement ifStmt) { List exprFragments = Refactoring.getSubExpressions(ifStmt.getExpression()); for (Expression expr : exprFragments) { if (expr instanceof InfixExpression infix && isEqualityOperator(infix.getOperator())) { @@ -117,15 +117,15 @@ private boolean isApplicable(@NonNull IfStatement ifStmt) { return false; } - private boolean isFlag(@NonNull SimpleName potentialFlag) { + private boolean isFlag(SimpleName potentialFlag) { return flagExpressions.get(potentialFlag.resolveBinding()) != null; } - private boolean isEqualityOperator(@NonNull Operator op) { + private boolean isEqualityOperator(Operator op) { return (op == Operator.NOT_EQUALS || op == Operator.EQUALS); } - private @Nullable SimpleName getNullComparisonVariable(@NonNull InfixExpression infix) { + private @Nullable SimpleName getNullComparisonVariable(InfixExpression infix) { Expression leftOperand = infix.getLeftOperand(); Expression rightOperand = infix.getRightOperand(); if (leftOperand instanceof SimpleName varName && rightOperand instanceof NullLiteral) { @@ -138,7 +138,7 @@ private boolean isEqualityOperator(@NonNull Operator op) { } @Override - public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { + public void apply(ASTNode node, ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -154,7 +154,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { } } - private void apply(@NonNull ASTRewrite rewriter, @Nullable SimpleName flagName) { + private void apply(ASTRewrite rewriter, @Nullable SimpleName flagName) { if (flagName == null || !isFlag(flagName)) { return; } @@ -169,7 +169,7 @@ private void apply(@NonNull ASTRewrite rewriter, @Nullable SimpleName flagName) * Checks Assignment node to see if it re-assigns an existing boolean flag, and * if so removes the flag from flagExpressions */ - private void checkReassignment(@NonNull Assignment assignmentNode) { + private void checkReassignment(Assignment assignmentNode) { Expression lhs = assignmentNode.getLeftHandSide(); if (!(lhs instanceof SimpleName varName)) { return; From 8701fcbc0170da784009b014f6b64771a86ccbdb Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:39:30 -0500 Subject: [PATCH 14/25] Removed NonNull annotations on parameter types for AddNullCheck --- .../AddNullCheckBeforeDereferenceRefactoring.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java index f90d14f..d8129e2 100644 --- a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java +++ b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java @@ -19,7 +19,6 @@ import org.eclipse.text.edits.TextEditGroup; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.NonNull; /** * A refactoring module that replaces checks on variables whose nullness is @@ -53,7 +52,7 @@ public class AddNullCheckBeforeDereferenceRefactoring extends Refactoring { * key, ensuring global uniqueness. Two variables who have the same name but * have different scopes will have different IBinding instances. */ - private final Map<@NonNull IBinding, @NonNull Expression> validRefactors; + private final Map validRefactors; /** Default constructor (for RefactoringEngine integration) */ public AddNullCheckBeforeDereferenceRefactoring() { @@ -62,7 +61,7 @@ public AddNullCheckBeforeDereferenceRefactoring() { } @Override - public boolean isApplicable(@NonNull ASTNode node) { + public boolean isApplicable(ASTNode node) { if (node instanceof VariableDeclarationFragment varFrag) { return isApplicable(varFrag); } @@ -79,7 +78,7 @@ public boolean isApplicable(@NonNull ASTNode node) { return false; } - private boolean isApplicable(@NonNull VariableDeclarationFragment var) { + private boolean isApplicable(VariableDeclarationFragment var) { Expression initializer = var.getInitializer(); if (initializer == null) return false; @@ -113,7 +112,7 @@ private boolean isApplicable(@NonNull VariableDeclarationFragment var) { return false; } - private boolean isApplicable(@NonNull IfStatement ifStmt) { + private boolean isApplicable(IfStatement ifStmt) { Expression ifStmtCondition = ifStmt.getExpression(); LOGGER.debug("Analyzing if-statement: %s", ifStmtCondition); List conditionFragments = Refactoring.getSubExpressions(ifStmtCondition); @@ -147,7 +146,7 @@ private boolean isApplicable(@NonNull IfStatement ifStmt) { } @Override - public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { + public void apply(ASTNode node, ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -199,7 +198,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { * Checks Assignment node to see if it re-assigns an existing valid refactoring, * and if so removes it from validRefactors */ - private void verifyRefactors(@NonNull Assignment assignmentNode) { + private void verifyRefactors(Assignment assignmentNode) { Expression lhs = assignmentNode.getLeftHandSide(); if (!(lhs instanceof SimpleName varName)) { return; From b218a5855b2d836ed8d3208975ef0184e886c821 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:40:19 -0500 Subject: [PATCH 15/25] Removed NonNull annotations on parameter types for Refactoring Engine --- src/main/java/RefactoringEngine.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/RefactoringEngine.java b/src/main/java/RefactoringEngine.java index cdb422b..7bbb5ec 100644 --- a/src/main/java/RefactoringEngine.java +++ b/src/main/java/RefactoringEngine.java @@ -25,7 +25,7 @@ public class RefactoringEngine { */ private final List refactorings; - public RefactoringEngine(@NonNull List refactoringNames) { + public RefactoringEngine(List refactoringNames) { refactorings = new ArrayList<>(); for (String name : refactoringNames) { @@ -53,7 +53,7 @@ public RefactoringEngine(@NonNull List refactoringNames) { * @param sourceCode * A string representing the filepath of the source code to refactor */ - public @NonNull String applyRefactorings(@NonNull CompilationUnit cu, @NonNull String sourceCode) { + public @NonNull String applyRefactorings(CompilationUnit cu, String sourceCode) { AST ast = cu.getAST(); ASTRewrite rewriter = ASTRewrite.create(ast); From bc8f3a4cd6c257f6aef66020810e58931dea344e Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:42:26 -0500 Subject: [PATCH 16/25] Removed NonNull annotations on parameter types for SentinelRefactoring --- src/main/java/SentinelRefactoring.java | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index 19e4131..6fe04ae 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -47,7 +47,7 @@ public class SentinelRefactoring extends Refactoring { * the key, ensuring global uniqueness. Two variables who have the same name but * have different scopes will have different IBinding instances. */ - private final Map sentinelCandidates; + private final Map sentinelCandidates; /** * Set of all sentinel assignments which have already been parsed; Used to * prevent repeated parsing of same sentinel assignment. @@ -96,7 +96,7 @@ public SentinelRefactoring() { * Detects reassignments of existing sentinels If reassignment is detected, * removes the sentinel from the list of valid sentinels. */ - private void detectReassignment(@NonNull Assignment assignmentNode) { + private void detectReassignment(Assignment assignmentNode) { // Skip assignments that initially define a sentinel. if (sentinelAssignments.contains(assignmentNode)) { return; @@ -114,7 +114,7 @@ private void detectReassignment(@NonNull Assignment assignmentNode) { /* * Detects sentinels which are shadowed by new local variables and removes them. */ - private void detectShadowing(@NonNull VariableDeclarationStatement declaration) { + private void detectShadowing(VariableDeclarationStatement declaration) { @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees type is // valid. List fragments = declaration.fragments(); @@ -140,8 +140,8 @@ private void detectShadowing(@NonNull VariableDeclarationStatement declaration) * The value assigned to the sentinel when the null_check condition * is true */ - private boolean isValidSentinel(@NonNull Assignment sentinel_assignment, @NonNull Expression null_check, - @NonNull Object newValue) { + private boolean isValidSentinel(Assignment sentinel_assignment, Expression null_check, + Object newValue) { LOGGER.debug("Parsing Sentinel: %s, %s, %s", sentinel_assignment, null_check, newValue); @@ -175,7 +175,7 @@ private boolean isValidSentinel(@NonNull Assignment sentinel_assignment, @NonNul } - private void updateSentinel(@NonNull ASTNode node) { + private void updateSentinel(ASTNode node) { if (node instanceof VariableDeclaration declaration) { updateSentinel(declaration); } else if (node instanceof Assignment assign) { @@ -193,13 +193,13 @@ private void updateSentinel(@NonNull ASTNode node) { } } - private void updateSentinel(@NonNull VariableDeclaration declaration) { + private void updateSentinel(VariableDeclaration declaration) { IBinding key = declaration.getName().resolveBinding(); Object newValue = declaration.getInitializer().resolveConstantExpressionValue(); updateSentinel(key, newValue); } - private void updateSentinel(@NonNull Assignment statement) { + private void updateSentinel(Assignment statement) { if (!(statement.getLeftHandSide() instanceof SimpleName varName)) { return; } @@ -208,7 +208,7 @@ private void updateSentinel(@NonNull Assignment statement) { updateSentinel(key, newValue); } - private void updateSentinel(@NonNull IBinding key, @NonNull Object newValue) { + private void updateSentinel(IBinding key, Object newValue) { Sentinel sentinel = sentinelCandidates.get(key); if (sentinel == null) { sentinelCandidates.put(key, new Sentinel(null, null, newValue)); @@ -218,7 +218,7 @@ private void updateSentinel(@NonNull IBinding key, @NonNull Object newValue) { } @Override - public boolean isApplicable(@NonNull ASTNode node) { + public boolean isApplicable(ASTNode node) { updateSentinel(node); if (node instanceof IfStatement ifStmt && isApplicable(ifStmt)) { return true; @@ -238,7 +238,7 @@ public boolean isApplicable(@NonNull ASTNode node) { * @param ifStmt * The node to parse */ - public boolean isApplicable(@NonNull IfStatement ifStmt) { + public boolean isApplicable(IfStatement ifStmt) { // Parse IfStatement block for declarations of sentinel candidates. detectSentinels(ifStmt); @@ -258,7 +258,7 @@ public boolean isApplicable(@NonNull IfStatement ifStmt) { * @param ifStmt * The node to parse */ - public boolean isApplicable(@NonNull InfixExpression infix) { + public boolean isApplicable(InfixExpression infix) { Expression leftOperand = infix.getLeftOperand(); Expression rightOperand = infix.getRightOperand(); InfixExpression.Operator operator = infix.getOperator(); @@ -283,7 +283,7 @@ private boolean isEqualityCheck(@Nullable Operator operator) { * @param expr * the Expression to parse */ - private boolean usesSentinel(@NonNull Expression expr) { + private boolean usesSentinel(Expression expr) { if (!(expr instanceof SimpleName sentinel_name)) { return false; } @@ -297,7 +297,7 @@ private boolean usesSentinel(@NonNull Expression expr) { * @param ifStmt * The node to parse */ - public void detectSentinels(@NonNull IfStatement ifStmt) { + public void detectSentinels(IfStatement ifStmt) { // Check if IfStatement conditonal utilizes a null check. InfixExpression null_check = parseNullCheck(Refactoring.getSubExpressions(ifStmt.getExpression())); if (null_check == null) { @@ -344,7 +344,7 @@ public void detectSentinels(@NonNull IfStatement ifStmt) { } @Override - public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { + public void apply(ASTNode node, ASTRewrite rewriter) { if (!(node instanceof IfStatement ifStmt)) { return; } @@ -404,7 +404,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { /** * Returns the opposite of the given InfixExpression equality operator. */ - private @NonNull Operator reverseOperator(@NonNull Operator op) { + private @NonNull Operator reverseOperator(Operator op) { if (op == InfixExpression.Operator.EQUALS) { return InfixExpression.Operator.NOT_EQUALS; } else if (op == InfixExpression.Operator.NOT_EQUALS) { @@ -416,7 +416,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { /** * Returns the conditonal operator to use in a refactored null check. */ - public @NonNull Operator getRefactoredOperator(@NonNull Operator null_check_op, @NonNull Operator sentinel_check_op, + public @NonNull Operator getRefactoredOperator(Operator null_check_op, Operator sentinel_check_op, boolean originalValueMatch) { Operator refactoredOperator = originalValueMatch ? null_check_op : reverseOperator(null_check_op); @@ -431,7 +431,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { * @param exprs * A list of expressions to parse */ - public @Nullable InfixExpression parseNullCheck(@NonNull List exprs) { + public @Nullable InfixExpression parseNullCheck(List exprs) { for (Expression expr : exprs) { if (expr instanceof InfixExpression null_check_candidate) { Expression leftOperand = null_check_candidate.getLeftOperand(); From 3ccb62e990e03ace97b9d19eac8e382f04123458 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:42:49 -0500 Subject: [PATCH 17/25] Removed NonNull annotations on parameter types for VGRTool --- src/main/java/VGRTool.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index b35e9de..c6cf7c2 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -73,7 +73,7 @@ public static void main(String[] args) { * @param directory * Filepath of directory to search through (non-recursive) */ - private static @NonNull List getJavaFiles(@NonNull String directory) throws IOException { + private static @NonNull List getJavaFiles(String directory) throws IOException { List javaFiles = new ArrayList<>(); Files.walk(Paths.get(directory)).filter(path -> path.toString().endsWith(".java")) @@ -93,7 +93,7 @@ public static void main(String[] args) { // parser.setEnvironment() and parser.createAST(). Passing null values is // explictly defined in the // method documentations as acceptable - private static void processFile(@NonNull File file, @NonNull String refactoringModule) { + private static void processFile(File file, String refactoringModule) { try { // Step 3: Read the file content String content = Files.readString(file.toPath()); From 8b56f3582706d5731410256d1be6149b1e6c046a Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:46:58 -0500 Subject: [PATCH 18/25] Removed NonNull annotations on parameter types for Boolean-Flag --- src/main/java/BooleanFlagRefactoring.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index eff703e..63dd217 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; From e5bf52b7765ea95037abd57b9febe3c1c5001120 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:47:38 -0500 Subject: [PATCH 19/25] Removed NonNull annotations on parameter types for NestedNullRefactoring --- src/main/java/NestedNullRefactoring.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/NestedNullRefactoring.java b/src/main/java/NestedNullRefactoring.java index 09cb569..ba927b1 100644 --- a/src/main/java/NestedNullRefactoring.java +++ b/src/main/java/NestedNullRefactoring.java @@ -2,7 +2,6 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import org.checkerframework.checker.nullness.qual.NonNull; import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; @@ -33,14 +32,14 @@ public class NestedNullRefactoring extends Refactoring { public static final String NAME = "NestedNullRefactoring"; - private final Dictionary applicableMethods; + private final Dictionary applicableMethods; public NestedNullRefactoring() { applicableMethods = new Hashtable<>(); } @Override - public boolean isApplicable(@NonNull ASTNode node) { + public boolean isApplicable(ASTNode node) { if (node instanceof MethodInvocation invocation) { return isApplicableImpl(invocation); } @@ -56,7 +55,7 @@ public boolean isApplicable(@NonNull ASTNode node) { * Returns true iff the provided invocation is of a registered one-line method * that returns the result of a null check */ - private boolean isApplicableImpl(@NonNull MethodInvocation invocation) { + private boolean isApplicableImpl(MethodInvocation invocation) { if (applicableMethods.get(invocation.resolveMethodBinding()) != null) { System.out.println("[DEBUG] Invocation of applicable method found"); return true; @@ -68,7 +67,7 @@ private boolean isApplicableImpl(@NonNull MethodInvocation invocation) { * Returns true iff Node is a one-line private method that returns the result of * a null check */ - private boolean isApplicableImpl(@NonNull MethodDeclaration declaration) { + private boolean isApplicableImpl(MethodDeclaration declaration) { // getReturnType() is deprecated and replaced by getReturnType2() Type retType = declaration.getReturnType2(); boolean returnsBoolean = (retType.isPrimitiveType() @@ -138,12 +137,12 @@ private boolean isApplicableImpl(@NonNull MethodDeclaration declaration) { * Returns true iff the provided expression can be on one side of a refactorable * null equality check, i.e. it represents a valid variable or constant. */ - private boolean isValidOperand(@NonNull Expression operand) { + private boolean isValidOperand(Expression operand) { return (operand instanceof SimpleName || operand instanceof FieldAccess || operand instanceof QualifiedName); } @Override - public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { + public void apply(ASTNode node, ASTRewrite rewriter) { // Check if Method Invocation is in applicableMethods if (node instanceof MethodInvocation invocation) { replace(node, rewriter, invocation); @@ -153,7 +152,7 @@ public void apply(@NonNull ASTNode node, @NonNull ASTRewrite rewriter) { } } - private void replace(@NonNull ASTNode node, @NonNull ASTRewrite rewriter, @NonNull MethodInvocation invocation) { + private void replace(ASTNode node, ASTRewrite rewriter, MethodInvocation invocation) { IMethodBinding binding = invocation.resolveMethodBinding(); if (binding == null) { return; From 04d3c3d1bde6436b19d6275cc4952d40ab6a9faf Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 05:47:57 -0500 Subject: [PATCH 20/25] Applied spotless --- src/main/java/SentinelRefactoring.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index 6fe04ae..0bcfabc 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -140,8 +140,7 @@ private void detectShadowing(VariableDeclarationStatement declaration) { * The value assigned to the sentinel when the null_check condition * is true */ - private boolean isValidSentinel(Assignment sentinel_assignment, Expression null_check, - Object newValue) { + private boolean isValidSentinel(Assignment sentinel_assignment, Expression null_check, Object newValue) { LOGGER.debug("Parsing Sentinel: %s, %s, %s", sentinel_assignment, null_check, newValue); From bc3cf67d69bd0f214e20948ed715e279b2da459a Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 06:04:50 -0500 Subject: [PATCH 21/25] Updated BooleanFlagRefactoring commments to better explain SuppressWarnings usage --- src/main/java/BooleanFlagRefactoring.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 63dd217..71971a1 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -19,7 +19,6 @@ import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.InfixExpression.Operator; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.text.edits.TextEditGroup; /** * This class represents a refactoring in which boolean flags are replaced with @@ -68,8 +67,9 @@ private boolean isApplicable(VariableDeclarationStatement stmt) { AST ast = stmt.getAST(); // Search through all declared variables in declaration node for a booleanflag - @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees type is - // valid. + @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees it returns + // a live + // list of type VariableDeclarationFragment List fragments = (List) stmt.fragments(); for (VariableDeclarationFragment frag : fragments) { Expression varInitializer = frag.getInitializer(); @@ -153,13 +153,17 @@ public void apply(ASTNode node, ASTRewrite rewriter) { } } + @SuppressWarnings("nullness:argument") // Supress warnings for passing a null values as the third parameter to + // rewriter.replace(). The parameter is used to collect a list of edits that are + // performed. Passing null is documented as acceptable if we do need to collect + // the text edits private void apply(ASTRewrite rewriter, @Nullable SimpleName flagName) { if (flagName == null || !isFlag(flagName)) { return; } Expression newExpr = flagExpressions.get(flagName.resolveBinding()); if (newExpr != null) { - rewriter.replace(flagName, newExpr, new TextEditGroup("")); + rewriter.replace(flagName, newExpr, null); } } From 31243d504b69574438e4f1bfc7e9adf615771f52 Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 06:06:07 -0500 Subject: [PATCH 22/25] Reduced scope of SuppressWarnings in VGRTool.java --- src/main/java/VGRTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index c6cf7c2..2cdaf80 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -89,7 +89,7 @@ public static void main(String[] args) { * @param refactoringModule * the refactoring to apply to the file */ - @SuppressWarnings("argument") // Supress warnings for passing null values as parameters to + @SuppressWarnings("nullness:argument") // Supress warnings for passing null values as parameters to // parser.setEnvironment() and parser.createAST(). Passing null values is // explictly defined in the // method documentations as acceptable From 3bed31ac4f9884f23635016bdd39e39165e8483a Mon Sep 17 00:00:00 2001 From: Possiblyai Date: Mon, 9 Feb 2026 06:36:59 -0500 Subject: [PATCH 23/25] Updated comments for clarity --- src/main/java/SentinelRefactoring.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index 0bcfabc..b1092f3 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -60,11 +60,14 @@ public class SentinelRefactoring extends Refactoring { */ private class Sentinel { /** - * The original assignment statement setting the sentinel's value. + * The original assignment statement setting the sentinel's value. A null value + * indicates the sentinel has not yet been assigned a value */ public @Nullable Assignment sentinel_assignment; /** - * The conditional expression used to decide the value of the sentinel. + * The conditional expression used to decide the value of the sentinel. A null + * value indicates a variable which could become a sentinel, but has not yet had + * a conditional assignemnt. */ public @Nullable InfixExpression null_check; /** From 053f18426b068add8593805cfea90f968c8ca0b4 Mon Sep 17 00:00:00 2001 From: AI Date: Mon, 16 Feb 2026 06:00:29 -0500 Subject: [PATCH 24/25] Replaced suppress warning annotations with stubfiles --- build.gradle | 3 +++ .../AddNullCheckBeforeDereferenceRefactoring.java | 3 +-- src/main/java/BooleanFlagRefactoring.java | 4 ---- src/main/java/NestedNullRefactoring.java | 3 +-- src/main/java/RefactoringEngine.java | 5 ++--- src/main/java/SentinelRefactoring.java | 3 +-- src/main/java/VGRTool.java | 4 ---- src/main/stubs/parser.astub | 11 +++++++++++ src/main/stubs/rewrite.astub | 9 +++++++++ src/test/java/TestingEngine.java | 4 ---- 10 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 src/main/stubs/parser.astub create mode 100644 src/main/stubs/rewrite.astub diff --git a/build.gradle b/build.gradle index c6444f5..fcc9159 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,9 @@ checkerFramework { checkers = [ "org.checkerframework.checker.nullness.NullnessChecker", ] +extraJavacArgs = [ +"-Astubs=$projectDir/src/main/stubs" +] } spotless { diff --git a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java index d8129e2..cf1d812 100644 --- a/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java +++ b/src/main/java/AddNullCheckBeforeDereferenceRefactoring.java @@ -16,7 +16,6 @@ import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.text.edits.TextEditGroup; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -189,7 +188,7 @@ public void apply(ASTNode node, ASTRewrite rewriter) { LOGGER.debug("[DEBUG] Replacing Variable: " + varName); LOGGER.debug("[DEBUG] New Value: " + pExpression); - rewriter.replace(condition, pExpression, new TextEditGroup("")); + rewriter.replace(condition, pExpression, null); } } diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 71971a1..5bef382 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -153,10 +153,6 @@ public void apply(ASTNode node, ASTRewrite rewriter) { } } - @SuppressWarnings("nullness:argument") // Supress warnings for passing a null values as the third parameter to - // rewriter.replace(). The parameter is used to collect a list of edits that are - // performed. Passing null is documented as acceptable if we do need to collect - // the text edits private void apply(ASTRewrite rewriter, @Nullable SimpleName flagName) { if (flagName == null || !isFlag(flagName)) { return; diff --git a/src/main/java/NestedNullRefactoring.java b/src/main/java/NestedNullRefactoring.java index ba927b1..5113836 100644 --- a/src/main/java/NestedNullRefactoring.java +++ b/src/main/java/NestedNullRefactoring.java @@ -2,7 +2,6 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Block; @@ -172,7 +171,7 @@ private void replace(ASTNode node, ASTRewrite rewriter, MethodInvocation invocat pExpr.setExpression(copiedExpression); System.out.println("Refactoring " + node + "\n\tReplacing \n\t" + invocation + "\n\tWith \n\t" + pExpr); - rewriter.replace(invocation, pExpr, new TextEditGroup("")); + rewriter.replace(invocation, pExpr, null); } diff --git a/src/main/java/RefactoringEngine.java b/src/main/java/RefactoringEngine.java index 7bbb5ec..7475bac 100644 --- a/src/main/java/RefactoringEngine.java +++ b/src/main/java/RefactoringEngine.java @@ -4,7 +4,6 @@ import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.apache.logging.log4j.LogManager; -import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTVisitor; @@ -72,8 +71,8 @@ public void preVisit(ASTNode node) { } Document document = new Document(sourceCode); - // JavaCore.getOptions() is the default set of options used by rewriteAST() - TextEdit edits = rewriter.rewriteAST(document, JavaCore.getOptions()); + + TextEdit edits = rewriter.rewriteAST(document, null); try { edits.apply(document); } catch (MalformedTreeException | org.eclipse.jface.text.BadLocationException e) { diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index b1092f3..7947b13 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.core.dom.NullLiteral; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.text.edits.TextEditGroup; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.VariableDeclaration; @@ -398,7 +397,7 @@ public void apply(ASTNode node, ASTRewrite rewriter) { boolean originalValueMatch = sent_val.resolveConstantExpressionValue() .equals(cond_val.resolveConstantExpressionValue()); replacement.setOperator(getRefactoredOperator(null_check_op, cond_op, originalValueMatch)); - rewriter.replace(expression, replacement, new TextEditGroup("")); + rewriter.replace(expression, replacement, null); } } diff --git a/src/main/java/VGRTool.java b/src/main/java/VGRTool.java index 2cdaf80..85cb167 100644 --- a/src/main/java/VGRTool.java +++ b/src/main/java/VGRTool.java @@ -89,10 +89,6 @@ public static void main(String[] args) { * @param refactoringModule * the refactoring to apply to the file */ - @SuppressWarnings("nullness:argument") // Supress warnings for passing null values as parameters to - // parser.setEnvironment() and parser.createAST(). Passing null values is - // explictly defined in the - // method documentations as acceptable private static void processFile(File file, String refactoringModule) { try { // Step 3: Read the file content diff --git a/src/main/stubs/parser.astub b/src/main/stubs/parser.astub new file mode 100644 index 0000000..499c995 --- /dev/null +++ b/src/main/stubs/parser.astub @@ -0,0 +1,11 @@ +package org.eclipse.jdt.core.dom; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.eclipse.jdt.core.dom.ASTParser; + +public class ASTParser extends Object { + public void setEnvironment(String[] classpathEntries, String @Nullable [] sourcepathEntries, String @Nullable [] encodings, boolean includeRunningVMBootclasspath); + public ASTNode createAST(@Nullable org.eclipse.core.runtime.IProgressMonitor monitor); + +} diff --git a/src/main/stubs/rewrite.astub b/src/main/stubs/rewrite.astub new file mode 100644 index 0000000..c55fe9f --- /dev/null +++ b/src/main/stubs/rewrite.astub @@ -0,0 +1,9 @@ +package org.eclipse.jdt.core.dom.rewrite; + +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class ASTRewrite extends Object { + public org.eclipse.text.edits.TextEdit rewriteAST(org.eclipse.jface.text.IDocument document, @Nullable Map options) throws JavaModelException, IllegalArgumentException; + public final void replace(ASTNode node, ASTNode replacement, @Nullable org.eclipse.text.edits.TextEditGroup editGroup); +} diff --git a/src/test/java/TestingEngine.java b/src/test/java/TestingEngine.java index 8ab5cb4..6036649 100644 --- a/src/test/java/TestingEngine.java +++ b/src/test/java/TestingEngine.java @@ -35,10 +35,6 @@ public static void testSingleRefactoring(String input, String expectedOutput, St runTest(input, expectedOutput, new RefactoringEngine(Collections.singletonList(refactoring))); } - @SuppressWarnings("argument") // Supress warnings for passing null values as parameters to - // parser.setEnvironment() and parser.createAST(). Passing null values is - // explictly defined in the - // method documentations as acceptable private static void runTest(String input, String expectedOutput, RefactoringEngine engine) { // Set parser source code parser.setSource(input.toCharArray()); From b3cb66ae51f5aaa1f22280a9595d2c797d5d1a83 Mon Sep 17 00:00:00 2001 From: AI Date: Mon, 16 Feb 2026 06:12:29 -0500 Subject: [PATCH 25/25] Added link to documentation for SuppressWarnings comments --- src/main/java/BooleanFlagRefactoring.java | 8 +++++--- src/main/java/NestedNullRefactoring.java | 6 +++++- src/main/java/SentinelRefactoring.java | 14 ++++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/BooleanFlagRefactoring.java b/src/main/java/BooleanFlagRefactoring.java index 5bef382..9634aed 100644 --- a/src/main/java/BooleanFlagRefactoring.java +++ b/src/main/java/BooleanFlagRefactoring.java @@ -67,9 +67,11 @@ private boolean isApplicable(VariableDeclarationStatement stmt) { AST ast = stmt.getAST(); // Search through all declared variables in declaration node for a booleanflag - @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees it returns - // a live - // list of type VariableDeclarationFragment + // Eclipse JDT API guarantees fragments() returns a live + // List + // See + // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/VariableDeclarationStatement.html#fragments() + @SuppressWarnings("unchecked") List fragments = (List) stmt.fragments(); for (VariableDeclarationFragment frag : fragments) { Expression varInitializer = frag.getInitializer(); diff --git a/src/main/java/NestedNullRefactoring.java b/src/main/java/NestedNullRefactoring.java index 5113836..8d33f83 100644 --- a/src/main/java/NestedNullRefactoring.java +++ b/src/main/java/NestedNullRefactoring.java @@ -93,7 +93,11 @@ private boolean isApplicableImpl(MethodDeclaration declaration) { return false; } - @SuppressWarnings("unchecked") // Silence type warnings; statements() documentation guarantees type is valid. + // Eclipse JDT API guarantees statements() returns a live + // List + // See + // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/Block.html#statements() + @SuppressWarnings("unchecked") List stmts = (List) body.statements(); boolean isOneLine = stmts.size() == 1; diff --git a/src/main/java/SentinelRefactoring.java b/src/main/java/SentinelRefactoring.java index 7947b13..1bf3b78 100644 --- a/src/main/java/SentinelRefactoring.java +++ b/src/main/java/SentinelRefactoring.java @@ -117,8 +117,11 @@ private void detectReassignment(Assignment assignmentNode) { * Detects sentinels which are shadowed by new local variables and removes them. */ private void detectShadowing(VariableDeclarationStatement declaration) { - @SuppressWarnings("unchecked") // Silence type warnings; fragments() documentation guarantees type is - // valid. + // Eclipse JDT API guarantees fragments() returns a live + // List + // See + // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/VariableDeclarationStatement.html#fragments() + @SuppressWarnings("unchecked") List fragments = declaration.fragments(); for (VariableDeclarationFragment fragment : fragments) { SimpleName varName = fragment.getName(); @@ -309,8 +312,11 @@ public void detectSentinels(IfStatement ifStmt) { return; } - @SuppressWarnings("unchecked") // Silence type warnings; statements() documentation guarantees type is - // valid. + // Eclipse JDT API guarantees statements() returns a live + // List + // See + // https://help.eclipse.org/latest/topic/org.eclipse.jdt.doc.isv/reference/api/org/eclipse/jdt/core/dom/Block.html#statements() + @SuppressWarnings("unchecked") List stmts = thenStmt.statements(); // Checks that there is only one line in the ifStatement.