Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow nowarn subclasses #22762

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def New(tpt: Tree, argss: List[List[Tree]])(using Context): Tree =
ensureApplied(argss.foldLeft(makeNew(tpt))(Apply(_, _)))

/** A new expression with constrictor and possibly type arguments. See
/** A new expression with constructor and possibly type arguments. See
* `New(tpt, argss)` for details.
*/
def makeNew(tpt: Tree)(using Context): Tree = {
Expand Down
35 changes: 30 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dotty.tools
package dotc
package core

import Symbols.*, Types.*, Contexts.*, Constants.*, Phases.*
import Symbols.*, Types.*, Contexts.*, Constants.*, Decorators.*, Names.*, Phases.*
import ast.tpd, tpd.*
import util.Spans.Span
import printing.{Showable, Printer}
Expand Down Expand Up @@ -43,6 +43,29 @@ object Annotations {
def argumentConstantString(i: Int)(using Context): Option[String] =
for (case Constant(s: String) <- argumentConstant(i)) yield s

def argsForSuper(parent: Symbol)(using Context): List[Tree] =
val symbol = this.symbol
val args = arguments
if symbol == parent then args
else if symbol.asClass.superClass == parent then
val params: List[Name] = parent.primaryConstructor.paramSymss.headOrNil.map(_.name)
val subArgs: Map[Name, Tree] = symbol.primaryConstructor.paramSymss.headOrNil.map(_.name).zip(args).toMap
val superArgs: Map[Name, Tree] =
symbol.annotations.collect {
case annot if annot.matches(defn.SuperArgMetaAnnot) =>
val List(Literal(Constant(param: String)), value) = annot.arguments: @unchecked
param.toTermName -> value
}.toMap
val superFwdArgs: Map[Name, Name] =
symbol.annotations.collect {
case annot if annot.matches(defn.SuperFwdArgMetaAnnot) =>
val List(Literal(Constant(param: String)), Literal(Constant(subParam: String))) = annot.arguments: @unchecked
param.toTermName -> subParam.toTermName
}.toMap
val argsForSuper = params.map(p => superArgs.getOrElse(p, subArgs(superFwdArgs(p))))
if params.lengthCompare(argsForSuper) == 0 then argsForSuper else Nil
else Nil

/** The tree evaluation is in progress. */
def isEvaluating: Boolean = false

Expand Down Expand Up @@ -148,6 +171,11 @@ object Annotations {
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree @unchecked]
}

class DeferredSym(sym: Symbol, treeFn: Context ?=> Tree)
extends LazyAnnotation:
protected var mySym: Symbol | (Context ?=> Symbol) | Null = sym
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)

