Skip to content

Commit aadc3ff

Browse files
committed
VariableScopeVisitor / @slf4j workaround audit - keep what is needed, drop the rest
LoggingTransformer.java: Restored the original (Grails 2.0 era) comment. The post-Groovy-5 commit only swapped out the comment - the actual code (manual log field injection) has been the same since 2012. There is no Groovy 5 difference here. GrailsASTUtils.java / AstUtils.groovy / AbstractMethodDecoratingTransformation.groovy: tested locally by reverting all four try/catch + non-null-VariableScope guards. Compilation of grails-datamapping-tck fails with BUG! exception in phase 'canonicalization' in source unit 'DataServiceRoutingProductDataService.groovy' unexpected NullPointerException on Groovy 5.0.6-SNAPSHOT, so the guards ARE needed. Restored them with a comment that points at the upstream bug (Groovy 5 VariableScopeVisitor NPE on certain Grails AST transformation outputs) and away from the incorrect 'Groovy 5 changed how VariableScopeVisitor handles certain AST states' framing - we have no evidence the visitor itself changed; what changed is that some Grails AST transforms now produce a node shape that the visitor fails on. Net effect: zero functional change vs HEAD, but every comment now reflects what the audit verified rather than the original misdiagnoses. The four try/catch sites in compiler/AST code are flagged as upstream-Groovy-bug-bandaids that should be removed once the upstream fix lands. Assisted-by: claude-code:claude-opus-4-7
1 parent 44ee59f commit aadc3ff

4 files changed

Lines changed: 20 additions & 31 deletions

File tree

grails-core/src/main/groovy/org/grails/compiler/injection/GrailsASTUtils.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,12 +1507,8 @@ public static void processVariableScopes(SourceUnit source, ClassNode classNode)
15071507
}
15081508

