Repository: uber/piranha (language: python)

Issue:
[Piranha Generic] Incomplete Enum cleanup compared to the behavior with PiranhaJava
PiranhaJava basically removes not only the treated part, but it also removes the enum values which are now going to be obsolete and have no usages.

But I don't see the same behaviour when working with `Piranha Generic`.

Here's an example

**Original Java class**

```java
class SampleJava {

  public void sampleMethod(ExperimentInterface exp) {
    if (exp.isToggleEnabled(MyEnum.SAMPLE_STALE_FLAG)) {
      System.out.println("SAMPLE_STALE_FLAG is enabled");
    } else {
      System.out.println("SAMPLE_STALE_FLAG is disabled");
    }
  }

  public void sampleMethod1(ExperimentInterface exp) {
    if (exp.isToggleDisabled(ExpEnum.SAMPLE_STALE_FLAG)) {
      System.out.println("SAMPLE_STALE_FLAG is disabled");
    } else {
      System.out.println("SAMPLE_STALE_FLAG is enabled");
    }
  }

  public enum MyEnum{
    SAMPLE_STALE_FLAG,
    DRAGON_WARRIOR
  }
}
```

Now with the below arguments and configuration file, when I run the piranha cli, I notice the output class looks like below

**Altered Java class by Piranha**

```java
class SampleJava {

  public void sampleMethod(ExperimentInterface exp) {
    System.out.println("SAMPLE_STALE_FLAG is enabled");
  }

  public void sampleMethod1(ExperimentInterface exp) {
    if (exp.isToggleDisabled(ExpEnum.SAMPLE_STALE_FLAG)) {
      System.out.println("SAMPLE_STALE_FLAG is disabled");
    } else {
      System.out.println("SAMPLE_STALE_FLAG is enabled");
    }
  }

  public enum MyEnum{
    SAMPLE_STALE_FLAG,
    DRAGON_WARRIOR
  }
}
```

The java based piranha was removing the stale entry in my enum `MyEnum`, but piranha generic doesn't do that. It only removes the enum reference from the method, but not from the actual enum as well.

`piranha_arguments.toml`

```
language = ["java"]
substitutions = [
    ["stale_flag_name", "MyEnum.SAMPLE_STALE_FLAG"],
    ["treated", "true"],
    ["treated_complement", "false"],
]
```

`rules.toml`

```
[[rules]]
name = "replace_isToggleEnabled_with_boolean_literal"
query = """((
    (method_invocation 
        name : (_) @name
        arguments: ((argument_list 
                        ([
                          (field_access field: (_)@argument)
                          (_) @argument
                         ])) )
            
    ) @method_invocation
)
(#eq? @name "isToggleEnabled")
(#eq? @argument "@stale_flag_name")
)"""
replace_node = "method_invocation"
replace = "@treated"
groups = [ "replace_expression_with_boolean_literal"]
holes = ["treated", "stale_flag_name"]

#
# For @stale_flag_name = STALE_FLAG and @treated = true
# Before :
#  exp.isToggleDisabled(STALE_FLAG)
# After :
#  false
#
[[rules]]
name = "replace_isToggleDisabled_with_boolean_literal"
query = """((
    (method_invocation 
        name : (_) @name
        arguments: ((argument_list 
                        ([
                          (field_access field: (_)@argument)
                          (_) @argument
                         ])) )
            
    ) @method_invocation
)
(#eq? @name "isToggleDisabled")
(#eq? @argument "@stale_flag_name")
)"""
replace_node = "method_invocation"
replace = "@treated_complement"
groups = [ "replace_expression_with_boolean_literal"]
holes = ["treated_complement", "stale_flag_name"]
```

The other difference I noticed was that I now had to give the qualified name of the enum 

```
    ["stale_flag_name", "MyEnum.SAMPLE_STALE_FLAG"],
```

in my configuration, which wasn't the case when dealing with `PiranhaJava`


Discussion / hints:
ping @ketkarameya 
@krmahadevan Do you have similarly named flag name (`SAMPLE_STALE_FLAG`) under two separate enum declarations (`MyEnum` and `ExpEnum`)? 
> Do you have similarly named flag names under two separate enum declarations ?

