Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Java → C# translation: an idiomatic C# printer driven by the modification and nullability analyses

Open
#115 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
8/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
csharp, java
Domain
compilers

Research direction

Start by reading the Kotlin printer, maddi-cst-print-kotlin, and its JavaToKotlinRatchet, KotlinPrintMessage and NullabilityVerdicts seam, since the issue says the C# printer mirrors them. The first step is the new maddi-cst-print-csharp module with its TypePrinter/MethodPrinter/FieldPrinter implementations and a csharp-printing.md design doc. The issue does not define a finished state beyond the six-step plan, so a newcomer should not start here.

Written by the indexing model from the issue text.

Description

enhancement extension front-end:csharp

Goal

Translate Java to idiomatic C#, automatically: parse with the openjdk front end, print the CST as C# with a new maddi-cst-print-csharp module, and judge the result with the C# compiler. This is the C# counterpart of the Java → Kotlin translation (maddi-cst-print-kotlin, JavaToKotlinRatchet).

This is the printer direction. #111 covers the opposite direction (a Roslyn front end reading C#) and lists a C# printer as out of scope. Nothing here depends on #111's plan: the struct spike, the Predefined refactor and the exporter are front-end work. A Java-parsed CST is JVM-shaped, and the printer starts from that.

What drives it: the analyses

What sets this apart from a syntax converter is that maddi's analyses decide the output:

  • Nullability (NullabilityPass in maddi-mod) decides where ? goes on reference types under #nullable enable, and where !, ?. or ?? appear. The NullabilityVerdicts seam that maddi-cst-print-kotlin uses carries over directly.
  • Modification and immutability decide whether something can be readonly, init-only or IReadOnlyList<T>/IReadOnlyDictionary<K,V>, whether a class can become a record, and whether a value type (readonly struct) is safe. Where the analysis proves code doesn't modify something, the C# says so.
  • Prepwork (getSetField) collapses getters and setters into C# properties, as it does for Kotlin.

Without verdicts the printer still produces valid C#, just less precise: every reference is nullable-oblivious and every collection mutable.

Automation

The translation has to run without manual editing. It is measured the way J2K is: a ratchet per corpus (fernflower first) that translates the main sources, compiles them with dotnet build / Roslyn, and records syntax errors, semantic errors, compiling files and eventually passing tests. A metric that gets worse fails, and one that gets better must be written into the ratchet file. A shape the printer can't translate gets a named message (as KotlinPrintMessage does), never silently wrong output.

Harder than Kotlin

  • No JVM interop. Kotlin can call the JDK and the corpus's original classes; C# can't. JDK references need a BCL mapping or a small runtime shim (java.util.*, String members, streams → LINQ, exceptions, boxing). This mapping is the bulk of the work. It also means there is no "compile against the original classes" stage: the whole corpus has to translate first.
  • Semantic gaps:
    • checked exceptions are dropped;
    • wildcards (? extends/? super) on reified generics;
    • non-static inner classes need an explicit outer reference;
    • anonymous classes become nested classes, or lambdas/delegates for functional interfaces;
    • enums with fields or bodies become classes;
    • Java methods are virtual by default, so C# needs virtual/override/sealed;
    • >>>, integer and char conversions, string switch, labelled break/continue (→ goto).

Plan

  1. maddi-cst-print-csharp: printers on the cst-api TypePrinter/MethodPrinter/FieldPrinter interfaces, reusing OutputElement and Formatter2; a CommonJavaToCSharp test base; a csharp-printing.md design document.
  2. Syntax coverage of every statement and expression form, plus a JavaToCSharpRatchet on fernflower counting syntax errors.
  3. Structure and the JDK → BCL mapping: semantic errors, then compiling files.
  4. Nullability verdicts → #nullable enable output, with a ratchet in maddi-mod (like TestJavaToKotlinFernflowerNullability).
  5. Modification/immutability verdicts → readonly, read-only collection interfaces, records.
  6. The corpus's tests, translated, passing.
Dominant language
Java
Stars
1
Forks
1
Avg merge
2h 51m
Merged PRs (30d)
3

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from CodeLaser/maddi

All issues in CodeLaser/maddi

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.