[
https://issues.apache.org/jira/browse/GROOVY-12689?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King resolved GROOVY-12689.
--------------------------------
Fix Version/s: 7.0.0-alpha-1
6.0.1
Assignee: Paul King
Resolution: Fixed
> FormatStringChecker fails inside the compiler when an argument's type is
> declared in the same source
> ----------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12689
> URL: https://issues.apache.org/jira/browse/GROOVY-12689
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Assignee: Paul King
> Priority: Major
> Fix For: 7.0.0-alpha-1, 6.0.1
>
>
> With {{groovy.typecheckers.FormatStringChecker}} enabled, a format call with
> a non-constant argument whose static type is declared in the same compilation
> unit aborts the compilation with a {{GroovyBugError}}. A plain class is
> enough, and so is an enum, a record, an anonymous class, an array of such a
> type, or {{this}} inside the checked class. The conversion does not matter:
> {{%s}} fails too.
> {{Show.groovy}} and {{ShowColor.groovy}}:
> {code:groovy}
> import groovy.transform.TypeChecked
> class Foo {}
> @TypeChecked(extensions = 'groovy.typecheckers.FormatStringChecker')
> String show() {
> String.format('%s', new Foo())
> }
> {code}
> {code:groovy}
> import groovy.transform.TypeChecked
> enum Color { RED, GREEN }
> @TypeChecked(extensions = 'groovy.typecheckers.FormatStringChecker')
> String show() {
> String.format('%s', Color.RED)
> }
> {code}
> {code:sh}
> groovyc --check --error-format=short Show.groovy
> groovyc --check --error-format=short ShowColor.groovy
> {code}
> Actual, for {{Show.groovy}}: exit 1 and
> {code:text}
> >>> a serious error occurred: BUG! exception in phase 'instruction selection'
> >>> in source unit 'Show.groovy' ClassNode#getTypeClass for Foo called before
> >>> the type class is set
> >>> stacktrace:
> BUG! exception in phase 'instruction selection' in source unit 'Show.groovy'
> ClassNode#getTypeClass for Foo called before the type class is set
> at org.codehaus.groovy.ast.ClassNode.getTypeClass(ClassNode.java:1987)
> at
> groovy.typecheckers.FormatStringChecker$1$_checkFormatStringTypes_closure5.doCall(FormatStringChecker.groovy:184)
> ...
> at
> groovy.typecheckers.FormatStringChecker$1.checkFormatStringTypes(FormatStringChecker.groovy:163)
> at
> groovy.typecheckers.FormatStringChecker$1.checkFormatterMethod(FormatStringChecker.groovy:156)
> at
> groovy.typecheckers.FormatStringChecker$1.visitMethodCallExpression(FormatStringChecker.groovy:122)
> ...
> at
> org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport.afterVisitMethod(GroovyTypeCheckingExtensionSupport.java:427)
> ...
> {code}
> {{ShowColor.groovy}} fails the same way with {{ClassNode#getTypeClass for
> Color called before the type class is set}}. The smallest form needs no
> second type:
> {code:groovy}
> @groovy.transform.TypeChecked(extensions =
> 'groovy.typecheckers.FormatStringChecker')
> class Main {
> String show() { String.format('%s', this) }
> }
> {code}
> It fails with {{ClassNode#getTypeClass for Main called before the type class
> is set}}. The same happens when the checker is applied through
> {{CompilerConfiguration}}:
> {code:groovy}
> import groovy.transform.TypeChecked
> import org.codehaus.groovy.control.CompilerConfiguration
> import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
> def config = new CompilerConfiguration()
> config.addCompilationCustomizers(new ASTTransformationCustomizer(
> [extensions: 'groovy.typecheckers.FormatStringChecker'], TypeChecked))
> new GroovyShell(config).evaluate('''
> class Foo {}
> String.format('%s', new Foo())
> ''')
> {code}
> {code:text}
> org.codehaus.groovy.GroovyBugError: BUG! exception in phase 'instruction
> selection' in source unit 'Script1.groovy' ClassNode#getTypeClass for Foo
> called before the type class is set
> {code}
> Expected: these sources compile. They do when {{Foo}} is compiled first and
> put on the class path: {{groovyc --check}} of the same checked method then
> exits 0. A real mismatch should still be reported, for example
> {{IllegalFormatConversion: d != Foo}} for {{String.format('%d', new Foo())}}.
> That is what the checker reports for a precompiled {{Foo}} or for
> {{java.util.UUID}}.
> When the checked code also declares an enum or a record, this error hides the
> one described in GROOVY-12688. The two have separate causes.
> Affected versions: 6.0.0, 6.0.0-beta-1, 5.0.8 and 5.0.0 fail the same way,
> and so does current master (7.0.0-SNAPSHOT at 5e9212ce1e).
> Cause: when a format call's arguments are not all constants,
> {{checkFormatStringTypes}} takes the runtime class of each argument's static
> type ({{getWrapper(getType(arg)).typeClass}}, line 184) and compares it with
> {{Class}} objects. A type compiled in the same compilation unit has no
> runtime class yet, so {{ClassNode#getTypeClass}} throws. The class is taken
> before the conversion is looked at, which is why {{%s}} fails as well.
> Because {{GroovyBugError}} is an {{Error}}, it escapes the extension's error
> handling and ends the compilation.
> A fix that works: compare the argument's {{ClassNode}} with the
> {{ClassHelper}} types, and use
> {{StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf}} for the
> date/time check, so that types from the same source are checked like
> precompiled ones. A pull request with regression tests follows.
> Impact: FormatStringChecker was graduated to stable for 6.0.0 (GROOVY-12039).
> Checked code that passes an instance of one of its own types to a format
> method, including {{this}} or one of its own enum constants, cannot be
> compiled with it. Workaround: compile those types separately, or leave
> FormatStringChecker off for such sources.
> Environment: Groovy 6.0.0 on BellSoft JDK 21.0.12, macOS 26.5 (arm64);
> reproduced on master at 5e9212ce1e with JDK 21.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)