Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ import groovy.transform.CompileStatic
'org.grails.compiler.WhereQueryTypeCheckingExtension',
'org.grails.compiler.DynamicFinderTypeCheckingExtension',
'org.grails.compiler.DomainMappingTypeCheckingExtension',
'org.grails.compiler.CriteriaTypeCheckingExtension',
'org.grails.compiler.RelationshipManagementMethodTypeCheckingExtension'])
@interface GrailsCompileStatic {}
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,28 @@ class CriteriaTypeCheckingExtension extends TypeCheckingDSL {
null
}

private static final List<String> CRITERIA_TERMINALS =
['list', 'listDistinct', 'get', 'count', 'scroll']

protected boolean isCriteriaCall(MethodCall call) {
call instanceof MethodCallExpression &&
call.objectExpression instanceof ClassExpression &&
GrailsASTUtils.isDomainClass(call.objectExpression.type, null) &&
(call.method.value == 'withCriteria' || call.method.value == 'createCriteria')
if (!(call instanceof MethodCallExpression)) {
return false
}
// Domain.withCriteria { } / Domain.createCriteria() — the criteria closure is the direct argument.
if (call.objectExpression instanceof ClassExpression &&
GrailsASTUtils.isDomainClass(call.objectExpression.type, null) &&
(call.method.value == 'withCriteria' || call.method.value == 'createCriteria')) {
return true
}
// Domain.createCriteria().list/get/count/... { } — the closure is the argument to the terminal
// call chained on the builder, so its body must be type-checked in a criteria scope too.
if (call.method.value in CRITERIA_TERMINALS &&
call.objectExpression instanceof MethodCallExpression &&
call.objectExpression.method.value == 'createCriteria' &&
call.objectExpression.objectExpression instanceof ClassExpression &&
GrailsASTUtils.isDomainClass(call.objectExpression.objectExpression.type, null)) {
return true
}
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,52 @@ class GrailsCompileStaticCompilationErrorsSpec extends Specification {
then: 'no errors are thrown'
c
}


void 'Test compiling a class which invokes a chained createCriteria() terminal on a domain class'() {
given:
def gcl = new GroovyClassLoader()

when: 'the criteria builder closure is the argument to a terminal chained directly on createCriteria()'
def c = gcl.parseClass('''
package grails.compiler

@GrailsCompileStatic
class SomeClass {

def someMethod() {
Person.createCriteria().list {
cache true
eq 'name', 'Anakin'
}

Person.createCriteria().list(max: 4, offset: 2) {
cache true
eq 'name', 'Anakin'
}

Person.createCriteria().listDistinct {
eq 'name', 'Anakin'
}

Person.createCriteria().get {
eq 'name', 'Anakin'
}

Person.createCriteria().count {
eq 'name', 'Anakin'
}

Person.createCriteria().scroll {
eq 'name', 'Anakin'
}
}
}
'''.stripIndent())

then: 'no errors are thrown'
c
}

void 'Test compiling a domain class with a mapping block'() {
given:
def gcl = new GroovyClassLoader()
Expand Down
Loading