class DeferredSymAndTree(symFn: Context ?=> Symbol, treeFn: Context ?=> Tree)
extends LazyAnnotation:
protected var mySym: Symbol | (Context ?=> Symbol) | Null = ctx ?=> symFn(using ctx)
Expand Down Expand Up @@ -214,10 +242,7 @@ object Annotations {

/** Create an annotation where the tree is computed lazily. */
def deferred(sym: Symbol)(treeFn: Context ?=> Tree): Annotation =
new LazyAnnotation {
protected var myTree: Tree | (Context ?=> Tree) | Null = ctx ?=> treeFn(using ctx)
protected var mySym: Symbol | (Context ?=> Symbol) | Null = sym
}
DeferredSym(sym, treeFn)

/** Create an annotation where the symbol and the tree are computed lazily. */
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree): Annotation =
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ object Decorators {
else
xs.reduceLeft(op)

extension [T](xss: List[List[T]])
def headOrNil: List[T] = if xss.isEmpty then Nil else xss.head

extension [T, U](xss: List[List[T]])
def nestedMap(f: T => U): List[List[U]] = xss match
case xs :: xss1 => xs.map(f) :: xss1.nestedMap(f)
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,7 @@ class Definitions {
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.untrackedCaptures")
@tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use")
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")

@tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature")
@tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter")
@tu lazy val BeanSetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanSetter")
Expand All @@ -1078,6 +1079,10 @@ class Definitions {
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
@tu lazy val CompanionClassMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionClass")
@tu lazy val CompanionMethodMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionMethod")
@tu lazy val DefaultArgMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.defaultArg")
@tu lazy val SuperArgMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.superArg")
@tu lazy val SuperFwdArgMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.superFwdArg")

@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
Expand Down
31 changes: 30 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2840,8 +2840,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
for (annot <- mdef.mods.annotations)
val annot1 = typedAnnotation(annot)(using annotCtx)
checkAnnotApplicable(annot1, sym)
if Annotations.annotClass(annot1) == defn.NowarnAnnot then
val annotCls = annotClass(annot1)
if annotCls == defn.NowarnAnnot then
registerNowarn(annot1, mdef)
else if annotCls.derivesFrom(defn.NowarnAnnot) then
val nowarnArgs = Annotation(annot1).argsForSuper(defn.NowarnAnnot)
val annot2 = New(defn.NowarnAnnot.typeRef, nowarnArgs).withSpan(annot1.span)
registerNowarn(annot2, mdef)
}

def typedAnnotation(annot: untpd.Tree)(using Context): Tree =
Expand Down Expand Up @@ -3120,6 +3125,28 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case _ =>
ptrees

/* add `@superArg` / `@superFwdArg` to subclasses of concrete annotations, e.g.,
* `@superArg("value", "cat=deprecation")` for `class nodep extends nowarn("cat=deprecation")`
* this is done by duplicating the untyped super arguments before type checking the super call, because the
* super call can be transformed by named/default arguments. to build the `@superArg` annotations, the super
* call is type checked using `typedAnnotation`, which uses Mode.ANNOTmode. */
def promoteSuperArgs(parentTree: Tree, parent: Symbol, constr: DefDef) =
val supCls = cls.superClass
if !supCls.is(Abstract)
&& supCls.derivesFrom(defn.AnnotationClass)
&& supCls.primaryConstructor.paramSymss.sizeIs == 1
then
val superAnnotArgs = tpd.allTermArguments(parentTree)
if superAnnotArgs.nonEmpty then
val ps = constr.termParamss.headOrNil.map(_.symbol).toSet
parent.primaryConstructor.paramSymss.headOrNil.lazyZip(superAnnotArgs).foreach: (p, arg) =>
val key = Literal(Constant(p.name.toString))
val annot = if ps(arg.symbol) then
Annotation(defn.SuperFwdArgMetaAnnot, List(key, Literal(Constant(arg.symbol.name.toString))), cls.span)
else
Annotation(defn.SuperArgMetaAnnot, List(key, arg), cls.span)
cls.addAnnotation(annot)

/** Checks if one of the decls is a type with the same name as class type member in selfType */
def classExistsOnSelf(decls: Scope, self: tpd.ValDef): Boolean = {
val selfType = self.tpt.tpe
Expand Down Expand Up @@ -3218,6 +3245,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val firstParentTpe = parents1.head.tpe.dealias
val firstParent = firstParentTpe.typeSymbol

if !ctx.isAfterTyper then promoteSuperArgs(parents1.head, firstParent, constr1)

checkEnumParent(cls, firstParent)

if defn.ScalaValueClasses()(cls) && ctx.settings.YcompileScala2Library.value then
Expand Down
18 changes: 18 additions & 0 deletions library/src/scala/annotation/meta/defaultArg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package scala.annotation
package meta

/**
* This internal meta annotation is used by the compiler to support default annotation arguments.
*
* For an annotation definition `class ann(x: Int = defaultExpr) extends Annotation`, the compiler adds
* `@defaultArg(defaultExpr)` to the parameter `x`. This causes the syntax tree of `defaultExpr` to be
* stored in the classfile.
*
* When using a default annotation argument, the compiler can recover the syntax tree and insert it in the
* `AnnotationInfo`.
*
* For details, see `scala.reflect.internal.AnnotationInfos.AnnotationInfo`.
*/
@meta.param class defaultArg(arg: Any) extends StaticAnnotation {
def this() = this(null)
}
22 changes: 22 additions & 0 deletions library/src/scala/annotation/meta/superArg.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package scala.annotation
package meta

/**
* This internal annotation encodes arguments passed to annotation superclasses. Example:
*
* {{{
* class a(x: Int) extends Annotation
* class b extends a(42) // the compiler adds `@superArg("x", 42)` to class b
* }}}
*/
class superArg(p: String, v: Any) extends StaticAnnotation

/**
* This internal annotation encodes arguments passed to annotation superclasses. Example:
*
* {{{
* class a(x: Int) extends Annotation
* class b(y: Int) extends a(y) // the compiler adds `@superFwdArg("x", "y")` to class b
* }}}
*/
class superFwdArg(p: String, n: String) extends StaticAnnotation
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
|""".stripMargin,
"""|superVisorStrategy: Int (commit: '')
|super (commit: '')
|superArg(p: String, v: Any): superArg - scala.annotation.meta (commit: '')
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta (commit: '')
|""".stripMargin,
includeCommitCharacter = true
)
Expand Down Expand Up @@ -78,6 +80,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
|""".stripMargin,
"""|superVisorStrategy: Int
|super
|superArg(p: String, v: Any): superArg - scala.annotation.meta
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta
|""".stripMargin
)

Expand All @@ -98,6 +102,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
|""".stripMargin,
"""|superVisorStrategy: Int
|super
|superArg(p: String, v: Any): superArg - scala.annotation.meta
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta
|""".stripMargin
)

Expand All @@ -118,6 +124,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
|""".stripMargin,
"""|superVisorStrategy: Int
|super
|superArg(p: String, v: Any): superArg - scala.annotation.meta
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta
|""".stripMargin
)

Expand All @@ -135,6 +143,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
|}
|""".stripMargin,
"""|super
|superArg(p: String, v: Any): superArg - scala.annotation.meta
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta
|""".stripMargin
)

Expand Down Expand Up @@ -392,6 +402,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
""".stripMargin,
"""|supervisorStrategy: Int
|super
|superArg(p: String, v: Any): superArg - scala.annotation.meta
|superFwdArg(p: String, n: String): superFwdArg - scala.annotation.meta
|""".stripMargin
)