Yes It does. That was why I guessed that the fully qualified name was needed. I can live with that, but the enum itself is not getting cleaned up. Any thing that I need to be adding to get it to do that as well, else over a period of time, the enum is going to have a lot of stale values which would need a manual clean-up
I see. 
1. All you are missing is a  rule that cleans up the enum constant like this one :) https://github.com/uber/piranha/blob/9993d613b011e06fe0bd9824d1854333d28df975/generic/piranha/test-resources/java/feature_flag_system_1/treated/configurations/rules.toml#L109
2. But the above rule will delete the enum constant under `ExpEnum` too. So we could do something more - by adding the qualifier name too in the query. 
In this [branch](https://github.com/ketkarameya/piranha/tree/ketkara-krmahadevan-piranha-generic/generic/piranha/demo/java) I have basically replicated your scenario in the `generic/piranha/demo/java/`- And added rules to delete the API and Enum , such that the qualifier is considered. 


I know Piranha doesn't delete the `,`.  I'll add a fix in a couple of days :) 
I usually play around with queries here - https://tree-sitter.github.io/tree-sitter/playground
Adding a rule for deleting empty Enum declarations could be done easily by adding a cleanup rule for Java. I will do that. 
> I usually play around with queries here - https://tree-sitter.github.io/tree-sitter/playground

@ketkarameya 

This is nice. Any pointers with how I can try out actually writing some queries ? 

For e.g., here's what I did

* Opened up the playground portal
* Copy pasted the same class used in this example.
* Pasted the below query 

```
((
    (method_invocation
        name : (_) @name
        arguments: ((argument_list
                        ([
                          (field_access field: (_)@argument)
                          (_) @argument
                         ])) )

    ) @method_invocation
)
(#eq? @name "isActive")
(#eq? @argument "MyEnum.SAMPLE_STALE_FLAG")
)
```

Nothing happened 😢 
Aaah! What "happens" is syntax highlighting :) Observe the code in the top box gets highlighted appropriately. 

>  
> Now with the below arguments and configuration file, when I run the piranha cli, I notice the output class looks like below
> ```
>   public void sampleMethod1(ExperimentInterface exp) {
>     if (exp.isToggleDisabled(ExpEnum.SAMPLE_STALE_FLAG)) {
>       System.out.println("SAMPLE_STALE_FLAG is disabled");
>     } else {
>       System.out.println("SAMPLE_STALE_FLAG is enabled");
>     }
>   }
>
> }
> ```
> 

> 
> ```
> [[rules]]
> #
> # For @stale_flag_name = STALE_FLAG and @treated = true
> # Before :
> #  exp.isToggleDisabled(STALE_FLAG)
> # After :
> #  false
> #
> [[rules]]
> name = "replace_isToggleDisabled_with_boolean_literal"
> query = """((
>     (method_invocation 
>         name : (_) @name
>         arguments: ((argument_list 
>                         ([
>                           (field_access field: (_)@argument)
>                           (_) @argument
>                          ])) )
>             
>     ) @method_invocation
> )
> (#eq? @name "isToggleDisabled")
> (#eq? @argument "@stale_flag_name")
> )"""
> replace_node = "method_invocation"
> replace = "@treated_complement"
> groups = [ "replace_expression_with_boolean_literal"]
> holes = ["treated_complement", "stale_flag_name"]
> ```

In your example, was the invocation of `isToggleDisabled` deleted? The updated `sampleMethod1` still shows the invocation. 