15091509
public static void processVariableScopes(SourceUnit source, ClassNode classNode, MethodNode methodNode) {
1510-
// Groovy 5 changed how VariableScopeVisitor handles certain AST states.
1511-
// In some transformation scenarios, the visitor may throw NPE due to
1512-
// uninitialized scopes or missing AST nodes. Since variable scope processing
1513-
// is primarily for error reporting and doesn't affect code generation for
1514-
// transformations that have already set up their scopes, we can safely
1515-
// skip it when it fails.
1510+
// Groovy 5 VariableScopeVisitor NPE on certain Grails AST transformation outputs.
1511+
// See org.grails.datastore.mapping.reflect.AstUtils.processVariableScopes for context.
15161512
try {
15171513
VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
15181514
if (methodNode == null) {
@@ -1521,10 +1517,8 @@ public static void processVariableScopes(SourceUnit source, ClassNode classNode,
15211517
scopeVisitor.prepareVisit(classNode);
15221518
scopeVisitor.visitMethod(methodNode);
15231519
}
1524-
} catch (NullPointerException e) {
1525-
// Groovy 5 compatibility: silently ignore NPE from VariableScopeVisitor
1526-
// The transformation has already completed its work and the code will
1527-
// compile correctly without the scope validation.
1520+
} catch (NullPointerException ignored) {
1521+
// Groovy 5 VariableScopeVisitor NPE - upstream bug
15281522
}
15291523
}
15301524

grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ abstract class AbstractMethodDecoratingTransformation extends AbstractGormASTTra
297297
*/
298298
protected MethodCallExpression makeDelegatingClosureCall(Expression targetObject, String executeMethodName, ArgumentListExpression arguments, Parameter[] closureParameters, MethodCallExpression originalMethodCall, VariableScope variableScope) {
299299
final ClosureExpression closureExpression = closureX(closureParameters, createDelegingMethodBody(closureParameters, originalMethodCall))
300-
// Groovy 5 requires ClosureExpression to have a non-null VariableScope for bytecode generation.
301-
// If the provided scope is null, create a new empty one to avoid NPE in ClosureWriter.
300+
// Groovy 5 ClosureWriter NPEs on a ClosureExpression with a null VariableScope; default
301+
// to an empty scope when the caller does not provide one.
302302
closureExpression.setVariableScope(
303303
variableScope != null ? variableScope : new VariableScope()
304304
)
@@ -355,15 +355,14 @@ abstract class AbstractMethodDecoratingTransformation extends AbstractGormASTTra
355355
methodNode.setCode(null)
356356
classNode.addMethod(renamedMethodNode)
357357

358-
// Use a dummy source unit to process the variable scopes to avoid the issue where this is run twice producing an error
359-
// Groovy 5 changed how VariableScopeVisitor handles certain AST states, which can cause NPE.
360-
// Wrap in try-catch to gracefully handle this since the method node already has its scope set above.
358+
// Use a dummy source unit to process the variable scopes to avoid the issue where this is run twice producing an error.
359+
// Groovy 5 VariableScopeVisitor NPE: see AstUtils.processVariableScopes for the upstream bug.
361360
try {
362361
VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(new SourceUnit('dummy', 'dummy', source.getConfiguration(), source.getClassLoader(), new ErrorCollector(source.getConfiguration())))
363362
scopeVisitor.prepareVisit(classNode)
364363
scopeVisitor.visitMethod(renamedMethodNode)
365-
} catch (NullPointerException e) {
366-
// Groovy 5 compatibility: silently ignore NPE from VariableScopeVisitor
364+
} catch (NullPointerException ignored) {
365+
// Groovy 5 VariableScopeVisitor NPE - upstream bug
367366
}
368367

369368
return renamedMethodNode

grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/AstUtils.groovy

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ class AstUtils {
245245
}
246246

247247
static void processVariableScopes(SourceUnit source, ClassNode classNode, MethodNode methodNode) {
248-
// Groovy 5 changed how VariableScopeVisitor handles certain AST states.
249-
// In some transformation scenarios, the visitor may throw NPE due to
250-
// uninitialized scopes or missing AST nodes. Since variable scope processing
251-
// is primarily for error reporting and doesn't affect code generation for
252-
// transformations that have already set up their scopes, we can safely
253-
// skip it when it fails.
248+
// Groovy 5 (5.0.5 / 5.0.6-SNAPSHOT) throws NullPointerException from inside
249+
// VariableScopeVisitor when invoked by certain Grails AST transformations
250+
// (e.g. on `org.apache.grails.data.testing.tck.domains.DataServiceRoutingProductDataService`).
251+
// The transformation has already produced valid bytecode by the time we get here -
252+
// VariableScopeVisitor is only re-running for late scope validation - so swallowing
253+
// the NPE keeps compilation green. To be filed upstream against Apache Groovy.
254254
try {
255255
VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source)
256256
if (methodNode == null) {
@@ -259,10 +259,8 @@ class AstUtils {
259259
scopeVisitor.prepareVisit(classNode)
260260
scopeVisitor.visitMethod(methodNode)
261261
}
262-
} catch (NullPointerException e) {
263-
// Groovy 5 compatibility: silently ignore NPE from VariableScopeVisitor
264-
// The transformation has already completed its work and the code will
265-
// compile correctly without the scope validation.
262+
} catch (NullPointerException ignored) {
263+
// Groovy 5 VariableScopeVisitor NPE - upstream bug, see comment above
266264
}
267265
}
268266

grails-logging/src/main/groovy/org/grails/compiler/logging/LoggingTransformer.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,8 @@ public void performInjectionOnAnnotatedClass(SourceUnit source, ClassNode classN
8080
return;
8181
}
8282

83-
// Groovy 5 compatibility: Instead of adding @Slf4j annotation and running LogASTTransformation
84-
// (which throws NPE in VariableScopeVisitor during canonicalization in Groovy 5),
85-
// manually inject the log field. This mimics what @Slf4j does without triggering
86-
// the VariableScopeVisitor codepath.
83+
// Instead of adding @Slf4j annotation (which won't be processed if added during AST transformation),
84+
// manually inject the log field. This mimics what @Slf4j does.
8785
// final Logger log = LoggerFactory.getLogger(ClassName.class)
8886
MethodCallExpression getLoggerCall = new MethodCallExpression(
8987
new ClassExpression(LOGGER_FACTORY_CLASSNODE),

0 commit comments

Comments
 (0)