Repository: scalacenter/tasty-query
Pull request #393 (2023-11-19 12:58:54)

Add a basic printer for trees, with a method Tree.showBasic.
I only wrote tests for the weirdest forms of trees.


Patch:
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Flags.scala b/tasty-query/shared/src/main/scala/tastyquery/Flags.scala
index ef85743..6ead278 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Flags.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Flags.scala
@@ -55,7 +55,6 @@ private[tastyquery] object Flags:
   val Extension: Flag = newFlag("Extension")
   val Final: Flag = newFlag("Final")
   val Given: Flag = newFlag("Given")
-  val HasDefault: Flag = newFlag("HasDefault")
   val Implicit: Flag = newFlag("Implicit")
   val Infix: Flag = newFlag("Infix")
   val Inline: Flag = newFlag("Inline")
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Names.scala b/tasty-query/shared/src/main/scala/tastyquery/Names.scala
index b8e63b9..2a89882 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Names.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Names.scala
@@ -62,6 +62,7 @@ object Names {
     val m_+ : SimpleName = termName("+")
 
     val m_apply: SimpleName = termName("apply")
+    val m_unapply: SimpleName = termName("unapply")
   }
 
   object tpnme {
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Printers.scala b/tasty-query/shared/src/main/scala/tastyquery/Printers.scala
index 5da3b87..71a1058 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Printers.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Printers.scala
@@ -75,10 +75,7 @@ private[tastyquery] object Printers:
       case tpe: AppliedType =>
         print(tpe.tycon)
         print("[")
-        print(tpe.args.head)
-        for arg <- tpe.args.tail do
-          print(", ")
-          print(arg)
+        printCommaSeparatedList(tpe.args)(print(_))
         print("]")
       case tpe: ByNameType =>
         print("=> ")
@@ -88,10 +85,7 @@ private[tastyquery] object Printers:
         print("*")
       case tpe: TypeLambda =>
         print("([")
-        print(tpe.typeLambdaParams.head)
-        for tparam <- tpe.typeLambdaParams.tail do
-          print(", ")
-          print(tparam)
+        printCommaSeparatedList(tpe.typeLambdaParams)(print(_))
         print("] =>> ")
         print(tpe.resultType)
         print(")")
@@ -128,19 +122,17 @@ private[tastyquery] object Printers:
       case tpe: MatchType =>
         print("(")
         print(tpe.scrutinee)
+        print(" match")
         if !isSyntacticAny(tpe.bound) then
           print(" <: ")
           print(tpe.bound)
-        print(" match { ")
-        var first = true
-        for caze <- tpe.cases do
-          if first then first = false
-          else print("; ")
+        printBlock(tpe.cases) { caze =>
           print("case ")
           print(caze.pattern)
           print(" => ")
           print(caze.result)
-        print(" })")
+        }
+        print(")")
       case tpe: SkolemType =>
         print("(\u2203") // ∃ U+2203 There Exists
         print(tpe.debugID)
@@ -168,23 +160,19 @@ private[tastyquery] object Printers:
         print("(")
         if tpe.isContextual then print("using ")
         else if tpe.isImplicit then print("implicit ")
-        var first = true
-        for (argName, argType) <- tpe.paramNames.lazyZip(tpe.paramTypes) do
-          if first then first = false
-          else print(", ")
-          print(argName)
+        printCommaSeparatedList(tpe.paramNames.zip(tpe.paramTypes)) { (paramName, paramType) =>
+          print(paramName)
           print(": ")
-          print(argType)
+          print(paramType)
+        }
         print(")")
         print(tpe.resultType)
       case tpe: PolyType =>
         print("[")
-        var first = true
-        for (argName, argBounds) <- tpe.paramNames.lazyZip(tpe.paramTypeBounds) do
-          if first then first = false
-          else print(", ")
-          print(argName)
-          print(argBounds)
+        printCommaSeparatedList(tpe.paramNames.zip(tpe.paramTypeBounds)) { (paramName, paramTypeBounds) =>
+          print(paramName)
+          print(paramTypeBounds)
+        }
         print("]")
         print(tpe.resultType)
 
@@ -204,6 +192,545 @@ private[tastyquery] object Printers:
           print(high)
     end print
 
+    final def printAnyTree(tree: Tree): Unit = tree match
+      case tree: TopLevelTree        => printTopLevel(tree)
+      case tree: TypeTree            => print(tree)
+      case tree: PatternTree         => print(tree)
+      case tree: TypeDefinitionTree  => print(tree)
+      case tree: Template            => print(tree)
+      case tree: ImportIdent         => print(tree.name)
+      case tree: ImportSelector      => print(tree)
+      case tree: CaseDef             => print(tree)
+      case tree: TypeCaseDef         => print(tree)
+      case tree: WildcardTypeArgTree => print(tree)
+      case tree: SelfDef             => print(tree)
+    end printAnyTree
+
+    def printTopLevel(tree: TopLevelTree): Unit = tree match
+      case PackageDef(sym, stats) =>
+        print("package ")
+        print(sym.displayFullName)
+        printBlock(stats)(printTopLevel(_))
+
+      case tree: StatementTree =>
+        print(tree)
+    end printTopLevel
+
+    def print(tree: StatementTree): Unit = tree match
+      case Import(expr, selectors) =>
+        print("import ")
+        print(expr)
+        print(".")
+        selectors match
+          case selector :: Nil =>
+            print(selector)
+          case _ =>
+            print("{")
+            printCommaSeparatedList(selectors)(print(_))
+            print("}")
+
+      case Export(expr, selectors) =>
+        print("export ")
+        print(expr)
+        print(".")
+        selectors match
+          case selector :: Nil =>
+            print(selector)
+          case _ =>
+            print("{")
+            printCommaSeparatedList(selectors)(print(_))
+            print("}")
+
+      case ClassDef(name, rhs, symbol) =>
+        if symbol.isTrait then print("trait ")
+        else if symbol.isModuleClass then print("object class ")
+        else print("class ")
+
+        print(name)
+        print(rhs)
+
+      case TypeMember(name, rhs, symbol) =>
+        print("type ")
+        print(name)
+        print(rhs)
+
+      case TypeParam(name, bounds, symbol) =>
+        symbol match
+          case symbol: ClassTypeParamSymbol => print(symbol.declaredVariance)
+          case symbol: LocalTypeParamSymbol => ()
+        print(name)
+        print(bounds)
+
+      case ValDef(name, tpt, rhs, symbol) =>
+        symbol.kind match
+          case TermSymbolKind.Module  => print("object lazy val ")
+          case TermSymbolKind.Val     => print("val ")
+          case TermSymbolKind.LazyVal => print("lazy val ")
+          case TermSymbolKind.Var     => print("var ")
+          case TermSymbolKind.Def     => print("def ") // should not happen
+        print(name)
+        print(": ")
+        print(tpt)
+        for r <- rhs do
+          print(" = ")
+          print(r)
+
+      case DefDef(name, paramLists, resultTpt, rhs, symbol) =>
+        print("def ")
+        print(name)
+
+        for paramList <- paramLists do
+          paramList match
+            case Left(valDefs) =>
+              if valDefs.isEmpty then print("()")
+              else
+                print("(")
+                val first = valDefs.head
+                if first.symbol.isGivenOrUsing then print("using ")
+                else if first.symbol.isImplicit then print("implicit ")
+                printCommaSeparatedList(valDefs) { valDef =>
+                  print(valDef.name)
+                  print(": ")
+                  print(valDef.tpt)
+                }
+                print(")")
+            case Right(typeDefs) =>
+              print("[")
+              printCommaSeparatedList(typeDefs) { typeDef =>
+                print(typeDef.name)
+                print(typeDef.bounds)
+              }
+              print("]")
+        end for
+
+        print(": ")
+        print(resultTpt)
+
+        for body <- rhs do
+          print(" = ")
+          print(body)
+
+      case Ident(name) =>
+        print(name)
+
+      case Select(qualifier, name) =>
+        print(qualifier)
+        print(".")
+        print(name)
+
+      case SelectOuter(qualifier, levels) =>
+        print(qualifier)
+        for _ <- 0 until levels do print(".<outer>")
+
+      case This(qualifier) =>
+        print(qualifier)
+        print(".this")
+
+      case Super(qual, optMix) =>
+        print(qual)
+        print(".super")
+        for mix <- optMix do
+          print("[")
+          print(mix)
+          print("]")
+
+      case tree: Apply =>
+        printApplication(tree, hideNew = false)
+
+      case tree: TypeApply =>
+        printApplication(tree, hideNew = false)
+
+      case New(tpt) =>
+        print("new ")
+        print(tpt)
+
+      case Typed(expr, tpt) =>
+        print("(")
+        print(expr)
+        print(": ")
+        print(tpt)
+        print(")")
+
+      case Assign(lhs, rhs) =>
+        print(lhs)
+        print(" = ")
+        print(rhs)
+
+      case NamedArg(name, arg) =>
+        print(name)
+        print(" = ")
+        print(arg)
+
+      case Block(stats, expr) =>
+        printBlock(stats, expr)(print(_))
+
+      case If(cond, thenPart, elsePart) =>
+        print("if ")
+        print(cond)
+        print(" then ")
+        print(thenPart)
+        print(" else ")
+        print(elsePart)
+
+      case InlineIf(cond, thenPart, elsePart) =>
+        print("inline ")
+        print(If(cond, thenPart, elsePart)(tree.pos))
+
+      case Lambda(meth, tpt) =>
+        print("(")
+        print(meth)
+        print(": ")
+        tpt match
+          case Some(tpt) => print(tpt)
+          case None      => print("<function>")
+        print(")")
+
+      case Match(selector, cases) =>
+        print("(")
+        print(selector)
+        print(" match")
+        printBlock(cases)(print(_))
+        print(")")
+
+      case InlineMatch(selector, cases) =>
+        print("inline ")
+        print(InlineMatch(selector, cases)(tree.pos))
+
+      case SeqLiteral(elems, elemtpt) =>
+        print("[")
+        printCommaSeparatedList(elems)(print(_))
+        print(": ")
+        print(elemtpt)
+        print("]")
+
+      case While(cond, body) =>
+        print("while ")
+        print(cond)
+        print(" do ")
+        print(body)
+
+      case Throw(expr) =>
+        print("throw ")
+        print(expr)
+
+      case Try(expr, cases, finalizer) =>
+        print("try ")
+        print(expr)
+        if cases.nonEmpty then
+          print(" catch")
+          printBlock(cases)(print(_))
+        for fin <- finalizer do
+          print(" finally ")
+          print(fin)
+
+      case Literal(constant) =>
+        print(constant)
+
+      case Return(expr, from) =>
+        expr match
+          case Some(expr) =>
+            print("return ")
+            print(expr)
+          case None =>
+            print("return")
+
+      case Inlined(expr, caller, bindings) =>
+        for c <- caller do
+          print("<inlined: ")
+          print(c)
+          print(">")
+        printBlock(bindings, expr)(print(_))
+    end print
+
+    def print(caze: CaseDef): Unit =
+      print("case ")
+      print(caze.pattern)
+      for guard <- caze.guard do
+        print(" if ")
+        print(guard)
+      print(" => ")
+      print(caze.body)
+    end print
+
+    def printApplication(tree: TermTree, hideNew: Boolean): Unit = tree match
+      case Apply(fun, args) =>
+        printApplication(fun, hideNew)
+        print("(")
+        printCommaSeparatedList(args)(print(_))
+        print(")")
+
+      case TypeApply(fun, args) =>
+        printApplication(fun, hideNew)
+        print("[")
+        printCommaSeparatedList(args)(print(_))
+        print("]")
+
+      case Select(New(tpt), SignedName(nme.Constructor, _, _)) =>
+        if !hideNew then print("new ")
+        print(tpt)
+
+      case Select(New(tpt), nme.Constructor) =>
+        if !hideNew then print("new ")
+        print(tpt)
+
+      case Select(qual, SignedName(underlying, _, _)) =>
+        print(qual)
+        print(".")
+        print(underlying)
+
+      case New(tpt) if hideNew =>
+        print(tpt)
+
+      case _ =>
+        print(tree)
+    end printApplication
+
+    def print(tree: ImportSelector): Unit =
+      if tree.isGiven then
+        tree.bound match
+          case Some(bound) =>
+            print("given ")
+            print(bound)
+          case None =>
+            print("given")
+      else
+        print(tree.name)
+        for renamed <- tree.renamed do
+          print(" as ")
+          print(renamed.name)
+    end print
+
+    def print(tree: Template): Unit =
+      val Template(constr, parents, self, body) = tree
+
+      val (typeParams, otherStats) = body.partition(_.isInstanceOf[TypeParam])
+
+      if typeParams.nonEmpty then
+        print("[")
+        printCommaSeparatedList(typeParams)(print(_))
+        print("]")
+      end if
+
+      print(" extends ")
+      parents.head match
+        case parent: TermTree => printApplication(parent, hideNew = true)
+        case parent: TypeTree => print(parent)
+      for parent <- parents.tail do
+        print(" with ")
+        parent match
+          case parent: TermTree => printApplication(parent, hideNew = true)
+          case parent: TypeTree => print(parent)
+      end for
+
+      printBlock[SelfDef | StatementTree](self.toList ::: constr :: otherStats) { stat =>
+        stat match
+          case stat: SelfDef       => print(stat)
+          case stat: StatementTree => print(stat)
+      }
+    end print
+
+    def print(tree: SelfDef): Unit =
+      print(tree.name)
+      print(": ")
+      print(tree.tpt)
+      print(" =>")
+    end print
+
+    def print(tree: PatternTree): Unit = tree match
+      case WildcardPattern(tpe) =>
+        print("_")
+
+      case TypeTest(body, tpt) =>
+        print(body)
+        print(": ")
+        print(tpt)
+
+      case Bind(name, body, symbol) =>
+        print(name)
+        print(" @ ")
+        print(body)
+
+      case Alternative(trees) =>
+        print(trees.head)
+        for tree <- trees do
+          print(" | ")
+          print(tree)
+
+      case Unapply(fun, implicits, patterns) =>
+        fun match
+          case Select(extractor, SignedName(nme.m_unapply, _, _)) =>
+            print(extractor)
+          case _ =>
+            printApplication(fun, hideNew = false)
+        print("(")
+        printCommaSeparatedList(patterns)(print(_))
+        print(")")
+        if implicits.nonEmpty then
+          print("(")
+          printCommaSeparatedList(implicits)(print(_))
+          print(")")
+
+      case ExprPattern(expr) =>
+        print(expr)
+    end print
+
+    def print(tree: TypeTree): Unit = tree match
+      case TypeIdent(name) =>
+        print(name)
+
+      case TypeWrapper(tp) =>
+        print(tp)
+
+      case SingletonTypeTree(ref) =>
+        print(ref)
+        print(".type")
+
+      case RefinedTypeTree(underlying, refinements, refinedCls) =>
+        print("(")
+        print(underlying)
+        printBlock(refinements) { refinement =>
+          print(refinement)
+        }
+        print(")")
+
+      case ByNameTypeTree(result) =>
+        print("=> ")
+        print(result)
+
+      case AppliedTypeTree(tycon, args) =>
+        print(tycon)
+        print("[")
+        printCommaSeparatedList(args) { arg =>
+          arg match
+            case arg: TypeTree            => print(arg)
+            case arg: WildcardTypeArgTree => print(arg)
+        }
+        print("]")
+
+      case SelectTypeTree(qualifier, name) =>
+        print(qualifier)
+        print(".")
+        print(name)
+
+      case TermRefTypeTree(qualifier, name) =>
+        print(qualifier)
+        print(".")
+        print(name)
+
+      case AnnotatedTypeTree(tpt, annotation) =>
+        print("(")
+        print(tpt)
+        print(" ")
+        print(annotation)
+        print(")")
+
+      case MatchTypeTree(bound, selector, cases) =>
+        print("(")
+        print(selector)
+        print(" match")
+        if !isSyntacticAny(bound.toType) then
+          print(" <: ")
+          print(bound)
+        printBlock(cases)(print(_))
+        print(")")
+
+      case TypeTreeBind(name, body, symbol) =>
+        print(name)
+
+      case TypeLambdaTree(tparams, body) =>
+        print("([")
+        printCommaSeparatedList(tparams)(print(_))
+        print("] =>> ")
+        print(body)
+        print(")")
+
+      case TypeBindingsTree(bindings, body) =>
+        printBlock[TypeMember | TypeTree](bindings, body) { elem =>
+          elem match
+            case elem: TypeMember => print(elem)
+            case elem: TypeTree   => print(elem)
+        }
+    end print
+
+    def print(tree: WildcardTypeArgTree): Unit =
+      print("?")
+      print(tree.bounds)
+    end print
+
+    def print(tree: TypeCaseDef): Unit =
+      print("case ")
+      print(tree.pattern)
+      print(" => ")
+      print(tree.body)
+    end print
+
+    def print(tree: TypeDefinitionTree): Unit = tree match
+      case InferredTypeBoundsTree(bounds) =>
+        print(bounds)
+
+      case ExplicitTypeBoundsTree(low, high) =>
+        if !isSyntacticNothing(low.toType) then
+          print(" >: ")
+          print(low)
+        if !isSyntacticAny(high.toType) then
+          print(" <: ")
+          print(high)
+
+      case TypeAliasDefinitionTree(alias) =>
+        alias match
+          case MatchTypeTree(bound, selector, cases) =>
+            if !isSyntacticAny(bound.toType) then
+              print(" <: ")
+              print(bound)
+            print(" = ")
+            print(selector)
+            print(" match")
+            printBlock(cases) { caze =>
+              print("case ")
+              print(caze.pattern)
+              print(" => ")
+              print(caze.body)
+            }
+          case _ =>
+            print(" = ")
+            print(alias)
+
+      case OpaqueTypeAliasDefinitionTree(bounds, alias) =>
+        print(bounds)
+        print(alias)
+
+      case PolyTypeDefinitionTree(tparams, body) =>
+        print("[")
+        printCommaSeparatedList(tparams)(print(_))
+        print("]")
+        print(body)
+
+      case NamedTypeBoundsTree(name, bounds) =>
+        print(name)
+    end print
+
+    def printCommaSeparatedList[A](elems: List[A])(printElem: A => Unit): Unit =
+      if elems.nonEmpty then
+        printElem(elems.head)
+        for elem <- elems.tail do
+          print(", ")
+          printElem(elem)
+    end printCommaSeparatedList
+
+    final def printBlock[A](initElems: List[A], lastElem: A)(printElem: A => Unit): Unit =
+      printBlock(initElems :+ lastElem)(printElem)
+
+    def printBlock[A](elems: List[A])(printElem: A => Unit): Unit =
+      if elems.isEmpty then print(" {}")
+      else
+        print(" { ")
+        printElem(elems.head)
+        for elem <- elems.tail do
+          print("; ")
+          printElem(elem)
+        print(" }")
+    end printBlock
+
     def print(name: Name): Unit =
       print(name.toString())
     end print
@@ -249,7 +776,8 @@ private[tastyquery] object Printers:
     end printEscaped
 
     def print(annot: Annotation): Unit =
-      print("@<annot>") // TODO Improve this
+      print("@")
+      printApplication(annot.tree, hideNew = true)
 
     def print(tparam: TypeConstructorParam): Unit =
       print(tparam.declaredVariance)
@@ -270,6 +798,41 @@ private[tastyquery] object Printers:
       out.write(str)
   end Printer
 
+  /** A `Printer` that prints blocks with line separators and indentation. */
+  class MultilinePrinter(out: Writer) extends Printer(out):
+    private var spaces: String = "                                        " // 40 spaces
+    private var currentIndent: Int = 0
+
+    private final val IndentWidth = 2
+
+    protected final def printNewLine(): Unit =
+      print("\n")
+      out.write(spaces, 0, currentIndent)
+
+    protected final def printNewLineAndIndent(): Unit =
+      currentIndent += IndentWidth
+      if spaces.length() < currentIndent then spaces = spaces + spaces
+      printNewLine()
+
+    protected final def printNewLineAndOutdent(): Unit =
+      if currentIndent < IndentWidth then throw IllegalStateException("Trying to outdent more than indent")
+      currentIndent -= IndentWidth
+      printNewLine()
+
+    override def printBlock[A](elems: List[A])(printElem: A => Unit): Unit =
+      if elems.isEmpty then print(" {}")
+      else
+        print(" {")
+        printNewLineAndIndent()
+        printElem(elems.head)
+        for elem <- elems.tail do
+          printNewLine()
+          printElem(elem)
+        printNewLineAndOutdent()
+        print("}")
+    end printBlock
+  end MultilinePrinter
+
   private def isSyntacticNothing(tpe: Type): Boolean = tpe match
     case tpe: TypeRef => tpe.name == tpnme.Nothing && isScalaPackageRef(tpe.prefix)
     case _            => false
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala b/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala
index 6e742b2..870b48b 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Symbols.scala
@@ -583,17 +583,6 @@ object Symbols {
     /** Is this symbol an exporter generated by an `export` statement? */
     final def isExport: Boolean = flags.is(Exported)
 
-    /** Is this symbol a method with at least one parameter with a default value? */
-    final def hasParamWithDefault: Boolean =
-      paramSymss.exists {
-        case Left(termParams) => termParams.exists(_.isParamWithDefault)
-        case Right(value)     => false
-      }
-    end hasParamWithDefault
-
-    /** Is this symbol a method parameter with a default value? */
-    final def isParamWithDefault: Boolean = flags.isAllOf(HasDefault)
-
     /** Get the module class of this module value definition, if it exists:
       * - for `object val C` => `object class C[$]`
       */
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Trees.scala b/tasty-query/shared/src/main/scala/tastyquery/Trees.scala
index b659f0d..ccee427 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Trees.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Trees.scala
@@ -52,6 +52,12 @@ object Trees {
     end computeAutoPos
 
     def withPos(pos: SourcePosition): Tree
+
+    final def showBasic: String =
+      Printers.withWriterToString(writer => new Printers.Printer(writer).printAnyTree(this))
+
+    final def showMultiline: String =
+      Printers.withWriterToString(writer => new Printers.MultilinePrinter(writer).printAnyTree(this))
   }
 
   sealed abstract class TopLevelTree(pos: SourcePosition) extends Tree(pos):
diff --git a/tasty-query/shared/src/main/scala/tastyquery/Types.scala b/tasty-query/shared/src/main/scala/tastyquery/Types.scala
index 46535f6..bb02b50 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/Types.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/Types.scala
@@ -266,6 +266,9 @@ object Types {
     def showBasic: String =
       Printers.withWriterToString(writer => new Printers.Printer(writer).print(this))
 
+    final def showMultiline: String =
+      Printers.withWriterToString(writer => new Printers.MultilinePrinter(writer).print(this))
+
     def requireType: Type = this match
       case self: Type => self
       case self       => throw InvalidProgramStructureException(s"Unexpected non-type $self")
diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala
index 0b45dda..ef2d988 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/reader/pickles/PickleReader.scala
@@ -487,7 +487,7 @@ private[pickles] class PickleReader {
     if pickleFlags.isStable then flags |= StableRealizable
     if pickleFlags.isStatic then flags |= Static
     if pickleFlags.isCaseAccessor then flags |= CaseAccessor
-    if pickleFlags.hasDefault then flags |= HasDefault
+    // if pickleFlags.hasDefault then flags |= HasDefault
     if pickleFlags.isTrait then flags |= Trait
     // if pickleFlags.isBridge then flags |= Bridge
     if pickleFlags.isAccessor then flags |= Accessor
diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala
index cfead58..3ab6c05 100644
--- a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala
+++ b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala
@@ -312,7 +312,7 @@ private[tasties] class TreeUnpickler private (
         case CASEaccessor  => addFlag(CaseAccessor)
         case COVARIANT     => addFlag(Covariant)
         case CONTRAVARIANT => addFlag(Contravariant)
-        case HASDEFAULT    => addFlag(HasDefault)
+        case HASDEFAULT    => ignoreFlag()
         case STABLE        => addFlag(StableRealizable)
         case EXTENSION     => addFlag(Extension)
         case GIVEN         => addFlag(Given)


Test patch:
diff --git a/tasty-query/shared/src/test/scala/tastyquery/PositionSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/PositionSuite.scala
index 84c82f0..651edf0 100644
--- a/tasty-query/shared/src/test/scala/tastyquery/PositionSuite.scala
+++ b/tasty-query/shared/src/test/scala/tastyquery/PositionSuite.scala
@@ -56,7 +56,7 @@ class PositionSuite extends RestrictedUnpicklingSuite {
   testUnpickle("literal", "simple_trees.Constants") { tree =>
     assertEquals(
       collectCode[Literal](tree),
-      List("()", "false", "true", "1", "1", "'a'", "1", "1L", "1.1f", "1.1d", "\"string\"", "null")
+      List("()", "false", "true", "1", "1", "'a'", "1", "1L", "1.5f", "1.1d", "\"string\"", "null")
     )
   }
 
diff --git a/tasty-query/shared/src/test/scala/tastyquery/PrintersTest.scala b/tasty-query/shared/src/test/scala/tastyquery/PrintersTest.scala
index b4b66c6..e7d6eb8 100644
--- a/tasty-query/shared/src/test/scala/tastyquery/PrintersTest.scala
+++ b/tasty-query/shared/src/test/scala/tastyquery/PrintersTest.scala
@@ -145,4 +145,171 @@ class PrintersTest extends UnrestrictedUnpicklingSuite:
       "[A >: scala.Null <: scala.AnyRef](x: java.lang.String, y: A)x.type"
     )
   }
+
+  testWithContext("tree printers") {
+    def testShowBasic(tree: Tree, expected: String)(using munit.Location): Unit =
+      assert(clue(tree.showBasic) == clue(expected))
+
+    def testShowBasicMember(cls: ClassSymbol, memberName: Name, expected: String)(using munit.Location): Unit =
+      val member = memberName match
+        case memberName: UnsignedTermName => cls.findNonOverloadedDecl(memberName)
+        case _                            => cls.findMember(memberName)
+      testShowBasic(member.tree.get, expected)
+
+    val ConstantsClass = ctx.findTopLevelClass("simple_trees.Constants")
+    testShowBasicMember(ConstantsClass, termName("unitVal"), "val unitVal: scala.Unit = ()")
+    testShowBasicMember(ConstantsClass, termName("falseVal"), "val falseVal: scala.Boolean = false")
+    testShowBasicMember(ConstantsClass, termName("byteVal"), "val byteVal: Byte = 1b")
+    testShowBasicMember(ConstantsClass, termName("shortVal"), "val shortVal: Short = 1s")
+    testShowBasicMember(ConstantsClass, termName("charVal"), "val charVal: scala.Char = 'a'")
+    testShowBasicMember(ConstantsClass, termName("intVal"), "val intVal: scala.Int = 1")
+    testShowBasicMember(ConstantsClass, termName("longVal"), "val longVal: Long = 1L")
+    testShowBasicMember(ConstantsClass, termName("floatVal"), "val floatVal: Float = 1.5f")
+    testShowBasicMember(ConstantsClass, termName("doubleVal"), "val doubleVal: Double = 1.1")
+    testShowBasicMember(ConstantsClass, termName("stringVal"), "val stringVal: java.lang.String = \"string\"")
+    testShowBasicMember(ConstantsClass, termName("nullVal"), "val nullVal: scala.Null = null")
+
+    val GenericClassWithNestedGenericClass = ctx.findTopLevelClass("simple_trees.GenericClassWithNestedGeneric")
+    testShowBasic(
+      GenericClassWithNestedGenericClass.tree.get,
+      "class GenericClassWithNestedGeneric[T] extends java.lang.Object() {"
+        + " def <init>[T](): scala.Unit; "
+        + "class NestedGeneric[U] extends java.lang.Object() { def <init>[U](): scala.Unit } }"
+    )
+
+    val PatternMatchingOnCaseClassClass = ctx.findTopLevelClass("simple_trees.PatternMatchingOnCaseClass")
+    testShowBasicMember(
+      PatternMatchingOnCaseClassClass,
+      termName("caseMatching"),
+      "def caseMatching(c: AbstractForCaseClasses): Int = (c match { "
+        + "case FirstCase(x @ _): simple_trees.FirstCase =>  { (x: scala.Int) }; "
+        + "case SecondCase(y @ _): simple_trees.SecondCase =>  { (y: scala.Int) }; "
+        + "case _ =>  { 0 } })"
+    )
+
+    val ClassWithSelfClass = ctx.findTopLevelClass("simple_trees.ClassWithSelf")
+    testShowBasic(
+      ClassWithSelfClass.tree.get,
+      "class ClassWithSelf extends java.lang.Object() with TraitWithSelf { "
+        + "self: simple_trees.ClassWithSelf =>; def <init>(): scala.Unit }"
+    )
+
+    val DoublePolyExtensionsClass = ctx.findTopLevelClass("simple_trees.DoublePolyExtensions")
+    testShowBasicMember(
+      DoublePolyExtensionsClass,
+      termName("+++:"),
+      "def +++:[A][B >: A](x: B)(list: List[A]): List[B] = list.::[B](x)"
+    )
+
+    val HigherKindedClass = ctx.findTopLevelClass("simple_trees.HigherKinded")
+    testShowBasic(
+      HigherKindedClass.tree.get,
+      "trait HigherKinded[A <: ([_$1] =>> scala.Any)] extends java.lang.Object { "
+        + "def <init>[A[_$1]](): scala.Unit; "
+        + "def m[T](x: A[T]): A[T]; "
+        + "def f[B[_$2], T](x: B[T]): B[T] }"
+    )
+
+    val MatchTypeClass = ctx.findTopLevelClass("simple_trees.MatchType")
+    testShowBasicMember(MatchTypeClass, typeName("MT"), "type MT[X] = X match { case Int => String }")
+    testShowBasicMember(
+      MatchTypeClass,
+      typeName("MTWithBound"),
+      "type MTWithBound[X] <: Product = X match { case Int => Some[Int] }"
+    )
+    testShowBasicMember(
+      MatchTypeClass,
+      typeName("MTWithWildcard"),
+      "type MTWithWildcard[X] = X match { case _ => Int }"
+    )
+    testShowBasicMember(MatchTypeClass, typeName("MTWithBind"), "type MTWithBind[X] = X match { case List[t] => t }")
+  }
+
+  testWithContext("multiline tree printers") {
+    def testShowMultiline(tree: Tree, expected: String)(using munit.Location): Unit =
+      assert(clue(tree.showMultiline) == clue(expected))
+
+    def testShowMultilineMember(cls: ClassSymbol, memberName: Name, expected: String)(using munit.Location): Unit =
+      val member = memberName match
+        case memberName: UnsignedTermName => cls.findNonOverloadedDecl(memberName)
+        case _                            => cls.findMember(memberName)
+      testShowMultiline(member.tree.get, expected)
+
+    val GenericClassWithNestedGenericClass = ctx.findTopLevelClass("simple_trees.GenericClassWithNestedGeneric")
+    testShowMultiline(
+      GenericClassWithNestedGenericClass.tree.get,
+      """class GenericClassWithNestedGeneric[T] extends java.lang.Object() {
+        |  def <init>[T](): scala.Unit
+        |  class NestedGeneric[U] extends java.lang.Object() {
+        |    def <init>[U](): scala.Unit
+        |  }
+        |}""".stripMargin
+    )
+
+    val PatternMatchingOnCaseClassClass = ctx.findTopLevelClass("simple_trees.PatternMatchingOnCaseClass")
+    testShowMultilineMember(
+      PatternMatchingOnCaseClassClass,
+      termName("caseMatching"),
+      """def caseMatching(c: AbstractForCaseClasses): Int = (c match {
+        |  case FirstCase(x @ _): simple_trees.FirstCase =>  {
+        |    (x: scala.Int)
+        |  }
+        |  case SecondCase(y @ _): simple_trees.SecondCase =>  {
+        |    (y: scala.Int)
+        |  }
+        |  case _ =>  {
+        |    0
+        |  }
+        |})""".stripMargin
+    )
+
+    val ClassWithSelfClass = ctx.findTopLevelClass("simple_trees.ClassWithSelf")
+    testShowMultiline(
+      ClassWithSelfClass.tree.get,
+      """class ClassWithSelf extends java.lang.Object() with TraitWithSelf {
+        |  self: simple_trees.ClassWithSelf =>
+        |  def <init>(): scala.Unit
+        |}""".stripMargin
+    )
+
+    val HigherKindedClass = ctx.findTopLevelClass("simple_trees.HigherKinded")
+    testShowMultiline(
+      HigherKindedClass.tree.get,
+      """trait HigherKinded[A <: ([_$1] =>> scala.Any)] extends java.lang.Object {
+        |  def <init>[A[_$1]](): scala.Unit
+        |  def m[T](x: A[T]): A[T]
+        |  def f[B[_$2], T](x: B[T]): B[T]
+        |}""".stripMargin
+    )
+
+    val MatchTypeClass = ctx.findTopLevelClass("simple_trees.MatchType")
+    testShowMultilineMember(
+      MatchTypeClass,
+      typeName("MT"),
+      """type MT[X] = X match {
+        |  case Int => String
+        |}""".stripMargin
+    )
+    testShowMultilineMember(
+      MatchTypeClass,
+      typeName("MTWithBound"),
+      """type MTWithBound[X] <: Product = X match {
+        |  case Int => Some[Int]
+        |}""".stripMargin
+    )
+    testShowMultilineMember(
+      MatchTypeClass,
+      typeName("MTWithWildcard"),
+      """type MTWithWildcard[X] = X match {
+        |  case _ => Int
+        |}""".stripMargin
+    )
+    testShowMultilineMember(
+      MatchTypeClass,
+      typeName("MTWithBind"),
+      """type MTWithBind[X] = X match {
+        |  case List[t] => t
+        |}""".stripMargin
+    )
+  }
 end PrintersTest
diff --git a/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala
index 66bbae5..b18fec3 100644
--- a/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala
+++ b/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala
@@ -426,7 +426,7 @@ class ReadTreeSuite extends RestrictedUnpicklingSuite {
     }
     assert(containsSubtree(longConstMatch)(clue(tree)))
 
-    val floatConstMatch: StructureCheck = { case ValDef(SimpleName("floatVal"), _, Some(Literal(Constant(1.1f))), _) =>
+    val floatConstMatch: StructureCheck = { case ValDef(SimpleName("floatVal"), _, Some(Literal(Constant(1.5f))), _) =>
     }
     assert(containsSubtree(floatConstMatch)(clue(tree)))
 
diff --git a/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala
index 4be6a06..db20727 100644
--- a/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala
+++ b/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala
@@ -1051,45 +1051,18 @@ class TypeSuite extends UnrestrictedUnpicklingSuite {
         throw AssertionError(s"unexpected type $tpe")
   }
 
-  testWithContext("default-params") {
-    extension (sym: TermSymbol) def paramCount: Int = sym.declaredType.asInstanceOf[MethodType].paramNames.size
-
+  testWithContext("scala-2-default-params") {
     val DefaultParamsClass = ctx.findTopLevelClass("simple_trees.DefaultParams")
     assert(clue(DefaultParamsClass.getNonOverloadedDecl(DefaultGetterName(termName("foo"), 0))).isEmpty)
     DefaultParamsClass.findNonOverloadedDecl(DefaultGetterName(termName("foo"), 1))
     DefaultParamsClass.findNonOverloadedDecl(DefaultGetterName(termName("foo"), 2))
     assert(clue(DefaultParamsClass.getNonOverloadedDecl(DefaultGetterName(termName("foo"), 3))).isEmpty)
 
-    val fooOverloads = DefaultParamsClass.findAllOverloadedDecls(termName("foo"))
-
-    val fooWithDefaults = fooOverloads.find(_.paramCount == 3).get
-    assert(clue(fooWithDefaults.hasParamWithDefault) && !clue(fooWithDefaults.isParamWithDefault))
-    val List(Left(fooWithDefaultsParams)) = fooWithDefaults.paramSymss: @unchecked
-    assert(clue(fooWithDefaultsParams.map(_.isParamWithDefault)) == List(false, true, true))
-    assert(clue(fooWithDefaultsParams.map(_.hasParamWithDefault)).forall(_ == false))
-
-    for fooOverload <- fooOverloads if fooOverload ne fooWithDefaults do
-      assert(!clue(fooOverload.hasParamWithDefault) && !clue(fooOverload.isParamWithDefault))
-      val List(Left(params)) = fooOverload.paramSymss: @unchecked
-      assert(clue(params.map(_.isParamWithDefault)).forall(_ == false))
-      assert(clue(params.map(_.hasParamWithDefault)).forall(_ == false))
-  }
-
-  testWithContext("default-params-scala-2") {
     val IteratorClass = ctx.findTopLevelClass("scala.collection.Iterator")
     assert(clue(IteratorClass.getNonOverloadedDecl(DefaultGetterName(termName("indexWhere"), 0))).isEmpty)
     IteratorClass.findNonOverloadedDecl(DefaultGetterName(termName("indexWhere"), 1))
     assert(clue(IteratorClass.getNonOverloadedDecl(DefaultGetterName(termName("indexWhere"), 2))).isEmpty)
 
-    locally {
-      val indexWhere = IteratorClass.findNonOverloadedDecl(termName("indexWhere"))
-      assert(clue(indexWhere.hasParamWithDefault) && !clue(indexWhere.isParamWithDefault))
-
-      val List(Left(List(p, from))) = indexWhere.paramSymss: @unchecked
-      assert(!clue(p.hasParamWithDefault) && !clue(p.isParamWithDefault))
-      assert(!clue(from.hasParamWithDefault) && clue(from.isParamWithDefault))
-    }
-
     val ArrayDequeModClass = ctx.findTopLevelModuleClass("scala.collection.mutable.ArrayDeque")
     ArrayDequeModClass.findNonOverloadedDecl(DefaultGetterName(nme.Constructor, 0))
     assert(clue(ArrayDequeModClass.getNonOverloadedDecl(DefaultGetterName(nme.Constructor, 1))).isEmpty)
diff --git a/test-sources/src/main/scala/simple_trees/Constants.scala b/test-sources/src/main/scala/simple_trees/Constants.scala
index b662cd8..4933593 100644
--- a/test-sources/src/main/scala/simple_trees/Constants.scala
+++ b/test-sources/src/main/scala/simple_trees/Constants.scala
@@ -9,7 +9,7 @@ class Constants {
   val charVal = 'a'
   val intVal = 1
   val longVal: Long = 1L
-  val floatVal: Float = 1.1f
+  val floatVal: Float = 1.5f
   val doubleVal: Double = 1.1d
   val stringVal = "string"
   val nullVal = null
diff --git a/test-sources/src/main/scala/simple_trees/DefaultParams.scala b/test-sources/src/main/scala/simple_trees/DefaultParams.scala
index 853f83d..75e205d 100644
--- a/test-sources/src/main/scala/simple_trees/DefaultParams.scala
+++ b/test-sources/src/main/scala/simple_trees/DefaultParams.scala
@@ -2,6 +2,4 @@ package simple_trees
 
 class DefaultParams:
   def foo(x: Int, y: Int = 1, z: String = "hello"): String = s"$x $y $z"
-
-  def foo(x: Int, y: String): String = s"$x $y"
 end DefaultParams