Fix patch:
diff --git a/generic/piranha/demo/java/TestEnum.java b/generic/piranha/demo/java/TestEnum.java
new file mode 100644
index 000000000..f1b65855b
--- /dev/null
+++ b/generic/piranha/demo/java/TestEnum.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2022 Uber Technologies, Inc.
+ *
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.uber.piranha;
+
+enum TestEnum {
+    SAMPLE_STALE_FLAG
+}
\ No newline at end of file
diff --git a/generic/piranha/demo/java/configurations/rules.toml b/generic/piranha/demo/java/configurations/rules.toml
index 54a946b21..8d7857e1a 100755
--- a/generic/piranha/demo/java/configurations/rules.toml
+++ b/generic/piranha/demo/java/configurations/rules.toml
@@ -65,4 +65,28 @@ query = """((
 replace_node = "method_invocation"
 replace = "@treated_complement"
 groups = [ "replace_expression_with_boolean_literal"]
-holes = ["treated_complement", "stale_flag_name"]
\ No newline at end of file
+holes = ["treated_complement", "stale_flag_name"]
+
+#
+# For @stale_flag_name = STALE_FLAG 
+# Before :
+#  enum Flags {
+#   ABC, STALE_FLAG, OTHER_FLAG    
+#  }
+# After :
+#  enum Flags {
+#   ABC, OTHER_FLAG    
+#  }
+#
+[[rules]]
+name = "delete_enum_constant"
+query = """
+    (
+    ((enum_constant name : (_) @n) @ec)       
+    (#eq? @n  "@stale_flag_name")
+    )
+    """
+replace_node = "ec"
+replace = ""
+holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
\ No newline at end of file
diff --git a/generic/piranha/demo/kt/TestEnum.kt b/generic/piranha/demo/kt/TestEnum.kt
new file mode 100644
index 000000000..92145b57c
--- /dev/null
+++ b/generic/piranha/demo/kt/TestEnum.kt
@@ -0,0 +1,19 @@
+/*
+Copyright (c) 2022 Uber Technologies, Inc.
+
+ <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+ <p>http://www.apache.org/licenses/LICENSE-2.0
+
+ <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ express or implied. See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package com.uber.input
+
+enum class TestEnum {
+    @Autorollout
+    SAMPLE_STALE_FLAG,
+}
\ No newline at end of file
diff --git a/generic/piranha/demo/kt/configurations/rules.toml b/generic/piranha/demo/kt/configurations/rules.toml
index aa768348e..5817a6dd2 100755
--- a/generic/piranha/demo/kt/configurations/rules.toml
+++ b/generic/piranha/demo/kt/configurations/rules.toml
@@ -35,6 +35,8 @@ replace = "@treated"
 groups = [ "replace_expression_with_boolean_literal"]
 holes = ["treated", "stale_flag_name"]
 
+
+
 [[rules]]
 name = "replace_isToggleDisabled_with_boolean_literal"
 query = """(
@@ -50,4 +52,31 @@ query = """(
 replace_node = "call_expression"
 replace = "@treated_complement"
 groups = [ "replace_expression_with_boolean_literal"]
-holes = ["treated_complement", "stale_flag_name"]
\ No newline at end of file
+holes = ["treated_complement", "stale_flag_name"]
+
+
+
+#
+# For @stale_flag_name = STALE_FLAG 
+# Before :
+#  enum Flags {
+#   ABC, STALE_FLAG, OTHER_FLAG    
+#  }
+# After :
+#  enum Flags {
+#   ABC, OTHER_FLAG    
+#  }
+#
+[[rules]]
+name = "delete_enum_constant"
+query = """
+   (
+(class_declaration (_) @name 
+	(enum_class_body (enum_entry (simple_identifier) @enum_name) @enum_entry))
+  (#eq? @enum_name "@stale_flag_name")
+)
+    """
+replace_node = "enum_entry"
+replace = ""
+holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
diff --git a/generic/piranha/src/cleanup_rules/java/edges.toml b/generic/piranha/src/cleanup_rules/java/edges.toml
index 5377efc87..e20e7d467 100755
--- a/generic/piranha/src/cleanup_rules/java/edges.toml
+++ b/generic/piranha/src/cleanup_rules/java/edges.toml
@@ -27,6 +27,18 @@ scope = "Parent"
 from = "boolean_expression_simplify"
 to = ["boolean_literal_cleanup"]
 
+[[edges]]
+scope = "Parent"
+from = "delete_enum_entry"
+to = ["delete_empty_enum_declaration"]
+
+
+[[edges]]
+scope = "Parent"
+from = "delete_empty_enum_declaration"
+to = ["delete_file_with_no_type_declarations"]
+
+
 [[edges]]
 scope = "Parent"
 from = "statement_cleanup"
diff --git a/generic/piranha/src/cleanup_rules/java/rules.toml b/generic/piranha/src/cleanup_rules/java/rules.toml
index a5f08d4d5..bc1c0c391 100755
--- a/generic/piranha/src/cleanup_rules/java/rules.toml
+++ b/generic/piranha/src/cleanup_rules/java/rules.toml
@@ -331,6 +331,28 @@ query = """
 replace = "@alternative"
 replace_node = "ternary_expression"
 
+
+[[rules]]
+name = "delete_empty_enum_declaration"
+query = """
+(enum_declaration) @enum_declaration"""
+replace = ""
+replace_node = "enum_declaration"
+[[rules.constraints]]
+matcher = "(enum_declaration) @ed"
+queries = ["(enum_constant) @ec"]
+
+
+[[rules]]
+name = "delete_file_with_no_type_declarations"
+query = """
+(program) @program"""
+replace = ""
+replace_node = "program"
+[[rules.constraints]]
+matcher = "(program) @prgrm"
+queries = ["(enum_declaration) @ec", "(class_declaration) @cd", "(interface_declaration) @id"]
+
 # This rule is part (and entry point) for the inline local variable cleanup. Example: 
 # 
 # Before :
diff --git a/generic/piranha/src/cleanup_rules/kt/edges.toml b/generic/piranha/src/cleanup_rules/kt/edges.toml
index fe1217bac..563256464 100755
--- a/generic/piranha/src/cleanup_rules/kt/edges.toml
+++ b/generic/piranha/src/cleanup_rules/kt/edges.toml
@@ -31,6 +31,16 @@ to = [
   "delete_parent_assignment",
 ]
 
+[[edges]]
+scope = "Parent"
+from = "delete_enum_entry"
+to = ["delete_empty_enum_declaration"]
+
+[[edges]]
+scope = "Parent"
+from = "delete_empty_enum_declaration"
+to = ["delete_file_with_no_declarations"]
+
 [[edges]]
 scope = "Parent"
 from = "boolean_expression_simplify"
diff --git a/generic/piranha/src/cleanup_rules/kt/rules.toml b/generic/piranha/src/cleanup_rules/kt/rules.toml
index 16ec7442e..8fb9eae9e 100755
--- a/generic/piranha/src/cleanup_rules/kt/rules.toml
+++ b/generic/piranha/src/cleanup_rules/kt/rules.toml
@@ -369,6 +369,30 @@ query = """
 replace = ""
 replace_node = "post"
 
+[[rules]]
+name = "delete_empty_enum_declaration"
+query = """
+(class_declaration (_) @name 
+	(enum_class_body)) @enum_declaration"""
+replace = ""
+replace_node = "enum_declaration"
+[[rules.constraints]]
+matcher = """(class_declaration (_) @nm
+	(enum_class_body)) @ed"""
+queries = ["(enum_entry) @ee"]
+
+# Delete all file contents if 
+[[rules]]
+name = "delete_file_with_no_declarations"
+query = """
+(source_file) @source_file"""
+replace = ""
+replace_node = "program"
+[[rules.constraints]]
+matcher = "(source_file) @sf"
+queries = ["(class_declaration) @cd", "(object_declaration) @od", "(function_declaration) @fd", "(property_declaration) @pd"]
+
+
 
 # This rule is part (and entry point) for the inline local variable cleanup. Example: 
 # 
diff --git a/generic/piranha/src/models/constraint.rs b/generic/piranha/src/models/constraint.rs
index dba330984..56b5e0865 100644
--- a/generic/piranha/src/models/constraint.rs
+++ b/generic/piranha/src/models/constraint.rs
@@ -35,7 +35,13 @@ impl Constraint {
     &self, node: Node, source_code_unit: SourceCodeUnit, rule_store: &mut RuleStore,
     substitutions: &HashMap<String, String>,
   ) -> bool {
+
     let mut current_node = node;
+    // This ensures that the below while loop considers the current node too when checking for constraints.
+    // It does not make sense to check for constraint if current node is a "leaf" node.
+    if node.child_count() > 0 {
+       current_node = node.child(0).unwrap();
+    }
     // Get the scope_node of the constraint (`scope.matcher`)
     let mut matched_matcher = false;
     while let Some(parent) = current_node.parent() {
diff --git a/generic/piranha/src/models/rule_graph.rs b/generic/piranha/src/models/rule_graph.rs
index d42ae70c3..3bae33f14 100644
--- a/generic/piranha/src/models/rule_graph.rs
+++ b/generic/piranha/src/models/rule_graph.rs
@@ -29,7 +29,12 @@ impl RuleGraph {
       rules_by_name
         .get(val)
         .map(|v| vec![v.name()])
-        .unwrap_or_else(|| rules_by_group[val].clone())
+        .unwrap_or_else(|| {
+          if rules_by_group.contains_key(val){
+            return rules_by_group[val].clone();
+          }
+          return vec![];
+      })
     };
 
     let mut graph = HashMap::new();
diff --git a/generic/piranha/src/models/source_code_unit.rs b/generic/piranha/src/models/source_code_unit.rs
index 958813169..dd1169018 100644
--- a/generic/piranha/src/models/source_code_unit.rs
+++ b/generic/piranha/src/models/source_code_unit.rs
@@ -44,7 +44,12 @@ impl SourceCodeUnit {
 
   /// Writes the current contents of `code` to the file system.
   pub fn persist(&self) {
-    fs::write(&self.path, self.code.as_str()).expect("Unable to Write file");
+    if self.code.as_str().is_empty(){
+      _ = fs::remove_file(&self.path).expect("Unable to Delete file");
+    }else{
+      fs::write(&self.path, self.code.as_str()).expect("Unable to Write file");
+    }
+    
   }
 
  
diff --git a/generic/piranha/src/piranha.rs b/generic/piranha/src/piranha.rs
index 3d88889a7..f51df1055 100644
--- a/generic/piranha/src/piranha.rs
+++ b/generic/piranha/src/piranha.rs
@@ -268,7 +268,7 @@ impl FlagCleaner {
       .filter(|x| pattern.is_match(x.1.as_str()))
       .collect();
     #[rustfmt::skip]
-    info!("{}", format!("Will parse and analyze {} files.", files.len()).green());
+    println!("{}", format!("Will parse and analyze {} files.", files.len()).green());
     files
   }
 


Test patch:
diff --git a/generic/piranha/src/tests/mod.rs b/generic/piranha/src/tests/mod.rs
index dda0111cb..dfc875163 100644
--- a/generic/piranha/src/tests/mod.rs
+++ b/generic/piranha/src/tests/mod.rs
@@ -61,6 +61,7 @@ fn check_result(updated_files: Vec<SourceCodeUnit>, path_to_expected: PathBuf) {
 
     if !eq_without_whitespace(&source_code_unit.code(), &expected_content) {
       all_files_match = false;
+      println!("{}", &source_code_unit.code());
     }
   }
   assert!(all_files_match);
diff --git a/generic/piranha/src/tests/test_piranha_java.rs b/generic/piranha/src/tests/test_piranha_java.rs
index 84d56bc12..f90e393f9 100644
--- a/generic/piranha/src/tests/test_piranha_java.rs
+++ b/generic/piranha/src/tests/test_piranha_java.rs
@@ -18,7 +18,7 @@ static LANGUAGE: &str = "java";
 #[test]
 fn test_java_scenarios_treated_ff1() {
   initialize();
-  run_test(LANGUAGE, "feature_flag_system_1", "treated", 1);
+  run_test(LANGUAGE, "feature_flag_system_1", "treated", 2);
 }
 
 #[test]
@@ -30,7 +30,7 @@ fn test_java_scenarios_treated_ff2() {
 #[test]
 fn test_java_scenarios_control_ff1() {
   initialize();
-  run_test(LANGUAGE, "feature_flag_system_1", "control", 1);
+  run_test(LANGUAGE, "feature_flag_system_1", "control", 2);
 }
 
 #[test]
diff --git a/generic/piranha/src/tests/test_piranha_kt.rs b/generic/piranha/src/tests/test_piranha_kt.rs
index fd7d59158..05a28ad6d 100644
--- a/generic/piranha/src/tests/test_piranha_kt.rs
+++ b/generic/piranha/src/tests/test_piranha_kt.rs
@@ -18,7 +18,7 @@ static LANGUAGE: &str = "kt";
 #[test]
 fn test_kotlin_scenarios_treated_ff1() {
   initialize();
-  run_test(LANGUAGE, "feature_flag_system_1", "treated", 1);
+  run_test(LANGUAGE, "feature_flag_system_1", "treated", 2);
   
 }
 
@@ -31,7 +31,7 @@ fn test_kotlin_scenarios_treated_ff2() {
 #[test]
 fn test_kotlin_scenarios_control_ff1() {
   initialize();
-  run_test(LANGUAGE, "feature_flag_system_1", "control", 1);
+  run_test(LANGUAGE, "feature_flag_system_1", "control", 2);
 }
 
 #[test]
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/control/configurations/rules.toml b/generic/piranha/test-resources/java/feature_flag_system_1/control/configurations/rules.toml
index 6f9b225ba..3583a6cd2 100755
--- a/generic/piranha/test-resources/java/feature_flag_system_1/control/configurations/rules.toml
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/control/configurations/rules.toml
@@ -117,6 +117,7 @@ query = """
 replace_node = "ec"
 replace = ""
 holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
 
 #
 # For @stale_flag_name = STALE_FLAG and @treated = true
@@ -141,7 +142,6 @@ query = """
 """
 replace_node = "expression_statement"
 replace = ""
-
 holes = ["stale_flag_name"]
 
 #
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/TestEnum.java b/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/TestEnum.java
new file mode 100644
index 000000000..e69de29bb
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/XPFlagCleanerPositiveCases.java b/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/XPFlagCleanerPositiveCases.java
index 9164e496b..8d8cbc19b 100644
--- a/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/XPFlagCleanerPositiveCases.java
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/control/expected/XPFlagCleanerPositiveCases.java
@@ -32,6 +32,9 @@ enum AnotherTestExperimentName {
     SOME_OTHER_FLAG
   }
 
+  enum TestEmptyEnum {
+  }
+
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Autorollout {
     boolean staged() default false;
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/control/input/TestEnum.java b/generic/piranha/test-resources/java/feature_flag_system_1/control/input/TestEnum.java
new file mode 100644
index 000000000..54aedaec9
--- /dev/null
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/control/input/TestEnum.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2022 Uber Technologies, Inc.
+ *
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.uber.piranha;
+
+enum TestEnum {
+    STALE_FLAG
+}
\ No newline at end of file
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/control/input/XPFlagCleanerPositiveCases.java b/generic/piranha/test-resources/java/feature_flag_system_1/control/input/XPFlagCleanerPositiveCases.java
index 674b7dd23..5a8dd308f 100644
--- a/generic/piranha/test-resources/java/feature_flag_system_1/control/input/XPFlagCleanerPositiveCases.java
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/control/input/XPFlagCleanerPositiveCases.java
@@ -33,6 +33,13 @@ enum AnotherTestExperimentName {
     SOME_OTHER_FLAG
   }
 
+  enum TestEmptyEnum {
+  }
+
+  enum TestExperimentName {
+    STALE_FLAG,
+  }
+
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Autorollout {
     boolean staged() default false;
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/treated/configurations/rules.toml b/generic/piranha/test-resources/java/feature_flag_system_1/treated/configurations/rules.toml
index 6f9b225ba..a652978d1 100755
--- a/generic/piranha/test-resources/java/feature_flag_system_1/treated/configurations/rules.toml
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/treated/configurations/rules.toml
@@ -117,6 +117,7 @@ query = """
 replace_node = "ec"
 replace = ""
 holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
 
 #
 # For @stale_flag_name = STALE_FLAG and @treated = true
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/TestEnum.java b/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/TestEnum.java
new file mode 100644
index 000000000..e69de29bb
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/XPFlagCleanerPositiveCases.java b/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/XPFlagCleanerPositiveCases.java
index 0716b04cc..88b7a969b 100644
--- a/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/XPFlagCleanerPositiveCases.java
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/treated/expected/XPFlagCleanerPositiveCases.java
@@ -30,6 +30,9 @@ enum AnotherTestExperimentName {
     SOME_OTHER_FLAG
   }
 
+  enum TestEmptyEnum {
+  }
+
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Autorollout {
     boolean staged() default false;
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/TestEnum.java b/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/TestEnum.java
new file mode 100644
index 000000000..54aedaec9
--- /dev/null
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/TestEnum.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2022 Uber Technologies, Inc.
+ *
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.uber.piranha;
+
+enum TestEnum {
+    STALE_FLAG
+}
\ No newline at end of file
diff --git a/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/XPFlagCleanerPositiveCases.java b/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/XPFlagCleanerPositiveCases.java
index 674b7dd23..5a8dd308f 100644
--- a/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/XPFlagCleanerPositiveCases.java
+++ b/generic/piranha/test-resources/java/feature_flag_system_1/treated/input/XPFlagCleanerPositiveCases.java
@@ -33,6 +33,13 @@ enum AnotherTestExperimentName {
     SOME_OTHER_FLAG
   }
 
+  enum TestEmptyEnum {
+  }
+
+  enum TestExperimentName {
+    STALE_FLAG,
+  }
+
   @Retention(RetentionPolicy.RUNTIME)
   public @interface Autorollout {
     boolean staged() default false;
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/control/configurations/rules.toml b/generic/piranha/test-resources/kt/feature_flag_system_1/control/configurations/rules.toml
index aa2734c60..b1d1d4e96 100755
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/control/configurations/rules.toml
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/control/configurations/rules.toml
@@ -106,6 +106,7 @@ query = """
 replace_node = "enum_entry"
 replace = ""
 holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
 
 #
 # For @stale_flag_name = STALE_FLAG and @treated = true
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/TestEnum.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/TestEnum.kt
new file mode 100644
index 000000000..e69de29bb
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/XPFlagCleanerCases.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/XPFlagCleanerCases.kt
index c983c43cf..90ad7c53b 100644
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/XPFlagCleanerCases.kt
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/control/expected/XPFlagCleanerCases.kt
@@ -25,6 +25,9 @@ internal class XPFlagCleanerPositiveCases {
         SOME_OTHER_FLAG
     }
 
+    enum class TestEmptyEnum {
+    }
+
     @Retention(RetentionPolicy.RUNTIME)
     annotation class Autorollout(val staged: Boolean = false)
 
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/TestEnum.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/TestEnum.kt
new file mode 100644
index 000000000..58680f420
--- /dev/null
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/TestEnum.kt
@@ -0,0 +1,19 @@
+/*
+Copyright (c) 2022 Uber Technologies, Inc.
+
+ <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+ <p>http://www.apache.org/licenses/LICENSE-2.0
+
+ <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ express or implied. See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package com.uber.input
+
+enum class TestEnum {
+    @Autorollout
+    STALE_FLAG,
+}
\ No newline at end of file
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/XPFlagCleanerCases.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/XPFlagCleanerCases.kt
index 6d261e578..80cd4e4ec 100644
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/XPFlagCleanerCases.kt
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/control/input/XPFlagCleanerCases.kt
@@ -28,6 +28,15 @@ internal class XPFlagCleanerPositiveCases {
         SOME_OTHER_FLAG
     }
 
+    enum class TestEmptyEnum {
+    }
+
+    enum class YetAnotherTestExperimentName {
+        @Autorollout
+        STALE_FLAG,
+    }
+    
+
     @Retention(RetentionPolicy.RUNTIME)
     annotation class Autorollout(val staged: Boolean = false)
 
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/configurations/rules.toml b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/configurations/rules.toml
index aa2734c60..b1d1d4e96 100755
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/configurations/rules.toml
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/configurations/rules.toml
@@ -106,6 +106,7 @@ query = """
 replace_node = "enum_entry"
 replace = ""
 holes = ["stale_flag_name"]
+groups = [ "delete_enum_entry"]
 
 #
 # For @stale_flag_name = STALE_FLAG and @treated = true
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/TestEnum.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/TestEnum.kt
new file mode 100644
index 000000000..e69de29bb
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/XPFlagCleanerCases.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/XPFlagCleanerCases.kt
index 92f6c8b7c..8c57978c2 100644
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/XPFlagCleanerCases.kt
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/expected/XPFlagCleanerCases.kt
@@ -24,6 +24,9 @@ internal class XPFlagCleanerPositiveCases {
         SOME_OTHER_FLAG
     }
 
+    enum class TestEmptyEnum {
+    }
+
     @Retention(RetentionPolicy.RUNTIME)
     annotation class Autorollout(val staged: Boolean = false)
 
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/TestEnum.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/TestEnum.kt
new file mode 100644
index 000000000..58680f420
--- /dev/null
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/TestEnum.kt
@@ -0,0 +1,19 @@
+/*
+Copyright (c) 2022 Uber Technologies, Inc.
+
+ <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of the License at
+ <p>http://www.apache.org/licenses/LICENSE-2.0
+
+ <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ express or implied. See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package com.uber.input
+
+enum class TestEnum {
+    @Autorollout
+    STALE_FLAG,
+}
\ No newline at end of file
diff --git a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/XPFlagCleanerCases.kt b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/XPFlagCleanerCases.kt
index 8cdcb9d10..cf8429f4b 100644
--- a/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/XPFlagCleanerCases.kt
+++ b/generic/piranha/test-resources/kt/feature_flag_system_1/treated/input/XPFlagCleanerCases.kt
@@ -28,6 +28,14 @@ internal class XPFlagCleanerPositiveCases {
         SOME_OTHER_FLAG
     }
 
+    enum class YetAnotherTestExperimentName {
+        @Autorollout
+        STALE_FLAG,
+    }
+
+    enum class TestEmptyEnum {
+    }
+
     @Retention(RetentionPolicy.RUNTIME)
     annotation class Autorollout(val staged: Boolean = false)