Skip to content

Fix NoClassDefFoundError during analysis of inner classes#1660

Merged
eed3si9n merged 2 commits intosbt:developfrom
BrianHotopp:fix/sbt-117-noclassdeffounderror-inner-classes
Mar 21, 2026
Merged

Fix NoClassDefFoundError during analysis of inner classes#1660
eed3si9n merged 2 commits intosbt:developfrom
BrianHotopp:fix/sbt-117-noclassdeffounderror-inner-classes

Conversation

@BrianHotopp
Copy link
Copy Markdown
Contributor

Summary

Fixes sbt/sbt#117 — the oldest open bug in sbt, filed July 22, 2011.

  • Problem: ClassToAPI used Java reflection (c.getClasses, c.getDeclaredClasses) to discover inner classes during post-compile analysis. When an inner class references types not on the classpath (e.g., optional dependencies like JBoss VFS in Spring), these reflection calls throw NoClassDefFoundError, crashing compilation.
  • Fix: Parse the classfile's InnerClasses attribute directly from bytecode instead of using reflection, and gracefully skip any inner classes that can't be loaded via the classloader.
  • Tests: Added regression test that compiles a class with an inner class extending a type that's then removed from the classpath, verifying ClassToAPI.process succeeds. Also added parser-level tests for InnerClasses attribute parsing.

This is a revival of #516 by @eed3si9n, rebased onto the current codebase with the tests that were requested at the time.

Changes

File What
ClassFile.scala Add innerClasses to trait, InnerClassInfo case class, isInnerClasses on AttributeInfo
Parser.scala Parse InnerClasses classfile attribute into Array[InnerClassInfo]
ClassToAPI.scala Replace reflection-based inner class discovery with classfile-based approach + try-catch
ParserSpecification.scala Tests for InnerClasses attribute parsing
ClassToAPISpecification.scala Regression test for sbt/sbt#117

Test plan

  • All existing zincClassfile3/test tests pass (26/26)
  • All existing zincApiInfo3/test tests pass (42/42), including existing inner class extraction tests
  • New regression test verifies ClassToAPI.process doesn't throw when inner class references a missing type
  • New parser tests verify correct extraction of InnerClasses attribute from JDK classes

🤖 Generated with Claude Code

@BrianHotopp BrianHotopp force-pushed the fix/sbt-117-noclassdeffounderror-inner-classes branch 2 times, most recently from 966552b to f614b98 Compare March 21, 2026 03:19
Fixes sbt/sbt#117

ClassToAPI used Java reflection (c.getClasses, c.getDeclaredClasses) to
discover inner classes during post-compile analysis. When an inner class
references types not on the classpath (e.g. optional dependencies like
JBoss VFS in Spring), these reflection calls throw NoClassDefFoundError,
crashing compilation.

The fix tries reflection first (preserving exact existing semantics),
and falls back to parsing the classfile's InnerClasses attribute when
reflection fails. The fallback logs a warning and gracefully skips any
inner classes that can't be loaded.

This is a revival of sbt#516 by @eed3si9n, rebased onto the current
codebase with tests added.

Co-Authored-By: Eugene Yokota <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@BrianHotopp BrianHotopp force-pushed the fix/sbt-117-noclassdeffounderror-inner-classes branch from f614b98 to bb0d02e Compare March 21, 2026 03:24
Replace getDeclaredClasses/getClasses with classfile InnerClasses
attribute parsing. Load each inner class individually so one broken
class doesn't take out the rest. Walk parent classfiles for inherited
public inner classes.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Copy link
Copy Markdown
Member

@eed3si9n eed3si9n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@eed3si9n eed3si9n merged commit 00e37d1 into sbt:develop Mar 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NoClassDefFound during analysis after java with exportJars = true

2 participants