diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1afb47457cf5..55aafaa8fc63 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -47,7 +47,7 @@ jobs: test_non_bootstrapped: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -65,8 +65,8 @@ jobs: && github.repository == 'scala/scala3' )" steps: - - name: Set JDK 16 as default - run: echo "/usr/lib/jvm/java-16-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH ## Workaround for https://github.com/actions/runner/issues/2033 (See https://github.com/scala/scala3/pull/19720) - name: Reset existing repo @@ -99,7 +99,7 @@ jobs: test: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -119,8 +119,8 @@ jobs: )" steps: - - name: Set JDK 16 as default - run: echo "/usr/lib/jvm/java-16-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | @@ -229,7 +229,7 @@ jobs: name: MiMa runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -248,6 +248,9 @@ jobs: && github.repository == 'scala/scala3' )" steps: + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -272,7 +275,7 @@ jobs: community_build_a: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -293,6 +296,8 @@ jobs: )" steps: + - name: Set JDK 8 as default + run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -324,7 +329,7 @@ jobs: community_build_b: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -345,6 +350,8 @@ jobs: )" steps: + - name: Set JDK 8 as default + run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -376,7 +383,7 @@ jobs: community_build_c: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -397,6 +404,8 @@ jobs: )" steps: + - name: Set JDK 8 as default + run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -428,7 +437,7 @@ jobs: test_sbt: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -447,6 +456,9 @@ jobs: )" steps: + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -470,7 +482,7 @@ jobs: test_java8: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -526,7 +538,7 @@ jobs: publish_nightly: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -542,6 +554,8 @@ jobs: SONATYPE_USER: ${{ secrets.SONATYPE_USER_ORGSCALALANG }} steps: + - name: Set JDK 8 as default + run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -579,7 +593,7 @@ jobs: nightly_documentation: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -633,7 +647,7 @@ jobs: contents: write # for actions/create-release to create a release runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -651,6 +665,8 @@ jobs: SONATYPE_USER: ${{ secrets.SONATYPE_USER_ORGSCALALANG }} steps: + - name: Set JDK 8 as default + run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory /__w/scala3/scala3 @@ -723,7 +739,7 @@ jobs: open_issue_on_failure: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2021-03-22 + image: lampepfl/dotty:2023-11-07 needs: [nightly_documentation, test_windows_full] # The `failure()` expression is true iff at least one of the dependencies # of this job (including transitive dependencies) has failed. diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 8e11c96ef363..9d668260aa27 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -141,7 +141,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint |""" val enableXprintSuspensionHint = if ctx.settings.XprintSuspension.value then "" - else "\n\nCompiling with -Xprint-suspension gives more information." + else "\n\nCompile with -Xprint-suspension for information." report.error(em"""Cyclic macro dependencies $where |Compilation stopped since no further progress can be made. | diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 920da377f9b4..55fb31fd1916 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -34,7 +34,10 @@ object ContextOps: if (elem.name == name) return elem.sym.denot // return self } val pre = ctx.owner.thisType - if ctx.isJava then javaFindMember(name, pre, required, excluded) + if ctx.isJava then + // Note: I didn't verify if there exists a code path that would require `lookInCompanion = true`, + // it is just to preserve the original behavior. + javaFindMember(name, pre, lookInCompanion = true, required, excluded) else pre.findMember(name, pre, required, excluded) } else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext. @@ -43,7 +46,13 @@ object ContextOps: ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix) } - final def javaFindMember(name: Name, pre: Type, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation = + /** Look in the prefix with Java semantics. + * @param lookInCompanion If true, try in the companion class of a module as a fallback. + * Note: originally this was used to type Select nodes in Java code, + * but that is no longer the case. + * It is preserved in case it is necessary for denotNamed, but this is unverified. + */ + final def javaFindMember(name: Name, pre: Type, lookInCompanion: Boolean, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation = assert(ctx.isJava) inContext(ctx) { @@ -53,7 +62,7 @@ object ContextOps: val directSearch = pre.findMember(name, pre, required, excluded) // 2. Try to search in companion class if current is an object. - def searchCompanionClass = if preSym.is(Flags.Module) then + def searchCompanionClass = if lookInCompanion && preSym.is(Flags.Module) then preSym.companionClass.thisType.findMember(name, pre, required, excluded) else NoDenotation diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a8a1be309b0c..d18f88dabfd8 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1501,6 +1501,13 @@ object SymDenotations { def namedType(using Context): NamedType = if (isType) typeRef else termRef + /** Like typeRef, but the prefix is widened. + * + * See tests/neg/i19619/Test.scala + */ + def javaTypeRef(using Context) = + TypeRef(maybeOwner.reachablePrefix.widen, symbol) + /** Like typeRef, but objects in the prefix are represented by their singleton type, * this means we output `pre.O.member` rather than `pre.O$.this.member`. * diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index eb60d76c77c4..ad2b3bfe527d 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -105,17 +105,28 @@ object Symbols extends SymUtils { } private def computeDenot(lastd: SymDenotation)(using Context): SymDenotation = { + // Written that way so that it comes in at 32 bytes and is therefore inlineable for + // the JIT (reputedly, cutoff is at 35 bytes) util.Stats.record("Symbol.computeDenot") val now = ctx.period checkedPeriod = now - if (lastd.validFor contains now) lastd else recomputeDenot(lastd) + if lastd.validFor.contains(now) then lastd else recomputeDenot(lastd) } /** Overridden in NoSymbol */ protected def recomputeDenot(lastd: SymDenotation)(using Context): SymDenotation = { util.Stats.record("Symbol.recomputeDenot") val newd = lastd.current.asInstanceOf[SymDenotation] - lastDenot = newd + if newd.exists || lastd.initial.validFor.firstPhaseId <= ctx.phaseId then + lastDenot = newd + else + // We are trying to bring forward a symbol that is defined only at a later phase + // (typically, a nested Java class, invisible before erasure). + // In that case, keep lastDenot as it was and set the checked period to lastDenot's + // previous validity, which means we will try another bring forward when the symbol + // is referenced at a later phase. Otherwise we'd get stuck on NoDenotation here. + // See #15562 and test i15562b in ReplCompilerTests + checkedPeriod = lastd.validFor newd } @@ -764,7 +775,7 @@ object Symbols extends SymUtils { cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType)(using Context): TermSymbol = - newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord) + newSymbol(cls, name, SelfSymFlags, selfInfo.orElse(cls.classInfo.selfType), coord = cls.coord) /** Create new type parameters with given owner, names, and flags. * @param boundsFn A function that, given type refs to the newly created @@ -931,7 +942,7 @@ object Symbols extends SymUtils { */ def getPackageClassIfDefined(path: PreName)(using Context): Symbol = staticRef(path.toTypeName, isPackage = true, generateStubs = false) - .disambiguate(_ is PackageClass).symbol + .disambiguate(_.is(PackageClass)).symbol def requiredModule(path: PreName)(using Context): TermSymbol = { val name = path.toTermName diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 98906a5703d2..9cdb0750bb94 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3958,7 +3958,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Given) newLineOpt() val vparamss = - if in.token == LPAREN && in.lookahead.isIdent(nme.using) + if in.token == LPAREN && (in.lookahead.isIdent(nme.using) || name != EmptyTermName) then termParamClauses(ParamOwner.Given) else Nil newLinesOpt() diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 5763e671e75a..6e37acfaac84 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -372,7 +372,7 @@ object MarkupParsers { while { xSpaceOpt() nextch() - ts.append(element) + content_LT(ts) charComingAfter(xSpaceOpt()) == '<' } do () handle.makeXMLseq(Span(start, curOffset, start), ts) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index c435ee0d7f5c..ee0557ea9773 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -286,7 +286,7 @@ extends NotFoundMsg(MissingIdentID) { } } -class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) +class TypeMismatch(val found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): def msg(using Context) = @@ -2749,7 +2749,11 @@ extends SyntaxMsg(TargetNameOnTopLevelClassID): class NotClassType(tp: Type)(using Context) extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): def msg(using Context) = i"$tp is not a class type" - def explain(using Context) = "" + def explain(using Context) = + i"""A class type includes classes and traits in a specific order. Defining a class, even an anonymous class, + |requires specifying a linearization order for the traits it extends. For example, `A & B` is not a class type + |because it doesn't specify which trait takes precedence, A or B. For more information about class types, please see the Scala Language Specification. + |Class types also can't have refinements.""" class NotConstant(suffix: String, tp: Type)(using Context) extends TypeMsg(NotConstantID), ShowMatchTrace(tp): diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 01c2417b98fe..6dde9e10c833 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -112,7 +112,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase * This info is used in phase ParamForwarding */ private def forwardParamAccessors(impl: Template)(using Context): Unit = impl.parents match - case superCall @ Apply(fn, superArgs) :: _ if superArgs.nonEmpty => + case superCall @ Apply(fn, superArgs) :: _ + if superArgs.nonEmpty && fn.symbol.isPrimaryConstructor => fn.tpe.widen match case MethodType(superParamNames) => for case stat: ValDef <- impl.body do diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 99074313e8bb..e16645cb4190 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -620,7 +620,7 @@ trait Applications extends Compatibility { defaultArg.tpe.widen match case _: MethodOrPoly if testOnly => matchArgs(args1, formals1, n + 1) case _ => matchArgs(args1, addTyped(treeToArg(defaultArg)), n + 1) - else if methodType.isContextualMethod && ctx.mode.is(Mode.ImplicitsEnabled) then + else if methodType.isImplicitMethod && ctx.mode.is(Mode.ImplicitsEnabled) then matchArgs(args1, addTyped(treeToArg(implicitArg)), n + 1) else missingArg(n) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c043492164b5..cfda041745e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1275,35 +1275,36 @@ trait Implicits: case alt1: SearchSuccess => var diff = compareAlternatives(alt1, alt2) assert(diff <= 0) // diff > 0 candidates should already have been eliminated in `rank` - if diff == 0 && alt1.isExtension && alt2.isExtension then - // Fall back: if both results are extension method applications, - // compare the extension methods instead of their wrappers. - def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe - (stripExtension(alt1), stripExtension(alt2)) match - case (ref1: TermRef, ref2: TermRef) => - // ref1 and ref2 might refer to type variables owned by - // alt1.tstate and alt2.tstate respectively, to compare the - // alternatives correctly we need a TyperState that includes - // constraints from both sides, see - // tests/*/extension-specificity2.scala for test cases. - val constraintsIn1 = alt1.tstate.constraint ne ctx.typerState.constraint - val constraintsIn2 = alt2.tstate.constraint ne ctx.typerState.constraint - def exploreState(alt: SearchSuccess): TyperState = - alt.tstate.fresh(committable = false) - val comparisonState = - if constraintsIn1 && constraintsIn2 then - exploreState(alt1).mergeConstraintWith(alt2.tstate) - else if constraintsIn1 then - exploreState(alt1) - else if constraintsIn2 then - exploreState(alt2) - else - ctx.typerState - - diff = inContext(ctx.withTyperState(comparisonState)) { - compare(ref1, ref2) - } - case _ => + if diff == 0 && alt2.isExtension then + if alt1.isExtension then + // Fall back: if both results are extension method applications, + // compare the extension methods instead of their wrappers. + def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe + (stripExtension(alt1), stripExtension(alt2)) match + case (ref1: TermRef, ref2: TermRef) => + // ref1 and ref2 might refer to type variables owned by + // alt1.tstate and alt2.tstate respectively, to compare the + // alternatives correctly we need a TyperState that includes + // constraints from both sides, see + // tests/*/extension-specificity2.scala for test cases. + val constraintsIn1 = alt1.tstate.constraint ne ctx.typerState.constraint + val constraintsIn2 = alt2.tstate.constraint ne ctx.typerState.constraint + def exploreState(alt: SearchSuccess): TyperState = + alt.tstate.fresh(committable = false) + val comparisonState = + if constraintsIn1 && constraintsIn2 then + exploreState(alt1).mergeConstraintWith(alt2.tstate) + else if constraintsIn1 then + exploreState(alt1) + else if constraintsIn2 then + exploreState(alt2) + else + ctx.typerState + + diff = inContext(ctx.withTyperState(comparisonState)): + compare(ref1, ref2) + else // alt1 is a conversion, prefer extension alt2 over it + diff = -1 if diff < 0 then alt2 else if diff > 0 then alt1 else SearchFailure(new AmbiguousImplicits(alt1, alt2, pt, argument), span) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9e4768922ad3..e9b00cf95cac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1539,17 +1539,19 @@ class Namer { typer: Typer => end parentType /** Check parent type tree `parent` for the following well-formedness conditions: - * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (1) It must be a class type with a stable prefix (unless `isJava`) (@see checkClassTypeWithStablePrefix) * (2) If may not derive from itself * (3) The class is not final * (4) If the class is sealed, it is defined in the same compilation unit as the current class + * + * @param isJava If true, the parent type is in Java mode, and we do not require a stable prefix */ - def checkedParentType(parent: untpd.Tree): Type = { + def checkedParentType(parent: untpd.Tree, isJava: Boolean): Type = { val ptype = parentType(parent)(using completerCtx.superCallContext).dealiasKeepAnnots if (cls.isRefinementClass) ptype else { val pt = checkClassType(ptype, parent.srcPos, - traitReq = parent ne parents.head, stablePrefixReq = true) + traitReq = parent ne parents.head, stablePrefixReq = !isJava) if (pt.derivesFrom(cls)) { val addendum = parent match { case Select(qual: Super, _) if Feature.migrateTo3 => @@ -1618,7 +1620,9 @@ class Namer { typer: Typer => val parentTypes = defn.adjustForTuple(cls, cls.typeParams, defn.adjustForBoxedUnit(cls, addUsingTraits( - ensureFirstIsClass(cls, parents.map(checkedParentType(_))) + locally: + val isJava = ctx.isJava + ensureFirstIsClass(cls, parents.map(checkedParentType(_, isJava))) ) ) ) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 0896d78c7229..02d8c2f51bf4 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -150,7 +150,10 @@ trait TypeAssigner { val pre = maybeSkolemizePrefix(qualType, name) val mbr = if ctx.isJava then - ctx.javaFindMember(name, pre) + // don't look in the companion class here if qual is a module, + // we use backtracking to instead change the qual to the companion class + // if this fails. + ctx.javaFindMember(name, pre, lookInCompanion = false) else qualType.findMember(name, pre) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 55adda0505ae..43e5d52d397a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -453,6 +453,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType else if (ctx.isJava && defDenot.symbol.isStatic) { defDenot.symbol.namedType + } + else if (ctx.isJava && defDenot.symbol.isClass) { + // in a java context a raw identifier to a class should have a widened prefix. + defDenot.symbol.javaTypeRef } else { val effectiveOwner = if (curOwner.isTerm && defDenot.symbol.maybeOwner.isType) @@ -4210,14 +4214,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val tree1 = if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree else { - val tp1 = - if (ctx.isJava) - // Cook raw type - AppliedType(tree.tpe, tp.typeParams.map(Function.const(TypeBounds.empty))) - else - // Eta-expand higher-kinded type - tree.tpe.EtaExpand(tp.typeParamSymbols) - tree.withType(tp1) + if (ctx.isJava) + // Cook raw type + val typeArgs = tp.typeParams.map(Function.const(TypeBounds.empty)) + val tree1 = AppliedTypeTree(tree, typeArgs.map(TypeTree(_))) + val tp1 = AppliedType(tree.tpe, typeArgs) + tree1.withType(tp1) + else + // Eta-expand higher-kinded type + val tp1 = tree.tpe.EtaExpand(tp.typeParamSymbols) + tree.withType(tp1) } if (ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) || tree1.tpe <:< pt) tree1 else err.typeMismatch(tree1, pt) diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 9131f4f761a2..0bd407261125 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -221,7 +221,7 @@ object Signatures { val funSymbol = fun.symbol val alternatives = if funSymbol.isLocalToBlock then List(funSymbol.denot) else funSymbol.owner.info.member(funSymbol.name).alternatives - val alternativeIndex = alternatives.map(_.symbol).indexOf(funSymbol) max 0 + val alternativeIndex = bestAlternative(alternatives, params, paramssListIndex) (alternativeIndex, alternatives) if alternativeIndex < alternatives.length then @@ -660,24 +660,56 @@ object Signatures { case msg: NoMatchingOverload => msg.alternatives case _ => Nil - val userParamsTypes = params.map(_.tpe) // Assign a score to each alternative (how many parameters are correct so far), and // use that to determine what is the current active signature. + val alternativeIndex = bestAlternative(alternatives, params, paramssIndex) + (alternativeIndex, alternatives) + } + + /** + * Given a list of alternatives, and a list of parameters, returns the index of the best + * alternative, i.e. the alternative that has the most formal parameters matching the given + * arguments and the least number of formal parameters. + * + * @param alternatives The list of alternatives to inspect. + * @param params The parameters that were given at the call site. + * @param paramssIndex Index of paramss we are currently in. + * + * @return The index of the best alternative. + */ + private def bestAlternative(alternatives: List[SingleDenotation], params: List[tpd.Tree], paramssIndex: Int)(using Context): Int = + val userParamsTypes = params.map( + _.tpe match + case e: PreviousErrorType => + /** + * In case: + * def foo(i: Int, s: String): Unit = ??? + * def foo(i: Boolean, s: Int, x: Double): Unit = ??? + * foo(false, @@) + * + * `false` has error type: `Required: Int, Found: Boolean` + */ + e.msg match + case tm: TypeMismatch => + tm.found + case _ => e + case t => t + ) val alternativesScores = alternatives.map { alt => val alreadyCurriedBonus = if (alt.symbol.paramSymss.length > paramssIndex) 1 else 0 - alt.info.stripPoly match - case tpe: MethodType => alreadyCurriedBonus + - userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size - case _ => 0 + alt.info.stripPoly match + case tpe: MethodType => + val score = alreadyCurriedBonus + + userParamsTypes + .zip(tpe.paramInfos) + .takeWhile { case (t0, t1) =>t0 <:< t1 } + .size + (score, -tpe.paramInfos.length) + case _ => (0, 0) } - - val bestAlternative = - if (alternativesScores.isEmpty) 0 - else alternativesScores.zipWithIndex.maxBy(_._1)._2 - - (bestAlternative, alternatives) - } + if (alternativesScores.isEmpty) 0 + else alternativesScores.zipWithIndex.maxBy(_._1)._2 } diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 8f9a9bd69a50..b8dfa833c437 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -76,7 +76,7 @@ class CoursierScalaTests: def emptyArgsEqualsRepl() = val output = CoursierScalaTests.csScalaCmd() - assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + assertTrue(output.mkString("\n").contains("Unable to create a terminal")) // Scala attempted to create REPL so we can assume it is working emptyArgsEqualsRepl() def run() = @@ -133,7 +133,7 @@ class CoursierScalaTests: def replWithArgs() = val output = CoursierScalaTests.csScalaCmd("-source", "3.0-migration") - assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + assertTrue(output.mkString("\n").contains("Unable to create a terminal")) // Scala attempted to create REPL so we can assume it is working replWithArgs() def argumentFile() = diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index ecdfeb512e1b..63da0e277bb4 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -353,6 +353,34 @@ class ReplCompilerTests extends ReplTest: @Test def `i13097 expect template after colon` = contextually: assert(ParseResult.isIncomplete("class C:")) + @Test def i15562: Unit = initially { + val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)") + assertEquals("2", storedOutput().trim) + s1 + } andThen { s1 ?=> + val comp = tabComplete("List(1, 2).filter(_ % 2 == 0).fore") + assertEquals(List("foreach"), comp.distinct) + s1 + } andThen { + val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)") + assertEquals("2", storedOutput().trim) + s2 + } + + @Test def i15562b: Unit = initially { + val s1 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)") + assertEquals("2", storedOutput().trim) + s1 + } andThen { s1 ?=> + val comp = tabComplete("val x = false + true; List(1, 2).filter(_ % 2 == 0).fore") + assertEquals(List("foreach"), comp.distinct) + s1 + } andThen { + val s2 = run("List(1, 2).filter(_ % 2 == 0).foreach(println)") + assertEquals("2", storedOutput().trim) + s2 + } + object ReplCompilerTests: private val pattern = Pattern.compile("\\r[\\n]?|\\n"); diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index 8fbf635c9a17..5f185e9c9e29 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -40,6 +40,10 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na def contextually[A](op: Context ?=> A): A = op(using initialState.context) + /** Returns the `(, )`*/ + def tabComplete(src: String)(implicit state: State): List[String] = + completions(src.length, src, state).map(_.value).sorted + extension [A](state: State) infix def andThen(op: State ?=> A): A = op(using state) diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 0bce525e1469..e4c3a2557e7d 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -8,10 +8,6 @@ import org.junit.Test /** These tests test input that has proved problematic */ class TabcompleteTests extends ReplTest { - /** Returns the `(, )`*/ - private def tabComplete(src: String)(implicit state: State): List[String] = - completions(src.length, src, state).map(_.value).sorted - @Test def tabCompleteList = initially { val comp = tabComplete("List.r") assertEquals(List("range"), comp.distinct) diff --git a/docs/_docs/contributing/testing.md b/docs/_docs/contributing/testing.md index 3c65c1591d40..bc0304616720 100644 --- a/docs/_docs/contributing/testing.md +++ b/docs/_docs/contributing/testing.md @@ -141,6 +141,11 @@ checkfiles with the test outputs. $ sbt > testCompilation --update-checkfiles ``` +Or for ScalaJS +```bash +$ sbt +> sjsCompilerTests/testOnly -- -Ddotty.tests.updateCheckfiles=TRUE +``` Use `--help` to see all the options ```bash diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index df8626b5119c..40c136085528 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -68,11 +68,7 @@ abstract class Any { def isInstanceOf[a]: Boolean /** Type cast; needs to be inlined to work as given */ */ - def asInstanceOf[A]: A = this match { - case x: A => x - case _ => if (this eq null) this - else throw new ClassCastException() - } + def asInstanceOf[A]: A } /** The root class of all value types */ diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala similarity index 63% rename from presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala rename to presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index d810ce5b07cc..3a17d30bc024 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -6,18 +6,18 @@ import java.nio.file.Paths import scala.meta.internal.metals.ReportContext import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.pc.printer.ShortenedTypePrinter +import scala.meta.internal.pc.InlayHints +import scala.meta.internal.pc.LabelPart +import scala.meta.internal.pc.LabelPart.* +import scala.meta.pc.InlayHintsParams import scala.meta.pc.SymbolSearch -import scala.meta.pc.SyntheticDecoration -import scala.meta.pc.SyntheticDecorationsParams -import scala.meta.internal.pc.DecorationKind -import scala.meta.internal.pc.Decoration - import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver @@ -26,9 +26,13 @@ import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span import dotty.tools.pc.IndexedContext -final class PcSyntheticDecorationsProvider( +import org.eclipse.lsp4j.InlayHint +import org.eclipse.lsp4j.InlayHintKind +import org.eclipse.{lsp4j as l} + +class PcInlayHintsProvider( driver: InteractiveDriver, - params: SyntheticDecorationsParams, + params: InlayHintsParams, symbolSearch: SymbolSearch, )(using ReportContext): @@ -43,75 +47,81 @@ final class PcSyntheticDecorationsProvider( given InferredType.Text = InferredType.Text(text) given ctx: Context = driver.currentCtx val unit = driver.currentCtx.run.nn.units.head + val pos = driver.sourcePosition(params) - def tpdTree = unit.tpdTree + def provide(): List[InlayHint] = + val deepFolder = DeepFolder[InlayHints](collectDecorations) + Interactive + .pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) + .headOption + .getOrElse(unit.tpdTree) + .enclosedChildren(pos.span) + .flatMap(tpdTree => deepFolder(InlayHints.empty, tpdTree).result()) - def provide(): List[SyntheticDecoration] = - val deepFolder = DeepFolder[Synthetics](collectDecorations) - deepFolder(Synthetics.empty, tpdTree).result() + private def adjustPos(pos: SourcePosition): SourcePosition = + pos.adjust(text)._1 def collectDecorations( - decorations: Synthetics, + inlayHints: InlayHints, tree: Tree, - ): Synthetics = + ): InlayHints = tree match - case ImplicitConversion(name, range) if params.implicitConversions() => - val adjusted = range.adjust(text)._1 - decorations + case ImplicitConversion(symbol, range) if params.implicitConversions() => + val adjusted = adjustPos(range) + inlayHints .add( - Decoration( - adjusted.startPos.toLsp, - name + "(", - DecorationKind.ImplicitConversion, - ) + adjusted.startPos.toLsp, + labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil, + InlayHintKind.Parameter, ) .add( - Decoration( - adjusted.endPos.toLsp, - ")", - DecorationKind.ImplicitConversion, - ) + adjusted.endPos.toLsp, + LabelPart(")") :: Nil, + InlayHintKind.Parameter, ) - case ImplicitParameters(names, pos, allImplicit) + case ImplicitParameters(symbols, pos, allImplicit) if params.implicitParameters() => + val labelParts = symbols.map(s => List(labelPart(s, s.decodedName))) val label = - if allImplicit then names.mkString("(", ", ", ")") - else names.mkString(", ", ", ", "") - decorations.add( - Decoration( - pos.adjust(text)._1.toLsp, - label, - DecorationKind.ImplicitParameter, - ) + if allImplicit then labelParts.separated("(", ", ", ")") + else labelParts.separated(", ") + inlayHints.add( + adjustPos(pos).toLsp, + label, + InlayHintKind.Parameter, + ) + case ValueOf(label, pos) if params.implicitParameters() => + inlayHints.add( + adjustPos(pos).toLsp, + LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), + InlayHintKind.Parameter, ) case TypeParameters(tpes, pos, sel) if params.typeParameters() && !syntheticTupleApply(sel) => - val label = tpes.map(toLabel(_, pos)).mkString("[", ", ", "]") - decorations.add( - Decoration( - pos.adjust(text)._1.endPos.toLsp, - label, - DecorationKind.TypeParameter, - ) + val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]") + inlayHints.add( + adjustPos(pos).endPos.toLsp, + label, + InlayHintKind.Type, ) - case InferredType(tpe, pos, defTree) if params.inferredTypes() => - val adjustedPos = pos.adjust(text)._1.endPos - if decorations.containsDef(adjustedPos.start) then decorations + case InferredType(tpe, pos, defTree) + if params.inferredTypes() && !isErrorTpe(tpe) => + val adjustedPos = adjustPos(pos).endPos + if inlayHints.containsDef(adjustedPos.start) then inlayHints else - decorations.add( - Decoration( + inlayHints + .add( adjustedPos.toLsp, - ": " + toLabel(tpe, pos), - DecorationKind.InferredType, - ), - adjustedPos.start, - ) - case _ => decorations + LabelPart(": ") :: toLabelParts(tpe, pos), + InlayHintKind.Type, + ) + .addDefinition(adjustedPos.start) + case _ => inlayHints - private def toLabel( + private def toLabelParts( tpe: Type, pos: SourcePosition, - ): String = + ): List[LabelPart] = val tpdPath = Interactive.pathTo(unit.tpdTree, pos.span) @@ -129,13 +139,15 @@ final class PcSyntheticDecorationsProvider( case AppliedType(tycon, args) => isInScope(tycon) && args.forall(isInScope) case _ => true - if isInScope(tpe) - then tpe + if isInScope(tpe) then tpe else tpe.metalsDealias(using indexedCtx.ctx) val dealiased = optDealias(tpe) - printer.tpe(dealiased) - end toLabel + val tpeStr = printer.tpe(dealiased) + val usedRenames = printer.getUsedRenames + val parts = partsFromType(dealiased, usedRenames) + InlayHints.makeLabelParts(parts, tpeStr) + end toLabelParts private val definitions = IndexedContext(ctx).ctx.definitions private def syntheticTupleApply(tree: Tree): Boolean = @@ -152,7 +164,32 @@ final class PcSyntheticDecorationsProvider( case _ => true else false case _ => false -end PcSyntheticDecorationsProvider + + private def labelPart(symbol: Symbol, label: String) = + if symbol.source == pos.source then + LabelPart( + label, + pos = Some(symbol.sourcePos.toLsp.getStart().nn), + ) + else + LabelPart( + label, + symbol = SemanticdbSymbols.symbolName(symbol), + ) + + private def partsFromType( + tpe: Type, + usedRenames: Map[Symbol, String], + ): List[LabelPart] = + NamedPartsAccumulator(_ => true)(Nil, tpe) + .filter(_.symbol != NoSymbol) + .map { t => + val label = usedRenames.get(t.symbol).getOrElse(t.symbol.decodedName) + labelPart(t.symbol, label) + } + + private def isErrorTpe(tpe: Type): Boolean = tpe.isError +end PcInlayHintsProvider object ImplicitConversion: def unapply(tree: Tree)(using Context) = @@ -170,7 +207,7 @@ object ImplicitConversion: val lastArgPos = args.lastOption.map(_.sourcePos).getOrElse(fun.sourcePos) Some( - fun.symbol.decodedName, + fun.symbol, lastArgPos.withStart(fun.sourcePos.start), ) end ImplicitConversion @@ -183,23 +220,29 @@ object ImplicitParameters: val (implicitArgs, providedArgs) = args.partition(isSyntheticArg) val allImplicit = providedArgs.isEmpty val pos = implicitArgs.head.sourcePos - Some(implicitArgs.map(_.symbol.decodedName), pos, allImplicit) + Some(implicitArgs.map(_.symbol), pos, allImplicit) + case _ => None + + private def isSyntheticArg(tree: Tree)(using Context) = tree match + case tree: Ident => + tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) + case _ => false +end ImplicitParameters + +object ValueOf: + def unapply(tree: Tree)(using Context) = + tree match case Apply(ta @ TypeApply(fun, _), _) if fun.span.isSynthetic && isValueOf(fun) => Some( - List("new " + tpnme.valueOf.decoded.capitalize + "(...)"), + "new " + tpnme.valueOf.decoded.capitalize + "(...)", fun.sourcePos, - true, ) case _ => None private def isValueOf(tree: Tree)(using Context) = val symbol = tree.symbol.maybeOwner symbol.name.decoded == tpnme.valueOf.decoded.capitalize - private def isSyntheticArg(tree: Tree)(using Context) = tree match - case tree: Ident => - tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) - case _ => false -end ImplicitParameters +end ValueOf object TypeParameters: def unapply(tree: Tree)(using Context) = @@ -259,35 +302,7 @@ object InferredType: */ def isValDefBind(text: Text, vd: ValDef)(using Context) = val afterDef = text.drop(vd.nameSpan.end) - val index = afterDef.indexAfterSpacesAndComments + val index = indexAfterSpacesAndComments(afterDef) index >= 0 && index < afterDef.size && afterDef(index) == '@' end InferredType - -case class Synthetics( - decorations: List[Decoration], - definitions: Set[Int], -): - def containsDef(offset: Int) = definitions(offset) - def add(decoration: Decoration, offset: Int) = - copy( - decorations = addDecoration(decoration), - definitions = definitions + offset, - ) - def add(decoration: Decoration) = - copy ( - decorations = addDecoration(decoration) - ) - - // If method has both type parameter and implicit parameter, we want the type parameter decoration to be displayed first, - // but it's added second. This method adds the decoration to the right position in the list. - private def addDecoration(decoration: Decoration): List[Decoration] = - val atSamePos = - decorations.takeWhile(_.range.getStart() == decoration.range.getStart()) - (atSamePos :+ decoration) ++ decorations.drop(atSamePos.size) - - def result(): List[Decoration] = decorations.reverse -end Synthetics - -object Synthetics: - def empty: Synthetics = Synthetics(Nil, Set.empty) diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 87b67015f9e4..f3dc8d1658c4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -106,15 +106,15 @@ case class ScalaPresentationCompiler( new PcSemanticTokensProvider(driver, params).provide().asJava } - override def syntheticDecorations( - params: SyntheticDecorationsParams - ): ju.concurrent.CompletableFuture[ju.List[SyntheticDecoration]] = + override def inlayHints( + params: InlayHintsParams + ): ju.concurrent.CompletableFuture[ju.List[l.InlayHint]] = compilerAccess.withInterruptableCompiler(Some(params))( - new ju.ArrayList[SyntheticDecoration](), + new ju.ArrayList[l.InlayHint](), params.token(), ) { access => val driver = access.compiler() - new PcSyntheticDecorationsProvider(driver, params, search) + new PcInlayHintsProvider(driver, params, search) .provide() .asJava } diff --git a/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala index a7d07b12f40c..9dce37028bea 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala @@ -6,10 +6,12 @@ import java.util as ju import scala.jdk.CollectionConverters._ import scala.meta.pc.OffsetParams +import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.util.SourcePosition import dotty.tools.pc.utils.MtagsEnrichments.* import org.eclipse.lsp4j @@ -46,11 +48,7 @@ class SelectionRangeProvider( Interactive.pathTo(driver.openedTrees(uri), pos)(using ctx) val bareRanges = path - .map { tree => - val selectionRange = new SelectionRange() - selectionRange.setRange(tree.sourcePos.toLsp) - selectionRange - } + .flatMap(selectionRangesFromTree(pos)) val comments = driver.compilationUnits.get(uri).map(_.comments).toList.flatten @@ -79,6 +77,33 @@ class SelectionRangeProvider( } end selectionRange + /** Given a tree, create a seq of [[SelectionRange]]s corresponding to that tree. */ + private def selectionRangesFromTree(pos: SourcePosition)(tree: tpd.Tree)(using Context) = + def toSelectionRange(srcPos: SourcePosition) = + val selectionRange = new SelectionRange() + selectionRange.setRange(srcPos.toLsp) + selectionRange + + val treeSelectionRange = toSelectionRange(tree.sourcePos) + + tree match + case tpd.DefDef(name, paramss, tpt, rhs) => + // If source position is within a parameter list, add a selection range covering that whole list. + val selectedParams = + paramss + .iterator + .flatMap: // parameter list to a sourcePosition covering the whole list + case Seq(param) => Some(param.sourcePos) + case params @ Seq(head, tail*) => + val srcPos = head.sourcePos + val lastSpan = tail.last.span + Some(SourcePosition(srcPos.source, srcPos.span union lastSpan, srcPos.outer)) + case Seq() => None + .find(_.contains(pos)) + .map(toSelectionRange) + selectedParams ++ Seq(treeSelectionRange) + case _ => Seq(treeSelectionRange) + private def setParent( child: SelectionRange, parent: SelectionRange diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala index 3b763523f9e6..ed37e133372b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala @@ -66,6 +66,8 @@ class ShortenedTypePrinter( private val foundRenames = collection.mutable.LinkedHashMap.empty[Symbol, String] + def getUsedRenames: Map[Symbol, String] = foundRenames.toMap + def getUsedRenamesInfo(using Context): List[String] = foundRenames.map { (from, to) => s"type $to = ${from.showName}" diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala index d6c8c10f8030..e4385392973f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala @@ -316,6 +316,24 @@ object MtagsEnrichments extends CommonMtagsEnrichments: .map(source.apply) .contains('.') case _ => false + + def children(using Context): List[Tree] = + val collector = new TreeAccumulator[List[Tree]]: + def apply(x: List[Tree], tree: Tree)(using Context): List[Tree] = + tree :: x + collector + .foldOver(Nil, tree) + .reverse + + /** + * Returns the children of the tree that overlap with the given span. + */ + def enclosedChildren(span: Span)(using Context): List[Tree] = + tree.children + .filter(tree => + tree.sourcePos.exists && tree.span.start <= span.end && tree.span.end >= span.start + ) + end enclosedChildren end extension extension (imp: Import) diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala new file mode 100644 index 000000000000..94b00ca82aea --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala @@ -0,0 +1,56 @@ +package dotty.tools.pc.base + +import java.net.URI + +import scala.meta.internal.jdk.CollectionConverters._ +import scala.meta.internal.metals.CompilerInlayHintsParams +import scala.meta.internal.metals.CompilerRangeParams +import scala.language.unsafeNulls + +import dotty.tools.pc.utils.TestInlayHints +import dotty.tools.pc.utils.TextEdits + +import org.eclipse.lsp4j.TextEdit + +class BaseInlayHintsSuite extends BasePCSuite { + + def check( + base: String, + expected: String, + kind: Option[Int] = None, + ): Unit = + def pkgWrap(text: String) = + if (text.contains("package")) text + else s"package test\n$text" + + val withPkg = pkgWrap(base) + val rangeParams = CompilerRangeParams( + URI.create("file:/InlayHints.scala"), + withPkg, + 0, + withPkg.length() + ) + val pcParams = CompilerInlayHintsParams( + rangeParams, + true, + true, + true, + true + ) + + val inlayHints = presentationCompiler + .inlayHints( + pcParams + ) + .get() + .asScala + .toList + + val obtained = TestInlayHints.applyInlayHints(withPkg, inlayHints) + + assertNoDiff( + obtained, + pkgWrap(expected) + ) + +} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala deleted file mode 100644 index 450a42f26153..000000000000 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala +++ /dev/null @@ -1,60 +0,0 @@ -package dotty.tools.pc.base - -import java.net.URI - -import scala.meta.internal.jdk.CollectionConverters._ -import scala.meta.internal.metals.CompilerSyntheticDecorationsParams -import scala.meta.internal.metals.CompilerVirtualFileParams -import scala.language.unsafeNulls - -import dotty.tools.pc.utils.TextEdits - -import org.eclipse.lsp4j.TextEdit - -class BaseSyntheticDecorationsSuite extends BasePCSuite { - - def check( - base: String, - expected: String, - kind: Option[Int] = None, - ): Unit = - def pkgWrap(text: String) = - if (text.contains("package")) text - else s"package test\n$text" - - val withPkg = pkgWrap(base) - val vFile = CompilerVirtualFileParams( - URI.create("file:/Decorations.scala"), - withPkg, - ) - - val pcParams = CompilerSyntheticDecorationsParams( - vFile, - true, - true, - true, - true, - ) - - val allDecorations = presentationCompiler - .syntheticDecorations( - pcParams - ) - .get() - .asScala - .toList - - val decorations = kind match { - case Some(k) => allDecorations.filter(_.kind == k) - case _ => allDecorations - } - - val edits = decorations.map(d => new TextEdit(d.range(), d.label())) - val obtained = TextEdits.applyEdits(withPkg, edits) - - assertNoDiff( - obtained, - pkgWrap(expected), - ) - -} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/tests/SelectionRangeSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/SelectionRangeSuite.scala index 84417bb5e414..e277a67c466b 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/SelectionRangeSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/SelectionRangeSuite.scala @@ -101,3 +101,49 @@ class SelectionRangeSuite extends BaseSelectionRangeSuite: |}<>region>>a: Int<>region>>a: Int, b: Int<>region>>def func(a: Int, b: Int) = + | a + b< + | a + b + |}""".stripMargin, + List[String]( + """|object Main extends App { + | val func = (>>region>>a: Int< + | a + b + |}""".stripMargin, + """|object Main extends App { + | val func = (>>region>>a: Int, b: Int< + | a + b + |}""".stripMargin, + """|object Main extends App { + | val func = >>region>>(a: Int, b: Int) => + | a + b<>region>>val func = (a: Int, b: Int) => + | a + b< 123 - |} - |""".stripMargin, - """|object Main { - | val foo: () => Int = () => 123 - |} - |""".stripMargin - ) - - @Test def `block` = - check( - """|object Main { - | val foo = { val z = 123; z + 2} - |} - |""".stripMargin, - """|object Main { - | val foo: Int = { val z: Int = 123; z + 2} - |} - |""".stripMargin - ) - - @Test def `refined-types` = - check( - """|object O{ - | trait Foo { - | type T - | type G - | } - | - | val c = new Foo { type T = Int; type G = Long} - |} - |""".stripMargin, - """|object O{ - | trait Foo { - | type T - | type G - | } - | - | val c: Foo{type T = Int; type G = Long} = new Foo { type T = Int; type G = Long} - |} - |""".stripMargin - ) - - @Test def `refined-types1` = - check( - """|object O{ - | trait Foo { - | type T - | } - | val c = new Foo { type T = Int } - | val d = c - |} - |""".stripMargin, - """|object O{ - | trait Foo { - | type T - | } - | val c: Foo{type T = Int} = new Foo { type T = Int } - | val d: Foo{type T = Int} = c - |} - |""".stripMargin - ) - - @Test def `refined-types4` = - check( - """|trait Foo extends Selectable { - | type T - |} - | - |val c = new Foo { - | type T = Int - | val x = 0 - | def y = 0 - | var z = 0 - |} - |""".stripMargin, - """|trait Foo extends Selectable { - | type T - |} - | - |val c: Foo{type T = Int; val x: Int; def y: Int; val z: Int; def z_=(x$1: Int): Unit} = new Foo { - | type T = Int - | val x: Int = 0 - | def y: Int = 0 - | var z: Int = 0 - |} - |""".stripMargin - ) - - @Test def `dealias` = - check( - """|class Foo() { - | type T = Int - | def getT: T = 1 - |} - | - |object O { - | val c = new Foo().getT - |} - |""".stripMargin, - """|class Foo() { - | type T = Int - | def getT: T = 1 - |} - | - |object O { - | val c: Int = new Foo().getT - |} - |""".stripMargin - ) - - @Test def `dealias2` = - check( - """|object Foo { - | type T = Int - | def getT: T = 1 - | val c = getT - |} - |""".stripMargin, - """|object Foo { - | type T = Int - | def getT: T = 1 - | val c: T = getT - |} - |""".stripMargin - ) - - @Test def `dealias3` = - check( - """|object Foo: - | opaque type T = Int - | def getT: T = 1 - |val c = Foo.getT - |""".stripMargin, - """|object Foo: - | opaque type T = Int - | def getT: T = 1 - |val c: T = Foo.getT - |""".stripMargin - ) - - @Test def `dealias4` = - check( - """|object O: - | type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m = O.get - |""".stripMargin, - """|object O: - | type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m: Int => Int = O.get - |""".stripMargin - ) - - @Test def `dealias5` = - check( - """|object O: - | opaque type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m = O.get - |""".stripMargin, - """|object O: - | opaque type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m: M => Int = O.get - |""".stripMargin - ) - - @Test def `explicit-tuple` = - check( - """|object Main { - | val x = Tuple2.apply(1, 2) - |} - |""".stripMargin, - """|object Main { - | val x: (Int, Int) = Tuple2.apply[Int, Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `explicit-tuple1` = - check( - """|object Main { - | val x = Tuple2(1, 2) - |} - |""".stripMargin, - """|object Main { - | val x: (Int, Int) = Tuple2[Int, Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `tuple-unapply` = - check( - """|object Main { - | val (fst, snd) = (1, 2) - |} - |""".stripMargin, - """|object Main { - | val (fst: Int, snd: Int) = (1, 2) - |} - |""".stripMargin - ) - - @Test def `list-unapply` = - check( - """|object Main { - | val hd :: tail = List(1, 2) - |} - |""".stripMargin, - """|object Main { - | val hd: Int ::[Int] tail: List[Int] = List[Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `list-match` = - check( - """|object Main { - | val x = List(1, 2) match { - | case hd :: tail => hd - | } - |} - |""".stripMargin, - """|object Main { - | val x: Int = List[Int](1, 2) match { - | case hd: Int ::[Int] tail: List[Int] => hd - | } - |} - |""".stripMargin - ) - - @Test def `case-class-unapply` = - check( - """|object Main { - |case class Foo[A](x: A, y: A) - | val Foo(fst, snd) = Foo(1, 2) - |} - |""".stripMargin, - """|object Main { - |case class Foo[A](x: A, y: A) - | val Foo[Int](fst: Int, snd: Int) = Foo[Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `valueOf` = - check( - """|object O { - | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value - | val m = foo[500] - |} - |""".stripMargin, - """|object O { - | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value - | val m: Int = foo[500](new ValueOf(...)) - |} - |""".stripMargin - ) - - @Test def `case-class1` = - check( - """|object O { - |case class A(x: Int, g: Int)(implicit y: String) - |} - |""".stripMargin, - """|object O { - |case class A(x: Int, g: Int)(implicit y: String) - |} - |""".stripMargin - ) - - @Test def `ord` = - check( - """ - |object Main { - | val ordered = "acb".sorted - |} - |""".stripMargin, - """ - |object Main { - | val ordered: String = augmentString("acb").sorted[Char](Char) - |} - |""".stripMargin - ) - - @Test def `val-def-with-bind` = - check( - """ - |object O { - | val tupleBound @ (one, two) = ("1", "2") - |} - |""".stripMargin, - """ - |object O { - | val tupleBound @ (one: String, two: String) = ("1", "2") - |} - |""".stripMargin - ) - - @Test def `val-def-with-bind-and-comment` = - check( - """ - |object O { - | val tupleBound /* comment */ @ (one, two) = ("1", "2") - |} - |""".stripMargin, - """ - |object O { - | val tupleBound /* comment */ @ (one: String, two: String) = ("1", "2") - |} - |""".stripMargin - ) - diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala new file mode 100644 index 000000000000..0e32e5cf0682 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala @@ -0,0 +1,667 @@ +package dotty.tools.pc.tests.inlayHints + +import dotty.tools.pc.base.BaseInlayHintsSuite + +import org.junit.Test +class InlayHintsSuite extends BaseInlayHintsSuite { + + @Test def `local` = + check( + """|object Main { + | def foo() = { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int) = x + one + | val x = addOne(1) + | } + |} + |""".stripMargin, + """|object Main { + | def foo()/*: Unit<>*/ = { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:17)>>)*/ + | } + |} + |""".stripMargin + ) + + @Test def `type-params` = + check( + """|object Main { + | def hello[T](t: T) = t + | val x = hello(List(1)) + |} + |""".stripMargin, + """|object Main { + | def hello[T](t: T)/*: T<<(2:12)>>*/ = t + | val x/*: List<>[Int<>]*/ = hello/*[List<>[Int<>]]*/(List/*[Int<>]*/(1)) + |} + |""".stripMargin + ) + + @Test def `type-params2` = + check( + """|object Main { + | def hello[T](t: T) = t + | val x = hello(Map((1,"abc"))) + |} + |""".stripMargin, + """|object Main { + | def hello[T](t: T)/*: T<<(2:12)>>*/ = t + | val x/*: Map<>[Int<>, String<>]*/ = hello/*[Map<>[Int<>, String<>]]*/(Map/*[Int<>, String<>]*/((1,"abc"))) + |} + |""".stripMargin, + ) + + @Test def `implicit-param` = + check( + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int) = x + one + | val x = addOne(1) + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ + |} + |""".stripMargin + ) + + @Test def `implicit-conversion` = + check( + """|case class User(name: String) + |object Main { + | implicit def intToUser(x: Int): User = new User(x.toString) + | val y: User = 1 + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit def intToUser(x: Int): User = new User(x.toString) + | val y: User = /*intToUser<<(3:15)>>(*/1/*)*/ + |} + |""".stripMargin + ) + + @Test def `using-param` = + check( + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(using one: Int) = x + one + | val x = addOne(1) + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(using one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ + |} + |""".stripMargin + ) + + @Test def `given-conversion` = + check( + """|case class User(name: String) + |object Main { + | given intToUser: Conversion[Int, User] = User(_.toString) + | val y: User = 1 + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | given intToUser: Conversion[Int, User] = User(_.toString) + | val y: User = /*intToUser<<(3:8)>>(*/1/*)*/ + |} + |""".stripMargin + ) + + @Test def `given-conversion2` = + check( + """|trait Xg: + | def doX: Int + |trait Yg: + | def doY: String + |given (using Xg): Yg with + | def doY = "7" + |""".stripMargin, + """|trait Xg: + | def doX: Int + |trait Yg: + | def doY: String + |given (using Xg): Yg with + | def doY/*: String<>*/ = "7" + |""".stripMargin + ) + + @Test def `basic` = + check( + """|object Main { + | val foo = 123 + |} + |""".stripMargin, + """|object Main { + | val foo/*: Int<>*/ = 123 + |} + |""".stripMargin + ) + + @Test def `list` = + check( + """|object Main { + | val foo = List[Int](123) + |} + |""".stripMargin, + """|object Main { + | val foo/*: List<>[Int<>]*/ = List[Int](123) + |} + |""".stripMargin + ) + + @Test def `list2` = + check( + """|object O { + | def m = 1 :: List(1) + |} + |""".stripMargin, + """|object O { + | def m/*: List<>[Int<>]*/ = 1 ::/*[Int<>]*/ List/*[Int<>]*/(1) + |} + |""".stripMargin + ) + + @Test def `two-param` = + check( + """|object Main { + | val foo = Map((1, "abc")) + |} + |""".stripMargin, + """|object Main { + | val foo/*: Map<>[Int<>, String<>]*/ = Map/*[Int<>, String<>]*/((1, "abc")) + |} + |""".stripMargin, + ) + + @Test def `tuple` = + check( + """|object Main { + | val foo = (123, 456) + |} + |""".stripMargin, + """|object Main { + | val foo/*: (Int<>, Int<>)*/ = (123, 456) + |} + |""".stripMargin + ) + + @Test def `import-needed` = + check( + """|object Main { + | val foo = List[String]("").toBuffer[String] + |} + |""".stripMargin, + """|object Main { + | val foo/*: Buffer<>[String<>]*/ = List[String]("").toBuffer[String] + |} + |""".stripMargin, + ) + + @Test def `lambda-type` = + check( + """|object Main { + | val foo = () => 123 + |} + |""".stripMargin, + """|object Main { + | val foo/*: () => Int<>*/ = () => 123 + |} + |""".stripMargin + ) + + @Test def `block` = + check( + """|object Main { + | val foo = { val z = 123; z + 2} + |} + |""".stripMargin, + """|object Main { + | val foo/*: Int<>*/ = { val z/*: Int<>*/ = 123; z + 2} + |} + |""".stripMargin + ) + + @Test def `refined-types` = + check( + """|object O{ + | trait Foo { + | type T + | type G + | } + | + | val c = new Foo { type T = Int; type G = Long} + |} + |""".stripMargin, + """|object O{ + | trait Foo { + | type T + | type G + | } + | + | val c/*: Foo<<(2:8)>>{type T = Int<>; type G = Long<>}*/ = new Foo { type T = Int; type G = Long} + |} + |""".stripMargin + ) + + @Test def `refined-types2` = + check( + """|object O{ + | trait Foo { + | type T + | } + | val c = new Foo { type T = Int } + | val d = c + |} + |""".stripMargin, + """|object O{ + | trait Foo { + | type T + | } + | val c/*: Foo<<(2:8)>>{type T = Int<>}*/ = new Foo { type T = Int } + | val d/*: Foo<<(2:8)>>{type T = Int<>}*/ = c + |} + |""".stripMargin + ) + + @Test def `refined-types3` = + check( + """|trait Foo extends Selectable { + | type T + |} + | + |val c = new Foo { + | type T = Int + | val x = 0 + | def y = 0 + | var z = 0 + |} + |""".stripMargin, + """|trait Foo extends Selectable { + | type T + |} + | + |val c/*: Foo<<(1:6)>>{type T = Int<>; val x: Int<>; def y: Int<>; val z: Int<>; def z_=(x$1: Int<>): Unit<>}*/ = new Foo { + | type T = Int + | val x/*: Int<>*/ = 0 + | def y/*: Int<>*/ = 0 + | var z/*: Int<>*/ = 0 + |} + |""".stripMargin + ) + + @Test def `dealias` = + check( + """|class Foo() { + | type T = Int + | def getT: T = 1 + |} + | + |object O { + | val c = new Foo().getT + |} + |""".stripMargin, + """|class Foo() { + | type T = Int + | def getT: T = 1 + |} + | + |object O { + | val c/*: Int<>*/ = new Foo().getT + |} + |""".stripMargin + ) + + @Test def `dealias2` = + check( + """|object Foo { + | type T = Int + | def getT: T = 1 + | val c = getT + |} + |""".stripMargin, + """|object Foo { + | type T = Int + | def getT: T = 1 + | val c/*: T<<(2:7)>>*/ = getT + |} + |""".stripMargin + ) + + @Test def `dealias3` = + check( + """|object Foo: + | opaque type T = Int + | def getT: T = 1 + |val c = Foo.getT + |""".stripMargin, + """|object Foo: + | opaque type T = Int + | def getT: T = 1 + |val c/*: T<<(2:14)>>*/ = Foo.getT + |""".stripMargin + ) + + @Test def `dealias4` = + check( + """|object O: + | type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m = O.get + |""".stripMargin, + """|object O: + | type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m/*: Int<> => Int<>*/ = O.get + |""".stripMargin + ) + + @Test def `dealias5` = + check( + """|object O: + | opaque type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m = O.get + |""".stripMargin, + """|object O: + | opaque type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m/*: M<<(2:13)>> => Int<>*/ = O.get + |""".stripMargin + ) + + @Test def `explicit-tuple` = + check( + """|object Main { + | val x = Tuple2.apply(1, 2) + |} + |""".stripMargin, + """|object Main { + | val x/*: (Int<>, Int<>)*/ = Tuple2.apply/*[Int<>, Int<>]*/(1, 2) + |} + |""".stripMargin + ) + + @Test def `explicit-tuple1` = + check( + """|object Main { + | val x = Tuple2(1, 2) + |} + |""".stripMargin, + """|object Main { + | val x/*: (Int<>, Int<>)*/ = Tuple2/*[Int<>, Int<>]*/(1, 2) + |} + |""".stripMargin + ) + + @Test def `tuple-unapply` = + check( + """|object Main { + | val (fst, snd) = (1, 2) + |} + |""".stripMargin, + """|object Main { + | val (fst/*: Int<>*/, snd/*: Int<>*/) = (1, 2) + |} + |""".stripMargin + ) + + @Test def `list-unapply` = + check( + """|object Main { + | val hd :: tail = List(1, 2) + |} + |""".stripMargin, + """|object Main { + | val hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ = List/*[Int<>]*/(1, 2) + |} + |""".stripMargin, + ) + + @Test def `list-match` = + check( + """|object Main { + | val x = List(1, 2) match { + | case hd :: tail => hd + | } + |} + |""".stripMargin, + """|object Main { + | val x/*: Int<>*/ = List/*[Int<>]*/(1, 2) match { + | case hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ => hd + | } + |} + |""".stripMargin, + ) + + @Test def `case-class-unapply` = + check( + """|object Main { + |case class Foo[A](x: A, y: A) + | val Foo(fst, snd) = Foo(1, 2) + |} + |""".stripMargin, + """|object Main { + |case class Foo[A](x: A, y: A) + | val Foo/*[Int<>]*/(fst/*: Int<>*/, snd/*: Int<>*/) = Foo/*[Int<>]*/(1, 2) + |} + |""".stripMargin, + ) + + @Test def `valueOf` = + check( + """|object O { + | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value + | val m = foo[500] + |} + |""".stripMargin, + """|object O { + | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value + | val m/*: Int<>*/ = foo[500]/*(new ValueOf(...))*/ + |} + |""".stripMargin + ) + + @Test def `case-class1` = + check( + """|object O { + |case class A(x: Int, g: Int)(implicit y: String) + |} + |""".stripMargin, + """|object O { + |case class A(x: Int, g: Int)(implicit y: String) + |} + |""".stripMargin + ) + + @Test def `ord` = + check( + """|object Main { + | val ordered = "acb".sorted + |} + |""".stripMargin, + """|object Main { + | val ordered/*: String<>*/ = /*augmentString<>(*/"acb"/*)*/.sorted/*[Char<>]*//*(Char<>)*/ + |} + |""".stripMargin + ) + + @Test def `partial-fun` = + check( + """|object Main { + | List(1).collect { case x => x } + | val x: PartialFunction[Int, Int] = { + | case 1 => 2 + | } + |} + |""".stripMargin, + """|object Main { + | List/*[Int<>]*/(1).collect/*[Int<>]*/ { case x/*: Int<>*/ => x } + | val x: PartialFunction[Int, Int] = { + | case 1 => 2 + | } + |} + |""".stripMargin + ) + + @Test def `val-def-with-bind` = + check( + """|object O { + | val tupleBound @ (one, two) = ("1", "2") + |} + |""".stripMargin, + """|object O { + | val tupleBound @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") + |} + |""".stripMargin + ) + + @Test def `val-def-with-bind-and-comment` = + check( + """|object O { + | val tupleBound /* comment */ @ (one, two) = ("1", "2") + |} + |""".stripMargin, + """|object O { + | val tupleBound /* comment */ @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") + |} + |""".stripMargin + ) + + @Test def `complex` = + check( + """|object ScalatestMock { + | class SRF + | implicit val subjectRegistrationFunction: SRF = new SRF() + | class Position + | implicit val here: Position = new Position() + | implicit class StringTestOps(name: String) { + | def should(right: => Unit)(implicit config: SRF): Unit = () + | def in(f: => Unit)(implicit pos: Position): Unit = () + | } + | implicit def instancesString: Eq[String] with Semigroup[String] = ??? + |} + | + |trait Eq[A] + |trait Semigroup[A] + | + |class DemoSpec { + | import ScalatestMock._ + | + | "foo" should { + | "checkThing1" in { + | checkThing1[String] + | } + | "checkThing2" in { + | checkThing2[String] + | } + | } + | + | "bar" should { + | "checkThing1" in { + | checkThing1[String] + | } + | } + | + | def checkThing1[A](implicit ev: Eq[A]) = ??? + | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A]) = ??? + |} + |""".stripMargin, + """|object ScalatestMock { + | class SRF + | implicit val subjectRegistrationFunction: SRF = new SRF() + | class Position + | implicit val here: Position = new Position() + | implicit class StringTestOps(name: String) { + | def should(right: => Unit)(implicit config: SRF): Unit = () + | def in(f: => Unit)(implicit pos: Position): Unit = () + | } + | implicit def instancesString: Eq[String] with Semigroup[String] = ??? + |} + | + |trait Eq[A] + |trait Semigroup[A] + | + |class DemoSpec { + | import ScalatestMock._ + | + | /*StringTestOps<<(6:17)>>(*/"foo"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | /*StringTestOps<<(6:17)>>(*/"checkThing2"/*)*/ in { + | checkThing2[String]/*(instancesString<<(10:15)>>, instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | }/*(subjectRegistrationFunction<<(3:15)>>)*/ + | + | /*StringTestOps<<(6:17)>>(*/"bar"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | }/*(subjectRegistrationFunction<<(3:15)>>)*/ + | + | def checkThing1[A](implicit ev: Eq[A])/*: Nothing<>*/ = ??? + | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A])/*: Nothing<>*/ = ??? + |} + |""".stripMargin + ) + + @Test def `import-rename` = + check( + """|import scala.collection.{AbstractMap => AB} + |import scala.collection.{Set => S} + | + |object Main { + | def test(d: S[Int], f: S[Char]): AB[Int, String] = { + | val x = d.map(_.toString) + | val y = f + | ??? + | } + | val x = test(Set(1), Set('a')) + |} + |""".stripMargin, + """|import scala.collection.{AbstractMap => AB} + |import scala.collection.{Set => S} + | + |object Main { + | def test(d: S[Int], f: S[Char]): AB[Int, String] = { + | val x/*: S<>[String<>]*/ = d.map/*[String<>]*/(_.toString) + | val y/*: S<>[Char<>]*/ = f + | ??? + | } + | val x/*: AB<>[Int<>, String<>]*/ = test(Set/*[Int<>]*/(1), Set/*[Char<>]*/('a')) + |} + |""".stripMargin, + ) + + @Test def `error-symbol` = + check( + """|package example + |case class ErrorMessage(error) + |""".stripMargin, + """|package example + |case class ErrorMessage(error) + |""".stripMargin + ) +} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala index 01d5e03b6c1e..ac63ef92aef5 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala @@ -1499,3 +1499,37 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | ^ |""".stripMargin ) + + @Test def `correct-alternative` = + check( + """ + |object x { + | def foo(i: Int, s: String): Unit = ??? + | def foo(i: Boolean, s: Int, x: Double): Unit = ??? + | + | foo(false, @@) + |} + |""".stripMargin, + """ + |foo(i: Boolean, s: Int, x: Double): Unit + | ^^^^^^ + |foo(i: Int, s: String): Unit + |""".stripMargin + ) + + @Test def `correct-alternative1` = + check( + """ + |object x { + | def foo(i: Boolean, s: String)(b: Int): Unit = ??? + | def foo(i: Boolean, s: Int)(b: String): Unit = ??? + | + | foo(false, 123)(@@) + |} + |""".stripMargin, + """ + |foo(i: Boolean, s: Int)(b: String): Unit + | ^^^^^^^^^ + |foo(i: Boolean, s: String)(b: Int): Unit + |""".stripMargin + ) diff --git a/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala new file mode 100644 index 000000000000..98ebb0852735 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala @@ -0,0 +1,70 @@ +package dotty.tools.pc.utils + +import scala.collection.mutable.ListBuffer + +import scala.meta.internal.jdk.CollectionConverters._ +import dotty.tools.pc.utils.MtagsEnrichments.* +import dotty.tools.pc.utils.TextEdits + +import org.eclipse.lsp4j.InlayHint +import org.eclipse.lsp4j.TextEdit +import org.eclipse.{lsp4j => l} + +object TestInlayHints { + + // For not local symbols - semanticdb symbol + // val x = 123 + // | + // v + // val x<<: Int/*scala/predef/Int#*/>> = 123 + // For local symbols - definition position in source + // type T = Int + // val x: T = ??? + // val y = x + // | + // v + // val y<<: T/*(0:5,0:5)*/>> = x + def decorationString(inlayHint: InlayHint): String = { + val buffer = ListBuffer.empty[String] + + val labels = inlayHint.getLabel().nn.asScala match { + case Left(label) => List(label) + case Right(labelParts) => labelParts.asScala.map(_.getValue()).toList + } + val data = + inlayHint.getData().asInstanceOf[Array[Any]] + buffer += "/*" + labels.zip(data).foreach { case (label, data) => + buffer += label.nn + buffer ++= readData(data) + } + buffer += "*/" + buffer.toList.mkString + } + + private def readData(data: Any): List[String] = { + data match { + case data: String if data.isEmpty => Nil + case data: String => List("<<", data, ">>") + case data: l.Position => + val str = s"(${data.getLine()}:${data.getCharacter()})" + List("<<", str, ">>") + } + } + + def applyInlayHints(text: String, inlayHints: List[InlayHint]): String = { + val textEdits = inlayHints.map { hint => + val newText = decorationString(hint) + val range = new l.Range(hint.getPosition(), hint.getPosition()) + new TextEdit( + range, + newText + ) + } + TextEdits.applyEdits(text, textEdits) + } + + def removeInlayHints(text: String): String = + text.replaceAll(raw"\/\*(.*?)\*\/", "").nn + +} \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 3c3c04408b84..8d045e50bba0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -579,9 +579,9 @@ object Build { libraryDependencies ++= Seq( "org.scala-lang.modules" % "scala-asm" % "9.6.0-scala-1", // used by the backend Dependencies.compilerInterface, - "org.jline" % "jline-reader" % "3.19.0", // used by the REPL - "org.jline" % "jline-terminal" % "3.19.0", - "org.jline" % "jline-terminal-jna" % "3.19.0", // needed for Windows + "org.jline" % "jline-reader" % "3.25.1", // used by the REPL + "org.jline" % "jline-terminal" % "3.25.1", + "org.jline" % "jline-terminal-jna" % "3.25.1", // needed for Windows ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), ), @@ -1128,7 +1128,7 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.2.0+67-30f8ab53-SNAPSHOT" + val mtagsVersion = "1.2.2+25-bb9dfbb9-SNAPSHOT" Seq( resolvers ++= Resolver.sonatypeOssRepos("snapshots"), @@ -1476,7 +1476,7 @@ object Build { lazy val `scaladoc-js-common` = project.in(file("scaladoc-js/common")). enablePlugins(DottyJSPlugin). dependsOn(`scala3-library-bootstrappedJS`). - settings(libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").cross(CrossVersion.for3Use2_13)) + settings(libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "2.8.0")) lazy val `scaladoc-js-main` = project.in(file("scaladoc-js/main")). enablePlugins(DottyJSPlugin). @@ -1492,7 +1492,7 @@ object Build { settings( Test / fork := false, scalaJSUseMainModuleInitializer := true, - libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").cross(CrossVersion.for3Use2_13) + libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "2.8.0") ) def generateDocumentation(configTask: Def.Initialize[Task[GenerationConfig]]) = diff --git a/project/build.properties b/project/build.properties index e8a1e246e8ad..04267b14af69 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.7 +sbt.version=1.9.9 diff --git a/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala b/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala index 2e10e4fae0fa..1db7973b3129 100644 --- a/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala +++ b/scaladoc-js/contributors/src/content-contributors/ContentContributors.scala @@ -5,9 +5,10 @@ import org.scalajs.dom.ext._ import scala.util.matching.Regex._ import scala.util.matching._ -import org.scalajs.dom.ext.Ajax -import scala.scalajs.js.JSON +import org.scalajs.dom._ import scala.scalajs.js +import scala.scalajs.js.JSON +import scala.scalajs.js.Thenable.Implicits.thenable2future import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -55,8 +56,8 @@ class ContentContributors: def linkForFilename(filename: String) = githubContributorsUrl() + s"/commits?path=$filename" def getAuthorsForFilename(filename: String): Future[List[FullAuthor]] = { val link = linkForFilename(filename) - Ajax.get(link).map(_.responseText).flatMap { json => - val res = JSON.parse(json).asInstanceOf[Commits] + fetch(link).flatMap(_.json()).flatMap { json => + val res = json.asInstanceOf[Commits] val authors = res.map { commit => commit.author match case null => @@ -79,8 +80,8 @@ class ContentContributors: } } def findRename(link: String, filename: String): Future[Option[String]] = { - Ajax.get(link).map(_.responseText).map { json => - val res = JSON.parse(json).asInstanceOf[CommitDescription] + fetch(link).flatMap(_.json()).map { json => + val res = json.asInstanceOf[CommitDescription] val files = res.files files .find(_.filename == filename) diff --git a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala index 010129eb9f59..842b1cb44c23 100644 --- a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala +++ b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala @@ -17,6 +17,9 @@ import java.net.URI class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearchEngine, parser: QueryParser): val initialChunkSize = 5 val resultsChunkSize = 20 + + val querySearch = Option(URLSearchParams(window.location.search).get("search")).filter(_.nonEmpty) + def pathToRoot() = window.document.documentElement.getAttribute("data-pathToRoot") extension (p: PageEntry) def toHTML(boldChars: Set[Int]) = @@ -148,7 +151,7 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc val htmlEntries = results.map(result => result.pageEntry.toHTML(result.indices)) val loadMoreElement = createLoadMoreElement - def loadMoreResults(entries: List[raw.HTMLElement]): Unit = { + def loadMoreResults(entries: List[HTMLElement]): Unit = { loadMoreElement.onclick = (event: Event) => { entries.take(resultsChunkSize).foreach(_.classList.remove("hidden")) val nextElems = entries.drop(resultsChunkSize) @@ -192,7 +195,7 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc } } - def createLoadingAnimation: raw.HTMLElement = + def createLoadingAnimation: HTMLElement = div(cls := "loading-wrapper")( div(cls := "loading") ) @@ -262,7 +265,8 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc document.body.addEventListener("keydown", (e: KeyboardEvent) => handleGlobalKeyDown(e)) private val inputElem: html.Input = - input(cls := "scaladoc-searchbar-input", `type` := "search", `placeholder`:= "Find anything").tap { element => + val initialValue = querySearch.getOrElse("") + input(cls := "scaladoc-searchbar-input", `type` := "search", `placeholder`:= "Find anything", value := initialValue).tap { element => element.addEventListener("input", { e => clearTimeout(timeoutHandle) val inputValue = e.target.asInstanceOf[html.Input].value @@ -346,7 +350,7 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc val selectedElement = resultsDiv.querySelector("[selected]") if selectedElement != null then { selectedElement.removeAttribute("selected") - def recur(elem: raw.Element): raw.Element = { + def recur(elem: Element): Element = { val prev = elem.previousElementSibling if prev == null then null else { @@ -366,7 +370,7 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc } private def handleArrowDown() = { val selectedElement = resultsDiv.querySelector("[selected]") - def recur(elem: raw.Element): raw.Element = { + def recur(elem: Element): Element = { val next = elem.nextElementSibling if next == null then null else { @@ -453,3 +457,7 @@ class SearchbarComponent(engine: PageSearchEngine, inkuireEngine: InkuireJSSearc } inputElem.dispatchEvent(new Event("input")) + if (querySearch.isDefined && !document.body.contains(rootDiv)) { + document.body.appendChild(rootDiv) + inputElem.focus() + } diff --git a/scaladoc-js/main/src/searchbar/engine/InkuireJSSearchEngine.scala b/scaladoc-js/main/src/searchbar/engine/InkuireJSSearchEngine.scala index 2dde7178e3f7..75cf6f43a907 100644 --- a/scaladoc-js/main/src/searchbar/engine/InkuireJSSearchEngine.scala +++ b/scaladoc-js/main/src/searchbar/engine/InkuireJSSearchEngine.scala @@ -2,7 +2,6 @@ package dotty.tools.scaladoc import scala.io.Source import dotty.tools.scaladoc.PageEntry -import org.scalajs.dom.webworkers.Worker import org.scalajs.dom._ import scala.scalajs.js.{ JSON, Dynamic } import scala.collection.mutable.ListBuffer diff --git a/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala b/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala index 465ee44d1ad4..3551b207a246 100644 --- a/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala +++ b/scaladoc-js/main/src/versions-dropdown/DropdownHandler.scala @@ -7,9 +7,10 @@ import scala.util.{Success,Failure} import org.scalajs.dom._ import org.scalajs.dom.ext._ import scala.scalajs.js.annotation.JSExportTopLevel -import org.scalajs.dom.ext.Ajax +import org.scalajs.dom._ import scala.scalajs.js import scala.scalajs.js.JSON +import scala.scalajs.js.Thenable.Implicits.thenable2future import utils.HTML._ @@ -33,7 +34,7 @@ class DropdownHandler: btn.classList.add("disabled") btn.classList.add("hidden") - private def getURLContent(url: String): Future[String] = Ajax.get(url).map(_.responseText) + private def getURLContent(url: String): Future[String] = fetch(url).flatMap(_.text()) window.sessionStorage.getItem(KEY) match case null => // If no key, returns null @@ -68,7 +69,7 @@ end DropdownHandler def dropdownHandler(e: Event) = e.stopPropagation() if document.getElementById("version-dropdown").getElementsByTagName("a").size > 0 && - window.getSelection.toString.length == 0 then + window.getSelection().toString.length == 0 then document.getElementById("version-dropdown").classList.toggle("expanded") document.getElementById("dropdown-trigger").classList.toggle("selected") diff --git a/tests/neg-macros/annot-suspend-cycle.check b/tests/neg-macros/annot-suspend-cycle.check index e3ecea7345fd..437398f1d668 100644 --- a/tests/neg-macros/annot-suspend-cycle.check +++ b/tests/neg-macros/annot-suspend-cycle.check @@ -9,4 +9,4 @@ Compilation stopped since no further progress can be made. To fix this, place macros in one set of files and their callers in another. -Compiling with -Xprint-suspension gives more information. +Compile with -Xprint-suspension for information. diff --git a/tests/neg-macros/i19526.check b/tests/neg-macros/i19526.check new file mode 100644 index 000000000000..2ad0a7aa2199 --- /dev/null +++ b/tests/neg-macros/i19526.check @@ -0,0 +1,6 @@ +Cyclic macro dependencies in tests/neg-macros/i19526/Test.scala. +Compilation stopped since no further progress can be made. + +To fix this, place macros in one set of files and their callers in another. + +Compile with -Xprint-suspension for information. diff --git a/tests/neg-macros/i19526/Macro.scala b/tests/neg-macros/i19526/Macro.scala new file mode 100644 index 000000000000..e6861c1986ef --- /dev/null +++ b/tests/neg-macros/i19526/Macro.scala @@ -0,0 +1,15 @@ +package crash.test + +import scala.language.dynamics + +import scala.quoted.* + +object Export extends Dynamic: + inline def applyDynamic(name: "apply")(inline args: Any*): Stack = ${ + applyDynamicImpl('args) + } + + def applyDynamicImpl(args: Expr[Seq[Any]])(using Quotes): Expr[Stack] = + import quotes.reflect.* + + '{ Stack("", Vector.empty) } diff --git a/tests/neg-macros/i19526/Test.scala b/tests/neg-macros/i19526/Test.scala new file mode 100644 index 000000000000..45ae51b664dd --- /dev/null +++ b/tests/neg-macros/i19526/Test.scala @@ -0,0 +1,14 @@ +package crash.test + +case class Stack private[crash] ( + exports: String, + dependsOn: Vector[Int] +) + +trait StackFactory: + val exports: Export.type = Export + + def apply(dependsOn: Int*): Stack = + Export().copy(dependsOn = dependsOn.toVector) + +// nopos-error diff --git a/tests/neg-macros/macros-in-same-project-4.check b/tests/neg-macros/macros-in-same-project-4.check index 79c78fd0fb7f..f027f57637f4 100644 --- a/tests/neg-macros/macros-in-same-project-4.check +++ b/tests/neg-macros/macros-in-same-project-4.check @@ -3,4 +3,4 @@ Compilation stopped since no further progress can be made. To fix this, place macros in one set of files and their callers in another. -Compiling with -Xprint-suspension gives more information. +Compile with -Xprint-suspension for information. diff --git a/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check index 7687543ea75f..54d3d8f6c556 100644 --- a/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check +++ b/tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.check @@ -6,14 +6,20 @@ 14 | val b = js.constructorOf[NativeJSObject.type] // error | ^^^^^^^^^^^^^^^^^^^ | NativeJSObject.type is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:16:27 ----------------------------- 16 | val c = js.constructorOf[NativeJSClass with NativeJSTrait] // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | NativeJSClass & NativeJSTrait is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:17:27 ----------------------------- 17 | val d = js.constructorOf[NativeJSClass { def bar: Int }] // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | NativeJSClass{def bar: Int} is not a class type + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:19:27 ----------------------------------------- 19 | val e = js.constructorOf[JSTrait] // error | ^^^^^^^ @@ -22,19 +28,29 @@ 20 | val f = js.constructorOf[JSObject.type] // error | ^^^^^^^^^^^^^ | JSObject.type is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:22:27 ----------------------------- 22 | val g = js.constructorOf[JSClass with JSTrait] // error | ^^^^^^^^^^^^^^^^^^^^ | JSClass & JSTrait is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:23:27 ----------------------------- 23 | val h = js.constructorOf[JSClass { def bar: Int }] // error | ^^^^^^^^^^^^^^^^^^^^^^^^ | JSClass{def bar: Int} is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:25:42 ----------------------------- 25 | def foo[A <: js.Any] = js.constructorOf[A] // error | ^ | A is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructorof-error-in-prepjsinterop.scala:26:66 ----------------------------- 26 | def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorOf[A] // error | ^ | A is not a class type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check index 142de318efd3..879a566d9fea 100644 --- a/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check +++ b/tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.check @@ -6,14 +6,20 @@ 14 | val b = js.constructorTag[NativeJSObject.type] // error | ^ | NativeJSObject.type is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:16:61 ---------------------------- 16 | val c = js.constructorTag[NativeJSClass with NativeJSTrait] // error | ^ | NativeJSClass & NativeJSTrait is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:17:59 ---------------------------- 17 | val d = js.constructorTag[NativeJSClass { def bar: Int }] // error | ^ | NativeJSClass{def bar: Int} is not a class type + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:19:36 ---------------------------------------- 19 | val e = js.constructorTag[JSTrait] // error | ^ @@ -22,19 +28,29 @@ 20 | val f = js.constructorTag[JSObject.type] // error | ^ | JSObject.type is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:22:49 ---------------------------- 22 | val g = js.constructorTag[JSClass with JSTrait] // error | ^ | JSClass & JSTrait is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:23:53 ---------------------------- 23 | val h = js.constructorTag[JSClass { def bar: Int }] // error | ^ | JSClass{def bar: Int} is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:25:45 ---------------------------- 25 | def foo[A <: js.Any] = js.constructorTag[A] // error | ^ | A is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg-scalajs/jsconstructortag-error-in-prepjsinterop.scala:26:69 ---------------------------- 26 | def bar[A <: js.Any: scala.reflect.ClassTag] = js.constructorTag[A] // error | ^ | A is not a class type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/19087.check b/tests/neg/19087.check new file mode 100644 index 000000000000..db6da907205d --- /dev/null +++ b/tests/neg/19087.check @@ -0,0 +1,20 @@ +-- [E103] Syntax Error: tests/neg/19087.scala:4:2 ---------------------------------------------------------------------- +4 | Option.when(state.x == 0) body // error: Illegal start of toplevel definition + | ^^^^^^ + | Illegal start of toplevel definition + | + | longer explanation available when compiling with `-explain` +-- [E040] Syntax Error: tests/neg/19087.scala:15:6 --------------------------------------------------------------------- +15 | bar = 2 // error: ',' or ')' expected + | ^^^ + | ',' or ')' expected, but identifier found +-- [E067] Syntax Error: tests/neg/19087.scala:3:4 ---------------------------------------------------------------------- +3 |def foo[T](state: State)(body: => T): Option[T] // error: only classes can have declared but undefined members + | ^ + | Declaration of method foo not allowed here: only classes can have declared but undefined members +-- [E050] Type Error: tests/neg/19087.scala:13:22 ---------------------------------------------------------------------- +13 | foo(state.copy(x = 5): // Missing ")" // error: method copy in class State does not take more parameters + | ^^^^^^^^^^^^^^^^^ + | method copy in class State does not take more parameters + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/19087.scala b/tests/neg/19087.scala new file mode 100644 index 000000000000..b52dddcad651 --- /dev/null +++ b/tests/neg/19087.scala @@ -0,0 +1,15 @@ +case class State(x: Int) + +def foo[T](state: State)(body: => T): Option[T] // error: only classes can have declared but undefined members + Option.when(state.x == 0) body // error: Illegal start of toplevel definition + +var bar = 0 +val state = State(0) + +def app: Function1[Int, Unit] = + new Function1[Int, Unit]: + def apply(x: Int): Unit = + foo(state): + foo(state.copy(x = 5): // Missing ")" // error: method copy in class State does not take more parameters + println("a") + bar = 2 // error: ',' or ')' expected diff --git a/tests/neg/classOf.check b/tests/neg/classOf.check index e3be3ca17026..e6576a60bbff 100644 --- a/tests/neg/classOf.check +++ b/tests/neg/classOf.check @@ -2,13 +2,19 @@ 6 | def f1[T] = classOf[T] // error | ^ | T is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg/classOf.scala:7:32 --------------------------------------------------------------------- 7 | def f2[T <: String] = classOf[T] // error | ^ | T is not a class type | | where: T is a type in method f2 with bounds <: String + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg/classOf.scala:9:18 --------------------------------------------------------------------- 9 | val y = classOf[C { type I = String }] // error | ^^^^^^^^^^^^^^^^^^^^^ | Test.C{type I = String} is not a class type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i13808.check b/tests/neg/i13808.check index f3e0ebac7141..e0f6d01c09db 100644 --- a/tests/neg/i13808.check +++ b/tests/neg/i13808.check @@ -2,7 +2,11 @@ 13 |case class Boom[A](value: A) derives OpaqueType, Foo // error // error | ^^^^^^^^^^ | OpaqueTypes.OpaqueType is not a class type + | + | longer explanation available when compiling with `-explain` -- [E170] Type Error: tests/neg/i13808.scala:13:49 --------------------------------------------------------------------- 13 |case class Boom[A](value: A) derives OpaqueType, Foo // error // error | ^^^ | FooModule.Foo is not a class type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i19402.scala b/tests/neg/i19402.scala new file mode 100644 index 000000000000..dd5a4903027c --- /dev/null +++ b/tests/neg/i19402.scala @@ -0,0 +1,11 @@ +object Test: + + class Bar(foo: Foo) + + class Foo + + given (Foo) = ??? + given (using a: Int): Int = ??? + given [T](using a: T): T = ??? + given bar(foo: Foo): Bar = Bar(foo) // error: using is expected + \ No newline at end of file diff --git a/tests/neg/i19594.check b/tests/neg/i19594.check new file mode 100644 index 000000000000..bb9ff3fc68af --- /dev/null +++ b/tests/neg/i19594.check @@ -0,0 +1,8 @@ +-- [E172] Type Error: tests/neg/i19594.scala:12:14 --------------------------------------------------------------------- +12 | assertEquals(true, 1, "values are not the same") // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Can you see me?! +-- [E172] Type Error: tests/neg/i19594.scala:13:14 --------------------------------------------------------------------- +13 | assertEquals(true, 1) // error + | ^^^^^^^^^^^^^^^^^^^^^ + | Can you see me?! diff --git a/tests/neg/i19594.scala b/tests/neg/i19594.scala new file mode 100644 index 000000000000..a559da8d9250 --- /dev/null +++ b/tests/neg/i19594.scala @@ -0,0 +1,13 @@ +import scala.annotation.implicitNotFound + +@implicitNotFound("Can you see me?!") +trait Compare[A, B] + +object example extends App: + + // The presence of the below default argument prevents the `implicitNotFound` message from appearing + def assertEquals[A, B](a: A, b: B, clue: => Any = "values are not the same") + (implicit comp: Compare[A, B]): Unit = () + + assertEquals(true, 1, "values are not the same") // error + assertEquals(true, 1) // error diff --git a/tests/neg/i19619/InnerClass.java b/tests/neg/i19619/InnerClass.java new file mode 100644 index 000000000000..32bb1642a232 --- /dev/null +++ b/tests/neg/i19619/InnerClass.java @@ -0,0 +1,67 @@ +// InnerClass.java + +package lib; + +public class InnerClass { + + public class Inner { + public U innerField; + + public Inner(U innerField) { + this.innerField = innerField; + } + + public U getInnerField() { + return innerField; + } + } + + public class Outer { + + public class Nested { + + public U outerField; + public V innerField; + + public Nested(U outerField, V innerField) { + this.outerField = outerField; + this.innerField = innerField; + } + + public U getOuterField() { + return outerField; + } + + public V getInnerField() { + return innerField; + } + } + } + + public Inner createInner(U innerField) { + return new Inner<>(innerField); + } + + public Outer.Nested createNested(U outerField, V innerField) { + Outer outer = new Outer<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static InnerClass.Inner createInnerStatic(U innerField) { + InnerClass innerClass = new InnerClass(); + return innerClass.new Inner<>(innerField); + } + + public static InnerClass.Outer.Nested createNestedStatic(U outerField, V innerField) { + InnerClass innerClass = new InnerClass(); + InnerClass.Outer outer = innerClass.new Outer<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static void consumeNestedStatic(InnerClass.Outer.Nested nested) { + } + + public static void consumeNestedStatic2(Outer.Nested nested) { + } + +} diff --git a/tests/neg/i19619/InnerClassGen.java b/tests/neg/i19619/InnerClassGen.java new file mode 100644 index 000000000000..3a691aa0608f --- /dev/null +++ b/tests/neg/i19619/InnerClassGen.java @@ -0,0 +1,80 @@ +// InnerClassGen.java + +package lib; + +public class InnerClassGen { + + public class Inner { + public T rootField; + public U innerField; + + public Inner(T rootField, U innerField) { + this.rootField = rootField; + this.innerField = innerField; + } + + public T getRootField() { + return rootField; + } + + public U getInnerField() { + return innerField; + } + } + + public class Outer { + + public class Nested { + public T rootField; + public U outerField; + public V innerField; + + public Nested(T rootField, U outerField, V innerField) { + this.rootField = rootField; + this.outerField = outerField; + this.innerField = innerField; + } + + public T getRootField() { + return rootField; + } + + public U getOuterField() { + return outerField; + } + + public V getInnerField() { + return innerField; + } + } + } + + public static class OuterStatic { + public static class NestedStatic { + } + } + + public Inner createInner(T rootField, U innerField) { + return new Inner<>(rootField, innerField); + } + + public Outer.Nested createNested(T rootField, U outerField, V innerField) { + Outer outer = new Outer<>(); + return outer.new Nested<>(rootField, outerField, innerField); + } + + public static InnerClassGen.Inner createInnerStatic(T rootField, U innerField) { + InnerClassGen innerClassGen = new InnerClassGen<>(); + return innerClassGen.new Inner<>(rootField, innerField); + } + + public static InnerClassGen.Outer.Nested createNestedStatic(T rootField, U outerField, V innerField) { + InnerClassGen innerClassGen = new InnerClassGen<>(); + InnerClassGen.Outer outer = innerClassGen.new Outer<>(); + return outer.new Nested<>(rootField, outerField, innerField); + } + + public static void consumeNestedStatic(InnerClassGen.Outer.Nested nested) { + } + +} diff --git a/tests/neg/i19619/Test.scala b/tests/neg/i19619/Test.scala new file mode 100644 index 000000000000..85f9b3e529ac --- /dev/null +++ b/tests/neg/i19619/Test.scala @@ -0,0 +1,12 @@ +import lib.InnerClass +import lib.InnerClassGen + +@main def Test = + + locally: + val ici: InnerClass = new InnerClass() + val ici_inner1: ici.Inner[Long] = ici.createInner[Long](47L) // error + + locally: + val ici: InnerClassGen[String] = new InnerClassGen() + val ici_inner1: ici.Inner[Long] = ici.createInner[Long]("Hello", 47L) // error diff --git a/tests/neg/i19762.check b/tests/neg/i19762.check new file mode 100644 index 000000000000..93def936e2a0 --- /dev/null +++ b/tests/neg/i19762.check @@ -0,0 +1,6 @@ +-- [E019] Syntax Error: tests/neg/i19762.scala:8:31 -------------------------------------------------------------------- +8 | def combine(x: Int, y: Int)) = x + y // error + | ^ + | Missing return type + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i19762.scala b/tests/neg/i19762.scala new file mode 100644 index 000000000000..4966f0d8cb53 --- /dev/null +++ b/tests/neg/i19762.scala @@ -0,0 +1,8 @@ +trait Monoid[A]: + def combine(x: A, y: A): A + def empty: A + +object Monoid: + lazy val addInt: Monoid[Int] = new: + val empty = 0 + def combine(x: Int, y: Int)) = x + y // error \ No newline at end of file diff --git a/tests/pos/i19715.scala b/tests/pos/i19715.scala new file mode 100644 index 000000000000..91aeda5c1698 --- /dev/null +++ b/tests/pos/i19715.scala @@ -0,0 +1,15 @@ +class Tup(): + def app(n: Int): String = "a" + +class NT(t: Tup): + def toTup = t +object NT: + extension (x: NT) + def app(n: Int): Boolean = true + given Conversion[NT, Tup] = _.toTup + +def test = + val nt = new NT(Tup()) + val x = nt.app(3) + val _: Boolean = x + diff --git a/tests/run/i16458.scala b/tests/run/i16458.scala new file mode 100644 index 000000000000..1c4b0365e45e --- /dev/null +++ b/tests/run/i16458.scala @@ -0,0 +1,54 @@ + +object Test { + import scala.xml.* + def main(args: Array[String]): Unit = { + val xml =
FooBar
+ assert( + xml match + case Seq(elm: Elem, comment: Comment) if + elm.label == "div" && + elm.child(0) == Atom(Text("FooBar")) && + comment.label == " /.modal-content " + => true + case _ => false + , + xml + ) + } +} + +package scala.xml { + type MetaData = AnyRef + + trait NamespaceBinding + object TopScope extends NamespaceBinding + object Null + abstract class Node { + def label: String + def child: Seq[Node] + override def toString = label + child.mkString + } + class Comment(commentText: String) extends Node{ + def label = commentText + def child = Nil + } + class Elem(prefix: String, val label: String, attributes1: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, val child: Node*) extends Node + class NodeBuffer extends Seq[Node] { + val nodes = scala.collection.mutable.ArrayBuffer.empty[Node] + def &+(o: Any): NodeBuffer = + o match { + case n: Node => nodes.addOne(n) ; this + case t: Text => nodes.addOne(Atom(t)) ; this + } + // Members declared in scala.collection.IterableOnce + def iterator: Iterator[scala.xml.Node] = nodes.iterator + // Members declared in scala.collection.SeqOps + def apply(i: Int): scala.xml.Node = nodes(i) + def length: Int = nodes.length + } + case class Text(text: String) + case class Atom(t: Text) extends Node { + def label = t.text + def child = Nil + } +} \ No newline at end of file diff --git a/tests/run/i19619/InnerClass.java b/tests/run/i19619/InnerClass.java new file mode 100644 index 000000000000..32bb1642a232 --- /dev/null +++ b/tests/run/i19619/InnerClass.java @@ -0,0 +1,67 @@ +// InnerClass.java + +package lib; + +public class InnerClass { + + public class Inner { + public U innerField; + + public Inner(U innerField) { + this.innerField = innerField; + } + + public U getInnerField() { + return innerField; + } + } + + public class Outer { + + public class Nested { + + public U outerField; + public V innerField; + + public Nested(U outerField, V innerField) { + this.outerField = outerField; + this.innerField = innerField; + } + + public U getOuterField() { + return outerField; + } + + public V getInnerField() { + return innerField; + } + } + } + + public Inner createInner(U innerField) { + return new Inner<>(innerField); + } + + public Outer.Nested createNested(U outerField, V innerField) { + Outer outer = new Outer<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static InnerClass.Inner createInnerStatic(U innerField) { + InnerClass innerClass = new InnerClass(); + return innerClass.new Inner<>(innerField); + } + + public static InnerClass.Outer.Nested createNestedStatic(U outerField, V innerField) { + InnerClass innerClass = new InnerClass(); + InnerClass.Outer outer = innerClass.new Outer<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static void consumeNestedStatic(InnerClass.Outer.Nested nested) { + } + + public static void consumeNestedStatic2(Outer.Nested nested) { + } + +} diff --git a/tests/run/i19619/InnerClassGen.java b/tests/run/i19619/InnerClassGen.java new file mode 100644 index 000000000000..3a691aa0608f --- /dev/null +++ b/tests/run/i19619/InnerClassGen.java @@ -0,0 +1,80 @@ +// InnerClassGen.java + +package lib; + +public class InnerClassGen { + + public class Inner { + public T rootField; + public U innerField; + + public Inner(T rootField, U innerField) { + this.rootField = rootField; + this.innerField = innerField; + } + + public T getRootField() { + return rootField; + } + + public U getInnerField() { + return innerField; + } + } + + public class Outer { + + public class Nested { + public T rootField; + public U outerField; + public V innerField; + + public Nested(T rootField, U outerField, V innerField) { + this.rootField = rootField; + this.outerField = outerField; + this.innerField = innerField; + } + + public T getRootField() { + return rootField; + } + + public U getOuterField() { + return outerField; + } + + public V getInnerField() { + return innerField; + } + } + } + + public static class OuterStatic { + public static class NestedStatic { + } + } + + public Inner createInner(T rootField, U innerField) { + return new Inner<>(rootField, innerField); + } + + public Outer.Nested createNested(T rootField, U outerField, V innerField) { + Outer outer = new Outer<>(); + return outer.new Nested<>(rootField, outerField, innerField); + } + + public static InnerClassGen.Inner createInnerStatic(T rootField, U innerField) { + InnerClassGen innerClassGen = new InnerClassGen<>(); + return innerClassGen.new Inner<>(rootField, innerField); + } + + public static InnerClassGen.Outer.Nested createNestedStatic(T rootField, U outerField, V innerField) { + InnerClassGen innerClassGen = new InnerClassGen<>(); + InnerClassGen.Outer outer = innerClassGen.new Outer<>(); + return outer.new Nested<>(rootField, outerField, innerField); + } + + public static void consumeNestedStatic(InnerClassGen.Outer.Nested nested) { + } + +} diff --git a/tests/run/i19619/InnerClassSub.java b/tests/run/i19619/InnerClassSub.java new file mode 100644 index 000000000000..5a1d1f4d3857 --- /dev/null +++ b/tests/run/i19619/InnerClassSub.java @@ -0,0 +1,54 @@ +// InnerClass.java + +package lib; + +public class InnerClassSub extends InnerClass { + + public class InnerSub extends Inner { + public InnerSub(U innerField) { + super(innerField); + } + } + + public class OuterSub extends Outer { + public OuterSub() { + super(); + } + } + + public Inner createInnerSub(U innerField) { + return new InnerSub<>(innerField); + } + + public Outer.Nested createNestedSub(U outerField, V innerField) { + OuterSub outer = new OuterSub<>(); + return outer.new Nested<>(outerField, innerField); + } + + public InnerClass.Inner createInnerSub2(U innerField) { + return new InnerSub<>(innerField); + } + + public InnerClass.Outer.Nested createNestedSub2(U outerField, V innerField) { + OuterSub outer = new OuterSub<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static InnerClass.Inner createInnerStatic(U innerField) { + InnerClassSub innerClass = new InnerClassSub(); + return innerClass.new Inner<>(innerField); + } + + public static InnerClass.Outer.Nested createNestedStatic(U outerField, V innerField) { + InnerClassSub innerClass = new InnerClassSub(); + InnerClassSub.Outer outer = innerClass.new Outer<>(); + return outer.new Nested<>(outerField, innerField); + } + + public static void consumeNestedStatic(InnerClass.Outer.Nested nested) { + } + + public static void consumeNestedStatic2(Outer.Nested nested) { + } + +} diff --git a/tests/run/i19619/RawTypes.java b/tests/run/i19619/RawTypes.java new file mode 100644 index 000000000000..4373a04093eb --- /dev/null +++ b/tests/run/i19619/RawTypes.java @@ -0,0 +1,18 @@ +// RawTypes.java + +package lib; + +public class RawTypes { + + public class C { + public class D { + } + } + + public static void mii_Raw_Raw(RawTypes.C.D d) { + } + + public static void mii_Raw_Raw2(C.D d) { + } + +} diff --git a/tests/run/i19619/Test.scala b/tests/run/i19619/Test.scala new file mode 100644 index 000000000000..871e7a490ea0 --- /dev/null +++ b/tests/run/i19619/Test.scala @@ -0,0 +1,61 @@ +// scalajs: --skip + +import lib.InnerClass +import lib.InnerClassGen +import lib.RawTypes +import lib.InnerClassSub + +@main def Test = + + locally: + val ici: InnerClass = new InnerClass() + // val ici_inner1: ici.Inner[Long] = ici.createInner[Long](47L) // error + val ici_inner2: InnerClass#Inner[Long] = ici.createInner[Long](47L) + val ici_inner3: InnerClass#Inner[Long] = InnerClass.createInnerStatic[Long](47L) + + val ici_outer: InnerClass#Outer[Long] = new ici.Outer[Long]() + val ici_nested1: InnerClass#Outer[Long]#Nested[Int] = new ici_outer.Nested[Int](47L, 23) + val ici_nested2: InnerClass#Outer[Long]#Nested[Int] = ici.createNested[Long, Int](47L, 23) + val ici_nested3: InnerClass#Outer[Long]#Nested[Int] = InnerClass.createNestedStatic[Long, Int](47L, 23) + + InnerClass.consumeNestedStatic(ici_nested3) + InnerClass.consumeNestedStatic2(ici_nested3) + + locally: + val ici: InnerClassGen[String] = new InnerClassGen() + // val ici_inner1: ici.Inner[Long] = ici.createInner[Long]("Hello", 47L) // error + val ici_inner2: InnerClassGen[String]#Inner[Long] = ici.createInner[Long]("Hello", 47L) + val ici_inner3: InnerClassGen[String]#Inner[Long] = InnerClassGen.createInnerStatic[String, Long]("Hello", 47L) + + val ici_outer: InnerClassGen[String]#Outer[Long] = new ici.Outer[Long]() + val ici_nested1: InnerClassGen[String]#Outer[Long]#Nested[Int] = new ici_outer.Nested[Int]("Hello", 47L, 23) + val ici_nested2: InnerClassGen[String]#Outer[Long]#Nested[Int] = ici.createNested[Long, Int]("Hello", 47L, 23) + val ici_nested3: InnerClassGen[String]#Outer[Long]#Nested[Int] = InnerClassGen.createNestedStatic[String, Long, Int]("Hello", 47L, 23) + + InnerClassGen.consumeNestedStatic(ici_nested3) + + locally: + val rt: RawTypes = new RawTypes() + val c: RawTypes#C[String] = new rt.C[String]() + + val cd_ii: RawTypes#C[String]#D[String] = new c.D[String]() + val cd_ii_Raw: RawTypes#C[?]#D[?] = cd_ii + + RawTypes.mii_Raw_Raw(cd_ii_Raw) + RawTypes.mii_Raw_Raw2(cd_ii_Raw) + + locally: + val ici: InnerClassSub = new InnerClassSub() + // val ici_inner1: ici.Inner[Long] = ici.createInner[Long](47L) // error + val ici_inner2: InnerClass#Inner[Long] = ici.createInnerSub[Long](47L) + val ici_inner2_2: InnerClass#Inner[Long] = ici.createInnerSub2[Long](47L) + val ici_inner3: InnerClass#Inner[Long] = InnerClassSub.createInnerStatic[Long](47L) + + val ici_outer: InnerClassSub#Outer[Long] = new ici.Outer[Long]() + val ici_nested1: InnerClassSub#Outer[Long]#Nested[Int] = new ici_outer.Nested[Int](47L, 23) + val ici_nested2: InnerClass#Outer[Long]#Nested[Int] = ici.createNestedSub[Long, Int](47L, 23) + val ici_nested2_2: InnerClass#Outer[Long]#Nested[Int] = ici.createNestedSub2[Long, Int](47L, 23) + val ici_nested3: InnerClass#Outer[Long]#Nested[Int] = InnerClassSub.createNestedStatic[Long, Int](47L, 23) + + InnerClass.consumeNestedStatic(ici_nested3) + InnerClass.consumeNestedStatic2(ici_nested3) diff --git a/tests/run/i19711.scala b/tests/run/i19711.scala new file mode 100644 index 000000000000..a9ef03b398e2 --- /dev/null +++ b/tests/run/i19711.scala @@ -0,0 +1,29 @@ +class Foo(val s: Any): + def this(s: String) = + this(0) +class Bar(s: String) extends Foo(s): + def foo = s + +class Foo2(val s: Any) +class Bar2(s: String) extends Foo2(s): + def foo = s + +case class Config(_config: String) + +abstract class Foo3(val config: Config) { + def this(config: String) = { + this(Config(config)) + } +} + +class Bar3(config: String) extends Foo3(config) { + def foo(): Unit = { + config.getClass() + } +} + + +@main def Test = + Bar("").foo + Bar2("").foo + Bar3("").foo()