Expand Down
103 changes: 103 additions & 0 deletions tests/warn/nowarn.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
-- [E002] Syntax Warning: tests/warn/nowarn.scala:88:25 ----------------------------------------------------------------
88 | @nowarn("v") def f = try 1 // warn: try without catch/finally
| ^^^^^
| A try without catch or finally is equivalent to putting
| its body in a block; no exceptions are handled.
|Matching filters for @nowarn or -Wconf:
| - id=E2
| - name=EmptyCatchAndFinallyBlock
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:35:14 -------------------------------------------------------
35 | val t7b = { 0; 1 } // warn: discard pure
| ^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/warn/nowarn.scala:41:18 -------------------------------------------------------
41 | def f: Unit = 1 // warn: discard non-Unit
| ^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:60:6 --------------------------------------------------------
60 | 123 // warn: discard pure
| ^^^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/warn/nowarn.scala:71:4 --------------------------------------------------------
71 | 123 // warn: discard non-Unit
| ^^^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/warn/nowarn.scala:79:4 --------------------------------------------------------
79 | 123 // warn: discard non-Unit
| ^^^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:94:14 -------------------------------------------------------
94 | def g = { 1; 2 } // warn: discard pure
| ^
| A pure expression does nothing in statement position
|Matching filters for @nowarn or -Wconf:
| - id=E129
| - name=PureExpressionInStatementPosition
|
| longer explanation available when compiling with `-explain`
-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:123:20 ------------------------------------------------------
123 | @nodep def t4 = { 1; 2 } // warn // warn: unused @nowarn
| ^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- Deprecation Warning: tests/warn/nowarn.scala:84:8 -------------------------------------------------------------------
84 | a + dep // warn: deprecated
| ^^^
| method dep in class C is deprecated since 1.2.3: message
-- Deprecation Warning: tests/warn/nowarn.scala:119:25 -----------------------------------------------------------------
119 | @purr def t2 = new C().dep // warn // warn: unused @nowarn
| ^^^^^^^^^^^
| method dep in class C is deprecated since 1.2.3: message
-- Warning: tests/warn/nowarn.scala:13:2 -------------------------------------------------------------------------------
13 | @nowarn def t0 = { 0: @nowarn; 1 } // warn: outer @nowarn unused
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:14:28 ------------------------------------------------------------------------------
14 | @nowarn def t1 = { 0: Int @nowarn; 1 } // warn: inner @nowarn unused, it covers the type, not the expression
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:15:2 -------------------------------------------------------------------------------
15 | @nowarn @ann(dep) def t2 = 0 // warn: deprecation warning, @nowarn unused
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:23:2 -------------------------------------------------------------------------------
23 | @nowarn class I1a { // warn: unused @nowarn
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:28:2 -------------------------------------------------------------------------------
28 | @nowarn class I1b { // warn: unused @nowarn
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:45:2 -------------------------------------------------------------------------------
45 | @nowarn object I3a { // warn: supresses nothing
| ^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:69:3 -------------------------------------------------------------------------------
69 | @nowarn("msg=something else") // warn: unused
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:95:5 -------------------------------------------------------------------------------
95 | @nowarn("v") def unused = 0 // warn: unused @nowarn
| ^^^^^^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:119:2 ------------------------------------------------------------------------------
119 | @purr def t2 = new C().dep // warn // warn: unused @nowarn
| ^^^^^
| @nowarn annotation does not suppress any warnings
-- Warning: tests/warn/nowarn.scala:123:2 ------------------------------------------------------------------------------
123 | @nodep def t4 = { 1; 2 } // warn // warn: unused @nowarn
| ^^^^^^
| @nowarn annotation does not suppress any warnings
Loading
Loading