Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 53d4ff7

Browse files
Warn if empty template name has trailing colon
This is to guard the edge case `object X_:` where the user may or may not have intended colon syntax. The next line does not tell us, since it may be indented yet not nested. Therefore, any empty template with a suspicious name will warn. Non-empty templates are given a pass even if written `object X_: :`.
1 parent ac337b8 commit 53d4ff7

File tree

7 files changed

+90
-17
lines changed

7 files changed

+90
-17
lines changed

‎compiler/src/dotty/tools/dotc/ast/untpd.scala‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
4444
extends MemberDef {
4545
type ThisTree[+T <: Untyped] <: Trees.NameTree[T] & Trees.MemberDef[T] & ModuleDef
4646
def withName(name: Name)(using Context): ModuleDef = cpy.ModuleDef(this)(name.toTermName, impl)
47+
def isBackquoted: Boolean = hasAttachment(Backquoted)
4748
}
4849

4950
/** An untyped template with a derives clause. Derived parents are added to the end

‎compiler/src/dotty/tools/dotc/core/NameOps.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ object NameOps {
8989
// Ends with operator characters
9090
while i >= 0 && isOperatorPart(name(i)) do i -= 1
9191
if i == -1 then return true
92-
// Optionnally prefixed with alpha-numeric characters followed by `_`
92+
// Optionally prefixed with alpha-numeric characters followed by `_`
9393
if name(i) != '_' then return false
9494
while i >= 0 && isIdentifierPart(name(i)) do i -= 1
9595
i == -1

‎compiler/src/dotty/tools/dotc/parsing/Parsers.scala‎

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3784,6 +3784,18 @@ object Parsers {
37843784
/* -------- DEFS ------------------------------------------- */
37853785

37863786
def finalizeDef(md: MemberDef, mods: Modifiers, start: Int): md.ThisTree[Untyped] =
3787+
def checkName(): Unit =
3788+
def checkName(name: Name): Unit =
3789+
if !name.isEmpty
3790+
&& !Chars.isOperatorPart(name.firstCodePoint) // warn a_: not ::
3791+
&& name.endsWith(":")
3792+
then
3793+
report.warning(AmbiguousTemplateName(md), md.namePos)
3794+
md match
3795+
case md @ TypeDef(name, impl: Template) if impl.body.isEmpty && !md.isBackquoted => checkName(name)
3796+
case md @ ModuleDef(name, impl) if impl.body.isEmpty && !md.isBackquoted => checkName(name)
3797+
case _ =>
3798+
checkName()
37873799
md.withMods(mods).setComment(in.getDocComment(start))
37883800

