From 2ab75969df9e9b8729564f246092b393474a6eea Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Tue, 27 Aug 2024 10:49:08 +0200 Subject: [PATCH] Flush `RecipeRunStats` on exception to record stats for failing recipes --- .../java/org/openrewrite/RecipeScheduler.java | 90 ++++++++++--------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/RecipeScheduler.java b/rewrite-core/src/main/java/org/openrewrite/RecipeScheduler.java index 73eb8832a07..795dad5b841 100644 --- a/rewrite-core/src/main/java/org/openrewrite/RecipeScheduler.java +++ b/rewrite-core/src/main/java/org/openrewrite/RecipeScheduler.java @@ -60,58 +60,60 @@ private LargeSourceSet runRecipeCycles(Recipe recipe, LargeSourceSet sourceSet, LargeSourceSet after = sourceSet; - for (int i = 1; i <= maxCycles; i++) { - if (ctx.getMessage(PANIC) != null) { - break; - } - - // this root cursor is shared by all `TreeVisitor` instances used created from `getVisitor` and - // single source applicable tests so that data can be shared at the root (especially for caching - // use cases like sharing a `JavaTypeCache` between `JavaTemplate` parsers). - Cursor rootCursor = new Cursor(null, Cursor.ROOT_VALUE); - try { - RecipeRunCycle cycle = new RecipeRunCycle<>(recipe, i, rootCursor, ctxWithWatch, - recipeRunStats, sourceFileResults, errorsTable, LargeSourceSet::edit); - ctxWithWatch.putCycle(cycle); - after.beforeCycle(i == maxCycles); - - // pre-transformation scanning phase where there can only be modifications to capture exceptions - // occurring during the scanning phase - if (hasScanningRecipe(recipe)) { - after = cycle.scanSources(after); + try { + for (int i = 1; i <= maxCycles; i++) { + if (ctx.getMessage(PANIC) != null) { + break; } - // transformation phases - after = cycle.generateSources(after); - after = cycle.editSources(after); + // this root cursor is shared by all `TreeVisitor` instances used created from `getVisitor` and + // single source applicable tests so that data can be shared at the root (especially for caching + // use cases like sharing a `JavaTypeCache` between `JavaTemplate` parsers). + Cursor rootCursor = new Cursor(null, Cursor.ROOT_VALUE); + try { + RecipeRunCycle cycle = new RecipeRunCycle<>(recipe, i, rootCursor, ctxWithWatch, + recipeRunStats, sourceFileResults, errorsTable, LargeSourceSet::edit); + ctxWithWatch.putCycle(cycle); + after.beforeCycle(i == maxCycles); + + // pre-transformation scanning phase where there can only be modifications to capture exceptions + // occurring during the scanning phase + if (hasScanningRecipe(recipe)) { + after = cycle.scanSources(after); + } + + // transformation phases + after = cycle.generateSources(after); + after = cycle.editSources(after); - boolean anyRecipeCausingAnotherCycle = false; - for (Recipe madeChanges : cycle.getMadeChangesInThisCycle()) { - if (madeChanges.causesAnotherCycle()) { - anyRecipeCausingAnotherCycle = true; + boolean anyRecipeCausingAnotherCycle = false; + for (Recipe madeChanges : cycle.getMadeChangesInThisCycle()) { + if (madeChanges.causesAnotherCycle()) { + anyRecipeCausingAnotherCycle = true; + } } - } - if (i >= minCycles && - (cycle.getMadeChangesInThisCycle().isEmpty() || !anyRecipeCausingAnotherCycle)) { - after.afterCycle(true); - break; - } + if (i >= minCycles && + (cycle.getMadeChangesInThisCycle().isEmpty() || !anyRecipeCausingAnotherCycle)) { + after.afterCycle(true); + break; + } - after.afterCycle(i == maxCycles); - ctxWithWatch.resetHasNewMessages(); - } finally { - // Clear any messages that were added to the root cursor during the cycle. This is important - // to avoid leaking memory in the case when a recipe defines a static TreeVisitor. That - // TreeVisitor will still contain a reference to this rootCursor and any messages in it - // after recipe execution completes. The pattern of holding a static TreeVisitor isn't - // recommended, but isn't possible for us to guard against at an API level, and so we are - // defensive about memory consumption here. - rootCursor.clearMessages(); + after.afterCycle(i == maxCycles); + ctxWithWatch.resetHasNewMessages(); + } finally { + // Clear any messages that were added to the root cursor during the cycle. This is important + // to avoid leaking memory in the case when a recipe defines a static TreeVisitor. That + // TreeVisitor will still contain a reference to this rootCursor and any messages in it + // after recipe execution completes. The pattern of holding a static TreeVisitor isn't + // recommended, but isn't possible for us to guard against at an API level, and so we are + // defensive about memory consumption here. + rootCursor.clearMessages(); + } } + } finally { + recipeRunStats.flush(ctx); } - - recipeRunStats.flush(ctx); return after; }