Skip to content

Commit 9eb8e31

Browse files
committed
Improved detection of Extract Method with calls in other mappers
1 parent 7b77896 commit 9eb8e31

File tree

9 files changed

+130
-31
lines changed

9 files changed

+130
-31
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,12 @@ Currently, it supports the detection of the following refactorings:
380380
**File**: [data.json](https://github.com/tsantalis/RefactoringMiner/blob/master/src/test/resources/oracle/data.json)
381381

382382
The original benchmark has been extended by adding newly supported refactoring types by RefactoringMiner.
383-
As of **November 11, 2025** the precision and recall of RefactoringMiner on this benchmark is:
383+
As of **November 14, 2025** the precision and recall of RefactoringMiner on this benchmark is:
384384

385385
| Refactoring Type | TP | FP | FN | Precision | Recall |
386386
|:-----------------------|-----------:|--------:|--------:|--------:|--------:|
387-
|**Total**|12533 | 13 | 219 | 0.999 | 0.983|
388-
|Extract Method|1016 | 1 | 20 | 0.999 | 0.981|
387+
|**Total**|12543 | 13 | 219 | 0.999 | 0.983|
388+
|Extract Method|1019 | 1 | 20 | 0.999 | 0.981|
389389
|Rename Class|56 | 0 | 2 | 1.000 | 0.966|
390390
|Move Attribute|257 | 0 | 8 | 1.000 | 0.970|
391391
|Move And Rename Attribute|11 | 0 | 0 | 1.000 | 1.000|
@@ -414,7 +414,7 @@ As of **November 11, 2025** the precision and recall of RefactoringMiner on this
414414
|Extract Attribute|26 | 0 | 0 | 1.000 | 1.000|
415415
|Inline Variable|147 | 0 | 0 | 1.000 | 1.000|
416416
|Inline Attribute| 9 | 0 | 0 | 1.000 | 1.000|
417-
|Rename Variable|343 | 2 | 11 | 0.994 | 0.969|
417+
|Rename Variable|346 | 2 | 11 | 0.994 | 0.969|
418418
|Rename Parameter|494 | 2 | 24 | 0.996 | 0.954|
419419
|Rename Attribute|146 | 0 | 9 | 1.000 | 0.942|
420420
|Merge Variable| 6 | 0 | 0 | 1.000 | 1.000|
@@ -428,7 +428,7 @@ As of **November 11, 2025** the precision and recall of RefactoringMiner on this
428428
|Localize Parameter|26 | 0 | 0 | 1.000 | 1.000|
429429
|Parameterize Attribute|25 | 0 | 0 | 1.000 | 1.000|
430430
|Change Return Type|433 | 0 | 12 | 1.000 | 0.973|
431-
|Change Variable Type|815 | 1 | 7 | 0.999 | 0.991|
431+
|Change Variable Type|816 | 1 | 7 | 0.999 | 0.991|
432432
|Change Parameter Type|651 | 1 | 10 | 0.998 | 0.985|
433433
|Change Attribute Type|244 | 0 | 8 | 1.000 | 0.968|
434434
|Add Method Annotation|330 | 0 | 0 | 1.000 | 1.000|
@@ -446,7 +446,7 @@ As of **November 11, 2025** the precision and recall of RefactoringMiner on this
446446
|Add Parameter|850 | 1 | 1 | 0.999 | 0.999|
447447
|Remove Parameter|318 | 0 | 0 | 1.000 | 1.000|
448448
|Reorder Parameter| 9 | 0 | 0 | 1.000 | 1.000|
449-
|Add Variable Annotation| 1 | 0 | 0 | 1.000 | 1.000|
449+
|Add Variable Annotation| 2 | 0 | 0 | 1.000 | 1.000|
450450
|Remove Variable Annotation| 4 | 0 | 0 | 1.000 | 1.000|
451451
|Add Thrown Exception Type|40 | 0 | 0 | 1.000 | 1.000|
452452
|Remove Thrown Exception Type|271 | 0 | 0 | 1.000 | 1.000|
@@ -460,7 +460,7 @@ As of **November 11, 2025** the precision and recall of RefactoringMiner on this
460460
|Remove Attribute Modifier|143 | 0 | 0 | 1.000 | 1.000|
461461
|Add Variable Modifier|135 | 0 | 0 | 1.000 | 1.000|
462462
|Add Parameter Modifier|133 | 0 | 0 | 1.000 | 1.000|
463-
|Remove Variable Modifier|65 | 0 | 0 | 1.000 | 1.000|
463+
|Remove Variable Modifier|67 | 0 | 0 | 1.000 | 1.000|
464464
|Remove Parameter Modifier|39 | 0 | 0 | 1.000 | 1.000|
465465
|Change Class Access Modifier|78 | 0 | 0 | 1.000 | 1.000|
466466
|Add Class Modifier|37 | 0 | 0 | 1.000 | 1.000|
@@ -509,12 +509,12 @@ Moreover, the benchmark has been extended with valid instances for the following
509509
* `Move Code`
510510
* `Split Class`
511511

512-
As of **November 13, 2025** the precision and recall of RefactoringMiner on this benchmark is:
512+
As of **November 14, 2025** the precision and recall of RefactoringMiner on this benchmark is:
513513

514514
| Refactoring Type | TP | FP | FN | Precision | Recall |
515515
|:-----------------------|-----------:|--------:|--------:|--------:|--------:|
516-
|**Total**|3434 | 47 | 91 | 0.986 | 0.974|
517-
|Extract Method|371 | 1 | 4 | 0.997 | 0.989|
516+
|**Total**|3436 | 47 | 90 | 0.987 | 0.974|
517+
|Extract Method|373 | 1 | 4 | 0.997 | 0.989|
518518
|Rename Class|233 | 0 | 1 | 1.000 | 0.996|
519519
|Move Attribute|72 | 0 | 7 | 1.000 | 0.911|
520520
|Move And Rename Attribute| 7 | 0 | 1 | 1.000 | 0.875|
@@ -537,7 +537,7 @@ As of **November 13, 2025** the precision and recall of RefactoringMiner on this
537537
|Replace Anonymous With Class| 5 | 0 | 0 | 1.000 | 1.000|
538538
|Extract Variable|290 | 1 | 8 | 0.997 | 0.973|
539539
|Extract Attribute| 3 | 0 | 0 | 1.000 | 1.000|
540-
|Inline Variable|127 | 0 | 12 | 1.000 | 0.914|
540+
|Inline Variable|127 | 0 | 11 | 1.000 | 0.920|
541541
|Rename Variable|305 | 5 | 8 | 0.984 | 0.974|
542542
|Rename Attribute|107 | 4 | 8 | 0.964 | 0.930|
543543
|Replace Variable With Attribute|11 | 0 | 0 | 1.000 | 1.000|

src/main/java/gr/uom/java/xmi/decomposition/ReplacementAlgorithm.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,7 @@ else if(!returnType1.getClassType().equals("void") && returnType2.getClassType()
14871487
s1 = ReplacementUtil.performReplacement(s1, s2, replacement.getBefore(), replacement.getAfter());
14881488
//find method invocation replacements within method invocation replacements, the boolean value indicates if the remaining part of the original replacement is identical or not
14891489
Map<Replacement, Boolean> nestedReplacementMap = replacementsWithinMethodInvocations(replacement.getBefore(), replacement.getAfter(), methodInvocations1, methodInvocations2, methodInvocationMap1, methodInvocationMap2);
1490+
nestedReplacementMap.putAll(replacementsWithinMethodInvocations(replacement.getBefore(), replacement.getAfter(), creations1, creations2, creationMap1, creationMap2));
14901491
if(!nestedReplacementMap.isEmpty()) {
14911492
if(!nestedReplacementMap.values().contains(false)) {
14921493
replacementsToBeRemoved.add(replacement);

src/main/java/gr/uom/java/xmi/diff/UMLClassBaseDiff.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3820,7 +3820,25 @@ private List<UMLOperationBodyMapper> checkForExtractedOperationsWithCallsInOther
38203820
List<ExtractOperationRefactoring> refs = detection.check(addedOperation);
38213821
for(ExtractOperationRefactoring refactoring : refs) {
38223822
UMLOperationBodyMapper operationBodyMapper = refactoring.getBodyMapper();
3823-
if(operationBodyMapper.exactMatches() > 1) {
3823+
boolean objectCreation = false;
3824+
for(AbstractCodeMapping mapping : operationBodyMapper.getMappings()) {
3825+
AbstractCall call1 = mapping.getFragment1().creationCoveringEntireFragment();
3826+
AbstractCall call2 = mapping.getFragment2().creationCoveringEntireFragment();
3827+
if(call1 != null && call2 != null && call1.identicalName(call2)) {
3828+
objectCreation = true;
3829+
break;
3830+
}
3831+
}
3832+
boolean duplicate = false;
3833+
for(Refactoring r : refactorings) {
3834+
if(r instanceof ExtractOperationRefactoring) {
3835+
ExtractOperationRefactoring ex = (ExtractOperationRefactoring)r;
3836+
if(ex.getBodyMapper().getMappings().equals(refactoring.getBodyMapper().getMappings())) {
3837+
duplicate = true;
3838+
}
3839+
}
3840+
}
3841+
if(!duplicate && (operationBodyMapper.exactMatches() > 1 || objectCreation)) {
38243842
refactorings.add(refactoring);
38253843
extractedOperationMappers.add(operationBodyMapper);
38263844
mapper.addChildMapper(operationBodyMapper);

src/test/java/org/refactoringminer/test/TestAllRefactorings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ public void testAllRefactorings() throws Exception {
1717
GitHistoryRefactoringMinerImpl detector = new GitHistoryRefactoringMinerImpl();
1818
TestBuilder test = new TestBuilder(detector, REPOS, Refactorings.All.getValue());
1919
RefactoringPopulator.feedRefactoringsInstances(Refactorings.All.getValue(), Systems.FSE.getValue(), test);
20-
test.assertExpectationsWithGitHubAPI(12533, 13, 219);
20+
test.assertExpectationsWithGitHubAPI(12543, 13, 219);
2121
}
2222
}

src/test/java/org/refactoringminer/test/TestNewDatasetRefactorings.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ public void testAllRefactorings() throws Exception {
5252
.or(Refactorings.SplitClass.getValue());
5353
TestBuilder test = new TestBuilder(detector, REPOS, types);
5454
RefactoringPopulator.feedTSERefactoringInstances(test);
55-
test.assertExpectationsWithGitHubAPI(3434, 47, 91);
55+
test.assertExpectationsWithGitHubAPI(3436, 47, 90);
5656
}
5757
}

src/test/resources/oracle/data.json

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12946,6 +12946,13 @@
1294612946
"validation": "TP",
1294712947
"detectionTools": "RefactoringMiner",
1294812948
"validators": null
12949+
}, {
12950+
"type": "Extract Method",
12951+
"description": "Extract Method public freeze() : void extracted from public edge(a int, b int) : EdgeSkipIterState in class com.graphhopper.storage.LevelGraphImpl",
12952+
"comment": null,
12953+
"validation": "TP",
12954+
"detectionTools": "RefactoringMiner",
12955+
"validators": null
1294912956
}],
1295012957
"refDiffExecutionTime": 3012
1295112958
}, {
@@ -76910,20 +76917,6 @@
7691076917
"validation": "TP",
7691176918
"detectionTools": "RefactoringMiner",
7691276919
"validators": "Nikos"
76913-
}, {
76914-
"type": "Rename Variable",
76915-
"description": "Rename Variable key : Object to entry : Map.Entry<Object,UpdateResponse> in method public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : RestLiResponseEnvelope from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
76916-
"comment": null,
76917-
"validation": "TP",
76918-
"detectionTools": "RefactoringMiner",
76919-
"validators": null
76920-
}, {
76921-
"type": "Change Variable Type",
76922-
"description": "Change Variable Type key : Object to entry : Map.Entry<Object,UpdateResponse> in method public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : RestLiResponseEnvelope from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
76923-
"comment": null,
76924-
"validation": "TP",
76925-
"detectionTools": "RefactoringMiner",
76926-
"validators": null
7692776920
}, {
7692876921
"type": "Inline Variable",
7692976922
"description": "Inline Variable entityResponse : EntityResponse<? extends RecordTemplate> in method public onResponse(requestContext FilterRequestContext, responseContext FilterResponseContext) : void from class com.linkedin.restli.server.validation.RestLiOutputValidationFilter",
@@ -76987,6 +76980,83 @@
7698776980
"validation": "TP",
7698876981
"detectionTools": "RefactoringMiner",
7698976982
"validators": null
76983+
}, {
76984+
"type": "Extract Method",
76985+
"description": "Extract Method private generateResultEntityResponse(routingResult RoutingResult, responses Map<Object,BatchResponseEntry>, mergedResults Map<Object,UpdateStatus>) : void extracted from public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : AugmentedRestLiResponseData in class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
76986+
"comment": null,
76987+
"validation": "TP",
76988+
"detectionTools": "RefactoringMiner",
76989+
"validators": null
76990+
}, {
76991+
"type": "Extract Method",
76992+
"description": "Extract Method private buildEntityResponse(routingResult RoutingResult, mergedResponse Map<Object,BatchResponseEntry>) : Map<Object,EntityResponse<RecordTemplate>> extracted from public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : AugmentedRestLiResponseData in class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
76993+
"comment": null,
76994+
"validation": "TP",
76995+
"detectionTools": "RefactoringMiner",
76996+
"validators": null
76997+
}, {
76998+
"type": "Remove Variable Modifier",
76999+
"description": "Remove Variable Modifier final in variable results : Map<Object,EntityResponse<RecordTemplate>> in method public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : AugmentedRestLiResponseData from class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
77000+
"comment": null,
77001+
"validation": "TP",
77002+
"detectionTools": "RefactoringMiner",
77003+
"validators": null
77004+
}, {
77005+
"type": "Remove Variable Modifier",
77006+
"description": "Remove Variable Modifier final in variable mergedResult : UpdateStatus in method public buildRestLiResponseData(request RestRequest, routingResult RoutingResult, result Object, headers Map<String,String>) : AugmentedRestLiResponseData from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
77007+
"comment": null,
77008+
"validation": "TP",
77009+
"detectionTools": "RefactoringMiner",
77010+
"validators": null
77011+
}, {
77012+
"type": "Rename Variable",
77013+
"description": "Rename Variable mergedResult : UpdateStatus to status : UpdateStatus in method private generateResultEntityResponse(routingResult RoutingResult, responses Map<Object,BatchResponseEntry>, mergedResults Map<Object,UpdateStatus>) : void from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
77014+
"comment": null,
77015+
"validation": "TP",
77016+
"detectionTools": "RefactoringMiner",
77017+
"validators": null
77018+
}, {
77019+
"type": "Rename Variable",
77020+
"description": "Rename Variable results : Map<Object,EntityResponse<RecordTemplate>> to entityBatchResponse : Map<Object,EntityResponse<RecordTemplate>> in method private buildEntityResponse(routingResult RoutingResult, mergedResponse Map<Object,BatchResponseEntry>) : Map<Object,EntityResponse<RecordTemplate>> from class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
77021+
"comment": null,
77022+
"validation": "TP",
77023+
"detectionTools": "RefactoringMiner",
77024+
"validators": null
77025+
}, {
77026+
"type": "Rename Variable",
77027+
"description": "Rename Variable key : Object to entry : Map.Entry<Object,BatchResponseEntry> in method private buildEntityResponse(routingResult RoutingResult, mergedResponse Map<Object,BatchResponseEntry>) : Map<Object,EntityResponse<RecordTemplate>> from class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
77028+
"comment": null,
77029+
"validation": "TP",
77030+
"detectionTools": "RefactoringMiner",
77031+
"validators": null
77032+
}, {
77033+
"type": "Rename Variable",
77034+
"description": "Rename Variable key : Object to entry : Map.Entry<?,BatchResponseEntry> in method private generateResultEntityResponse(routingResult RoutingResult, responses Map<Object,BatchResponseEntry>, mergedResults Map<Object,UpdateStatus>) : void from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
77035+
"comment": null,
77036+
"validation": "TP",
77037+
"detectionTools": "RefactoringMiner",
77038+
"validators": null
77039+
}, {
77040+
"type": "Add Variable Annotation",
77041+
"description": "Add Variable Annotation @SuppressWarnings(\"unchecked\") in variable entityResponse : EntityResponse<RecordTemplate> in method private buildEntityResponse(routingResult RoutingResult, mergedResponse Map<Object,BatchResponseEntry>) : Map<Object,EntityResponse<RecordTemplate>> from class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
77042+
"comment": null,
77043+
"validation": "TP",
77044+
"detectionTools": "RefactoringMiner",
77045+
"validators": null
77046+
}, {
77047+
"type": "Change Variable Type",
77048+
"description": "Change Variable Type key : Object to entry : Map.Entry<?,BatchResponseEntry> in method private generateResultEntityResponse(routingResult RoutingResult, responses Map<Object,BatchResponseEntry>, mergedResults Map<Object,UpdateStatus>) : void from class com.linkedin.restli.internal.server.methods.response.BatchUpdateResponseBuilder",
77049+
"comment": null,
77050+
"validation": "TP",
77051+
"detectionTools": "RefactoringMiner",
77052+
"validators": null
77053+
}, {
77054+
"type": "Change Variable Type",
77055+
"description": "Change Variable Type key : Object to entry : Map.Entry<Object,BatchResponseEntry> in method private buildEntityResponse(routingResult RoutingResult, mergedResponse Map<Object,BatchResponseEntry>) : Map<Object,EntityResponse<RecordTemplate>> from class com.linkedin.restli.internal.server.methods.response.BatchGetResponseBuilder",
77056+
"comment": null,
77057+
"validation": "TP",
77058+
"detectionTools": "RefactoringMiner",
77059+
"validators": null
7699077060
}],
7699177061
"refDiffExecutionTime": 2657
7699277062
}, {

src/test/resources/oracle/expected.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ a1525ac9a0bb8f727167a8be94c81a3415128ef4, 2, 0, 0
216216
d49765899cb9df6781fff9773ffc244b5167351c, 12, 0, 0
217217
be292763b8c4cb09988023d6081b0a2d57b4c778, 10, 0, 1
218218
db024f5ec3e9611ddf8103bdc4c3817c704f7b27, 22, 0, 0
219-
ec5ea36faa3dd74585bb339beabdba6149ed63be, 114, 0, 2
219+
ec5ea36faa3dd74585bb339beabdba6149ed63be, 123, 0, 2
220220
0f8a0af934f09deef1b58e961ffe789c7299bcc1, 25, 0, 0
221221
e84e96ff5c2bdc48cea7f75fd794506159c4e1f7, 22, 0, 0
222222
84b7b3974ae8171a4de2f804eb94fcd1d6cd6647, 1, 0, 0
@@ -333,7 +333,7 @@ b70f7ea0ce27b5defa0a7773d448732364e7aee0, 5, 0, 0
333333
35668435090eb47cf8c5e704243510b6cee35a7b, 42, 0, 0
334334
e37d577b6cfc2d3e11252cef87ab9ebba72e1d52, 3, 0, 0
335335
2b2bb6c734d106cdd1c0f4691607be2fe11d7ebb, 1, 0, 0
336-
7f80425b6a0af9bdfef12c8a873676e39e0a04a6, 409, 2, 7
336+
7f80425b6a0af9bdfef12c8a873676e39e0a04a6, 410, 2, 7
337337
4fcd7d4d366d001cf5f1f7d926c608c902e3f0af, 3, 0, 0
338338
573a1d115b86abbe3fb53ff930464d7d8fd95600, 18, 0, 0
339339
ec52e77ecde749e7c5a483b26cbd8041f2a5a33c, 2, 0, 0

src/test/resources/oracle/tse-dataset/flink.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,7 @@
12251225
{
12261226
"type": "INLINE_VARIABLE",
12271227
"description": "Inline Variable\tsink : FileSystemOutputFormat<Row> in method private createSink(override boolean, partition boolean, dynamicGrouped boolean, staticPartitions LinkedHashMap<String,String>, sinkRef AtomicReference<FileSystemOutputFormat<Row>>) : OneInputStreamOperatorTestHarness<Row,Object> from class org.apache.flink.connector.file.table.FileSystemOutputFormatTest",
1228-
"validation": true
1228+
"validation": false
12291229
}
12301230
],
12311231
"baseline": [
@@ -1258,6 +1258,11 @@
12581258
"type": "INLINE_OPERATION",
12591259
"description": "Inline Method\tprivate writeUnorderedRecords(testHarness OneInputStreamOperatorTestHarness<Row,Object>) : void inlined to private checkWriteAndCommit(override boolean, partitioned boolean, dynamicGrouped boolean, staticPartitions LinkedHashMap<String,String>, inputSupplier Supplier<List<StreamRecord<Row>>>, outputSupplier Supplier<?>) : void in class org.apache.flink.connector.file.table.FileSystemOutputFormatTest",
12601260
"validation": true
1261+
},
1262+
{
1263+
"type": "EXTRACT_OPERATION",
1264+
"description": "Extract Method\tprivate createTestHarness(outputFormat FileSystemOutputFormat<Row>) : OneInputStreamOperatorTestHarness<Row,Object> extracted from private createSink(override boolean, partition boolean, dynamicGrouped boolean, staticPartitions LinkedHashMap<String,String>, sinkRef AtomicReference<FileSystemOutputFormat<Row>>) : OneInputStreamOperatorTestHarness<Row,Object> in class org.apache.flink.connector.file.table.FileSystemOutputFormatTest",
1265+
"validation": true
12611266
}
12621267
]
12631268
},

src/test/resources/oracle/tse-dataset/jetty.project.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,11 @@
10341034
"type": "MOVE_CODE",
10351035
"description": "Move Code\tfrom public Parser(byteBufferPool ByteBufferPool, listener Listener, maxDynamicTableSize int, maxHeaderSize int, rateControl RateControl) to public init(listener Listener) : void in class org.eclipse.jetty.http2.parser.Parser",
10361036
"validation": true
1037+
},
1038+
{
1039+
"type": "EXTRACT_OPERATION",
1040+
"description": "Extract Method\tpublic setTableCapacity(tableCapacity int) : void extracted from public encodeMaxDynamicTableSize(buffer ByteBuffer, maxDynamicTableSize int) : void in class org.eclipse.jetty.http2.hpack.HpackEncoder",
1041+
"validation": true
10371042
}
10381043
]
10391044
},

0 commit comments

Comments
 (0)