37893801
type ImportConstr = (Tree, List[ImportSelector]) => Tree
@@ -4233,14 +4245,15 @@ object Parsers {
42334245

42344246
/** ClassDef ::= id ClassConstr TemplateOpt
42354247
*/
4236-
def classDef(start: Offset, mods: Modifiers): TypeDef = atSpan(start, nameStart) {
4237-
classDefRest(start, mods, ident().toTypeName)
4238-
}
4248+
def classDef(start: Offset, mods: Modifiers): TypeDef =
4249+
val td = atSpan(start, nameStart):
4250+
classDefRest(mods, ident().toTypeName)
4251+
finalizeDef(td, mods, start)
42394252

4240-
def classDefRest(start: Offset, mods: Modifiers, name: TypeName): TypeDef =
4253+
def classDefRest(mods: Modifiers, name: TypeName): TypeDef =
42414254
val constr = classConstr(if mods.is(Case) then ParamOwner.CaseClass else ParamOwner.Class)
42424255
val templ = templateOpt(constr)
4243-
finalizeDef(TypeDef(name, templ), mods, start)
4256+
TypeDef(name, templ)
42444257

42454258
/** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsTermParamClauses
42464259
*/
@@ -4258,11 +4271,15 @@ object Parsers {
42584271

42594272
/** ObjectDef ::= id TemplateOpt
42604273
*/
4261-
def objectDef(start: Offset, mods: Modifiers): ModuleDef = atSpan(start, nameStart) {
4262-
val name = ident()
4263-
val templ = templateOpt(emptyConstructor)
4264-
finalizeDef(ModuleDef(name, templ), mods, start)
4265-
}
4274+
def objectDef(start: Offset, mods: Modifiers): ModuleDef =
4275+
val md = atSpan(start, nameStart):
4276+
val nameIdent = termIdent()
4277+
val templ = templateOpt(emptyConstructor)
4278+
ModuleDef(nameIdent.name.asTermName, templ)
4279+
.tap: md =>
4280+
if nameIdent.isBackquoted then
4281+
md.pushAttachment(Backquoted, ())
4282+
finalizeDef(md, mods, start)
42664283

42674284
private def checkAccessOnly(mods: Modifiers, caseStr: String): Modifiers =
42684285
// We allow `infix` and `into` on `enum` definitions.
@@ -4494,7 +4511,7 @@ object Parsers {
44944511
Template(constr, parents, Nil, EmptyValDef, Nil)
44954512
else if !newSyntaxAllowed
44964513
|| in.token == WITH && tparams.isEmpty && vparamss.isEmpty
4497-
// if new syntax is still allowed and there are parameters, they mist be new style conditions,
4514+
// if new syntax is still allowed and there are parameters, they must be new style conditions,
44984515
// so old with-style syntax would not be allowed.
44994516
then
45004517
withTemplate(constr, parents)

‎compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
235235
case CannotInstantiateQuotedTypeVarID // errorNumber: 219
236236
case DefaultShadowsGivenID // errorNumber: 220
237237
case RecurseWithDefaultID // errorNumber: 221
238+
case AmbiguousTemplateNameID // errorNumber: 222
238239

239240
def errorNumber = ordinal - 1
240241

‎compiler/src/dotty/tools/dotc/reporting/messages.scala‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import parsing.Tokens
1313
import printing.Highlighting.*
1414
import printing.Formatting
1515
import ErrorMessageID.*
16-
import ast.Trees
16+
import ast.Trees.*
1717
import ast.desugar
18+
import ast.tpd
19+
import ast.untpd
1820
import config.{Feature, MigrationVersion, ScalaVersion}
1921
import transform.patmat.Space
2022
import transform.patmat.SpaceEngine
@@ -25,9 +27,6 @@ import typer.Inferencing
2527
import scala.util.control.NonFatal
2628
import StdNames.nme
2729
import Formatting.{hl, delay}
28-
import ast.Trees.*
29-
import ast.untpd
30-
import ast.tpd
3130
import scala.util.matching.Regex
3231
import java.util.regex.Matcher.quoteReplacement
3332
import cc.CaptureSet.IdentityCaptRefMap
@@ -3109,7 +3108,7 @@ class MissingImplicitArgument(
31093108
def msg(using Context): String =
31103109

31113110
def formatMsg(shortForm: String)(headline: String = shortForm) = arg match
3112-
case arg: Trees.SearchFailureIdent[?] =>
3111+
case arg: SearchFailureIdent[?] =>
31133112
arg.tpe match
31143113
case _: NoMatchingImplicits => headline
31153114
case tpe: SearchFailureType =>
@@ -3741,3 +3740,8 @@ final class RecurseWithDefault(name: Name)(using Context) extends TypeMsg(Recurs
37413740
i"Recursive call used a default argument for parameter $name."
37423741
override protected def explain(using Context): String =
37433742
"It's more explicit to pass current or modified arguments in a recursion."
3743+
3744+
class AmbiguousTemplateName(tree: NamedDefTree[?])(using Context) extends SyntaxMsg(AmbiguousTemplateNameID):
3745+
override protected def msg(using Context) = i"name `${tree.name}` should be enclosed in backticks"
3746+
override protected def explain(using Context): String =
3747+
"Names with trailing operator characters may fuse with a subsequent colon if not set off by backquotes or spaces."

‎tests/warn/i16072.check‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Warning: tests/warn/i16072.scala:4:2 --------------------------------------------------------------------------------
2+
4 | def x = 1 // warn too far right
3+
| ^
4+
| Line is indented too far to the right, or a `{` or `:` is missing
5+
-- [E222] Syntax Warning: tests/warn/i16072.scala:3:7 ------------------------------------------------------------------
6+
3 |object Hello_: // warn colon in name without backticks because the body is empty
7+
| ^^^^^^^
8+
| name `Hello_:` should be enclosed in backticks
9+
|
10+
| longer explanation available when compiling with `-explain`
11+
-- Deprecation Warning: tests/warn/i16072.scala:12:10 ------------------------------------------------------------------
12+
12 |object :: : // warn deprecated colon without backticks for operator name
13+
| ^
14+
| `:` after symbolic operator is deprecated; use backticks around operator instead
15+
-- Warning: tests/warn/i16072.scala:21:2 -------------------------------------------------------------------------------
16+
21 | def y = 1 // warn
17+
| ^
18+
| Line is indented too far to the right, or a `{` or `:` is missing
19+
-- [E222] Syntax Warning: tests/warn/i16072.scala:20:6 -----------------------------------------------------------------
20+
20 |class Uhoh_: // warn
21+
| ^^^^^^
22+
| name `Uhoh_:` should be enclosed in backticks
23+
|
24+
| longer explanation available when compiling with `-explain`

‎tests/warn/i16072.scala‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//> using options -deprecation
2+
3+
object Hello_: // warn colon in name without backticks because the body is empty
4+
def x = 1 // warn too far right
5+
6+
object Goodbye_: : // nowarn if non-empty body without nit-picking about backticks
7+
def x = 2
8+
9+
object `Byte_`:
10+
def x = 3
11+
12+
object :: : // warn deprecated colon without backticks for operator name
13+
def x = 42
14+
15+
object ::: // nowarn
16+
17+
object Braces_: { // nowarn because body is non-empty with an EmptyTree
18+
}
19+
20+
class Uhoh_: // warn
21+
def y = 1 // warn
22+
23+
@main def hello =
24+
println(Byte_)
25+
println(Hello_:) // apparently user did forget a colon, see https://youforgotapercentagesignoracolon.com/
26+
println(x)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /