Skip to content

Reconcile TestParser with JavaParserAdapter to remove duplicated parsing logic #5078

Description

@jlerbsc

The test helper TestParser hand-rolls the "parse → unwrap the ParseResult → handle failure" flow that JavaParserAdapter already implements. This issue proposes reimplementing TestParser on top of JavaParserAdapter, keeping its public API unchanged, so there is a single implementation of that flow in the codebase.

Background

TestParser (javaparser-core-testing/src/test/java/com/github/javaparser/utils/TestParser.java) is a static test helper used by ~16 test classes. It:

  • caches one JavaParser per LanguageLevel in a static map;
  • exposes terse, level-parameterized static methods (parseExpression, parseStatement, parseCompilationUnit, parseBodyDeclaration, parseVariableDeclarationExpr, each with a BLEEDING_EDGE default and a (LanguageLevel, String) overload);
  • unwraps the ParseResult via a private unpack(...) that calls fail(result.getProblems().toString()) on error and returns the node otherwise.
    Separately, JavaParserAdapter (javaparser-core) already wraps a JavaParser and offers parseX(String) methods that return the node directly and throw ParseProblemException on failure. Since the migration that introduced StaticJavaParser.newParserAdapter(...), isolated per-configuration parsing is available through JavaParserAdapter as well — so there are now two idioms for the same thing, and TestParser.unpack duplicates JavaParserAdapter.handleResult.

Problem

Duplicated logic: the parse-and-unwrap mechanics exist in two places. This is a maintenance smell (two failure-handling paths, two unwrap paths) with no functional justification.

Proposed approach — wrap, don't rewrite
Keep TestParser's public API identical (so the ~16 callers are untouched) and change only its internals to delegate to JavaParserAdapter:

  • Change the cache value type to JavaParserAdapter:

    Map<LanguageLevel, JavaParserAdapter> parserCache;
    JavaParserAdapter parser(LanguageLevel ll) =
    parserCache.computeIfAbsent(ll, l ->
    StaticJavaParser.newParserAdapter(new ParserConfiguration().setLanguageLevel(l)));

  • Delegate each method and delete the private unpack(...):

    parseExpression(ll, expr) -> parser(ll).parseExpression(expr)
    parseStatement(ll, s) -> parser(ll).parseStatement(s)
    parseCompilationUnit(ll, s) -> parser(ll).parse(s) // name mapping
    // ...same for the remaining methods

Key decision: failure semantics
The one behavioral difference to resolve:

  • TestParser today: fail(problems) → a parse problem is reported as a JUnit failure (AssertionFailedError).
  • JavaParserAdapter: throws ParseProblemException → the test is reported as an error.

Two options:

  • (a) Adopt the adapter's behavior — parse problems become errors instead of failures. Defensible (a parse problem in a test is an error) but changes the reporting category for all callers.
  • (b) Preserve current behavior — wrap the delegated call to convert the exception back into fail():
    try { return parser(ll).parseExpression(expr); }
    catch (ParseProblemException e) { return fail(e.getProblems().toString()); }

Slightly less elegant, but strictly non-regressive.

Recommendation: (b) — the reconciliation should be invisible to callers.

Minor considerations

  • Generics: parseBodyDeclaration returns <T extends BodyDeclaration> T while the adapter returns BodyDeclaration; keep the same unchecked (T) cast as today. parseExpression already matches ().
  • Edge case: unpack does getResult().get() (throws if empty) whereas handleResult returns orElse(null) on a successful-but-empty result — a near-theoretical difference, worth a glance.
  • Out of scope: the parser cache's thread-safety caveat (shared, non-thread-safe JavaParser instances) is orthogonal and already documented in TestParser. It should be addressed separately, before enabling parallel test execution.

Acceptance criteria

  • TestParser's public method signatures are unchanged; no caller is modified.
    non terminé
  • unpack(...) and the manual ParseResult handling are removed; parsing goes through JavaParserAdapter.
    non terminé
  • Failure semantics preserved (option b) or an explicit, reviewed decision to adopt option a.
    non terminé
  • Full javaparser-core-testing suite passes with no other changes.

Notes

Low-risk, self-contained cleanup — independent of the StaticJavaParser test-isolation work; not a prerequisite for anything, just one less piece of duplicated logic.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ImprovementNot a bug, but a way that JP can be be enhanced to work better.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions