Skip to content
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
38 changes: 38 additions & 0 deletions .github/workflows/scaladoc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,41 @@ jobs:

- name: Test sourcelinks to stdlib
run: true # ./project/scripts/sbt scaladoc/sourceLinksIntegrationTest:test

check-error-code-snippets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: coursier/cache-action@v7
- uses: VirtusLab/scala-cli-setup@v1
with:
jvm: temurin:17
apps: sbt
- name: Publish compiler locally
run: |
version=$(./project/scripts/sbt "print scala3-compiler-bootstrapped-new/version" | tail -n1)
echo "SCALA_VERSION=$version" >> $GITHUB_ENV
echo "Would test snippets using Scala $version"

# Publish locally minimal set of artifacts that would be required to test snippets
sbt "set every doc := new File(\"unused\");\
scala3-bootstrapped-new/publishLocal
"
- name: Test error code snippets
shell: bash
run: scala-cli test -S ${{ env.SCALA_VERSION }} --with-compiler project/scripts/checkErrorCodeSnippets.test.scala -- +l +a +c
- name: "[On failure] Print reproduction/fix steps"
if: failure()
run: |
cat << EOF >> $GITHUB_STEP_SUMMARY
Snippets validation for error codes failed

If you've modified messages of positions of error codes, you might need to update the expected outputs, to do it run: following command:
scala -S ${{ env.SCALA_VERSION }} project/scripts/checkErrorCodeSnippets.scala --with-compiler --main-class=updateAllErrorCodeOutputs

Single error code validation can be performed using:
scala -S ${{ env.SCALA_VERSION }} project/scripts/checkErrorCodeSnippets.scala --with-compiler -- docs/_docs/reference/error-codes/E001.md [--verbose --update-output]

To check the validation use dedicated test suite:
scala test -S ${{ env.SCALA_VERSION }} --with-compiler project/scripts/checkErrorCodeSnippets.test.scala -- +l +a +c
EOF
16 changes: 8 additions & 8 deletions compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case SuperCallsNotAllowedInlineableID // errorNumber: 82
case NotAPathID // errorNumber: 83
case WildcardOnTypeArgumentNotAllowedOnNewID // errorNumber: 84
case FunctionTypeNeedsNonEmptyParameterListID // errorNumber: 85
case FunctionTypeNeedsNonEmptyParameterListID extends ErrorMessageID(isActive = false) // errorNumber: 85
case WrongNumberOfParametersID // errorNumber: 86
case DuplicatePrivateProtectedQualifierID // errorNumber: 87
case ExpectedStartOfTopLevelDefinitionID // errorNumber: 88
Expand All @@ -107,7 +107,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case ReturnOutsideMethodDefinitionID // errorNumber: 91
case UncheckedTypePatternID // errorNumber: 92
case ExtendFinalClassID // errorNumber: 93
case EnumCaseDefinitionInNonEnumOwnerID // errorNumber: 94
case EnumCaseDefinitionInNonEnumOwnerID extends ErrorMessageID(isActive = false) // errorNumber: 94
case ExpectedTypeBoundOrEqualsID // errorNumber: 95
case ClassAndCompanionNameClashID // errorNumber: 96
case TailrecNotApplicableID // errorNumber: 97
Expand All @@ -118,15 +118,15 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case UndefinedNamedTypeParameterID // errorNumber: 102
case IllegalStartOfStatementID // errorNumber: 1033
case TraitIsExpectedID // errorNumber: 104
case TraitRedefinedFinalMethodFromAnyRefID // errorNumber: 105
case TraitRedefinedFinalMethodFromAnyRefID extends ErrorMessageID(isActive = false) // errorNumber: 105
case PackageNameAlreadyDefinedID // errorNumber: 106
case UnapplyInvalidNumberOfArgumentsID // errorNumber: 107
case UnapplyInvalidReturnTypeID // errorNumber: 108
case StaticFieldsOnlyAllowedInObjectsID // errorNumber: 109
case CyclicInheritanceID // errorNumber: 110
case BadSymbolicReferenceID // errorNumber: 111
case UnableToExtendSealedClassID // errorNumber: 112
case SymbolHasUnparsableVersionNumberID // errorNumber: 113
case SymbolHasUnparsableVersionNumberID // errorNumber: 113
case SymbolChangedSemanticsInVersionID // errorNumber: 114
case UnableToEmitSwitchID // errorNumber: 115
case MissingCompanionForStaticID // errorNumber: 116
Expand Down Expand Up @@ -163,9 +163,9 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case RedundantModifierID // errorNumber: 147
case TypedCaseDoesNotExplicitlyExtendTypedEnumID // errorNumber: 148
case IllegalRedefinitionOfStandardKindID // errorNumber: 149
case NoExtensionMethodAllowedID // errorNumber: 150
case ExtensionMethodCannotHaveTypeParamsID // errorNumber: 151
case ExtensionCanOnlyHaveDefsID // errorNumber: 152
case NoExtensionMethodAllowedID extends ErrorMessageID(isActive = false) // errorNumber: 150
case ExtensionMethodCannotHaveTypeParamsID extends ErrorMessageID(isActive = false) // errorNumber: 151
case ExtensionCanOnlyHaveDefsID extends ErrorMessageID(isActive = false) // errorNumber: 152
case UnexpectedPatternForSummonFromID // errorNumber: 153
case AnonymousInstanceCannotBeEmptyID // errorNumber: 154
case TypeSpliceInValPatternID extends ErrorMessageID(isActive = false) // errorNumber: 155
Expand Down Expand Up @@ -209,7 +209,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case VolatileOnValID // errorNumber: 193
case ExtensionNullifiedByMemberID // errorNumber: 194
case PhantomSymbolNotValueID // errorNumber: 195
case ContextBoundCompanionNotValueID // errorNumber: 196
case ContextBoundCompanionNotValueID extends ErrorMessageID(isActive = false) // errorNumber: 196
case InlinedAnonClassWarningID // errorNumber: 197
case UnusedSymbolID // errorNumber: 198
case TailrecNestedCallID //errorNumber: 199
Expand Down
28 changes: 0 additions & 28 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2108,11 +2108,6 @@ class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsEx
}
}

class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) {
def msg(using Context) = i"Traits cannot redefine final $method from ${hl("class AnyRef")}."
def explain(using Context) = ""
}

class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context)
extends NamingMsg(AlreadyDefinedID):
def msg(using Context) =
Expand Down Expand Up @@ -2823,29 +2818,6 @@ class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Cont
| Please choose a different name to avoid conflicts
|"""
}

class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context)
extends SyntaxMsg(NoExtensionMethodAllowedID) {
def msg(using Context) = i"No extension method allowed here, since collective parameters are given"
def explain(using Context) =
i"""|Extension method:
| `${mdef}`
|is defined inside an extension clause which has collective parameters.
|"""
}

class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context)
extends SyntaxMsg(ExtensionMethodCannotHaveTypeParamsID) {
def msg(using Context) = i"Extension method cannot have type parameters since some were already given previously"

def explain(using Context) =
i"""|Extension method:
| `${mdef}`
|has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has
|it's own type parameters. Please consider moving these to the extension clause's type parameter list.
|"""
}

class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context)
extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) {
def msg(using Context) = i"Only methods allowed here, since collective parameters are given"
Expand Down
106 changes: 106 additions & 0 deletions docs/_docs/reference/error-codes/E001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: E001: Empty Catch Block
kind: Error
---
# E001: Empty Catch Block

This error is emitted when a `try` expression has a `catch` block that does not contain any case handlers.

A `try` expression should be followed by some mechanism to handle any exceptions
thrown. Typically a `catch` expression follows the `try` and pattern matches
on any expected exceptions. For example:

```scala
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we recoment braceless syntax in these snippets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of example for other error codes use bracless syntax, I can adjust this one.

println("hello")
} catch {
case e: Exception => ???
}
```

It is also possible to follow a `try` immediately by a `finally` - letting the
exception propagate - but still allowing for some clean up in `finally`:

```scala
try {
println("hello")
} finally {
// perform your cleanup here!
}
```

It is recommended to use the `NonFatal` extractor to catch all exceptions as it
correctly handles transfer functions like `return`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might need an example? What is transfer function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, example and description was generated based on -explain description from compiler.
So in fact here we have a duplication, the same example in explain and in description (altough outputs are allowed to change any moment)


---

## Example

```scala sc:fail sc-opts:-explain
def example() =
try {
println("hello")
} catch { }
```

### Error

```scala sc:nocompile
-- [E001] Syntax Error: example.scala:4:4 --------------------------------------
4 | } catch { }
| ^^^^^^^^^
| The catch block does not contain a valid expression, try
| adding a case like - case e: Exception => to the block
|-----------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| A try expression should be followed by some mechanism to handle any exceptions
| thrown. Typically a catch expression follows the try and pattern matches
| on any expected exceptions. For example:
|
| import scala.util.control.NonFatal
|
| try {
| println("hello")
| } catch {
| case NonFatal(e) => ???
| }
|
| It is also possible to follow a try immediately by a finally - letting the
| exception propagate - but still allowing for some clean up in finally:
|
| try {
| println("hello")
| } finally {
| // perform your cleanup here!
| }
|
| It is recommended to use the NonFatal extractor to catch all exceptions as it
| correctly handles transfer functions like return.
-----------------------------------------------------------------------------
```

### Solution

```scala sc:compile
// Remove redundant 'try' block
def example() =
println("hello")
```

```scala sc:compile
// Alternative: Add a case handler to catch exceptions
import scala.util.control.NonFatal

def example() =
try println("hello")
catch { case NonFatal(e) => println(s"Caught: $e") }
```

```scala sc:compile
// Alternative: use finally instead if you only need cleanup
def example() =
try println("hello")
finally println("cleanup")
```

112 changes: 112 additions & 0 deletions docs/_docs/reference/error-codes/E002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: E002: Empty Catch And Finally Block
kind: Warning
---
# E002: Empty Catch And Finally Block

This warning is emitted when a `try` expression has neither a `catch` block nor a `finally` block. Such a `try` is redundant since no exceptions are handled.

A `try` expression should be followed by some mechanism to handle any exceptions
thrown. Typically a `catch` expression follows the `try` and pattern matches
on any expected exceptions. For example:

```scala
try {
println("hello")
} catch {
case e: Exception => ???
}
```

It is also possible to follow a `try` immediately by a `finally` - letting the
exception propagate - but still allowing for some clean up in `finally`:

```scala
try {
println("hello")
} finally {
// perform your cleanup here!
}
```

It is recommended to use the `NonFatal` extractor to catch all exceptions as it
correctly handles transfer functions like `return`.

---

## Example

```scala sc:fail sc-opts:-explain,-Werror
@main def example() =
try {
println("hello")
}
```

### Warning

```scala sc:nocompile
-- [E002] Syntax Warning: example.scala:2:2 ------------------------------------
2 | try {
| ^
| A try without catch or finally is equivalent to putting
| its body in a block; no exceptions are handled.
3 | println("hello")
4 | }
|-----------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| A try expression should be followed by some mechanism to handle any exceptions
| thrown. Typically a catch expression follows the try and pattern matches
| on any expected exceptions. For example:
|
| import scala.util.control.NonFatal
|
| try {
| println("hello")
| } catch {
| case NonFatal(e) => ???
| }
|
| It is also possible to follow a try immediately by a finally - letting the
| exception propagate - but still allowing for some clean up in finally:
|
| try {
| println("hello")
| } finally {
| // perform your cleanup here!
| }
|
| It is recommended to use the NonFatal extractor to catch all exceptions as it
| correctly handles transfer functions like return.
-----------------------------------------------------------------------------
```

### Solution

```scala sc:compile sc-opts:-Werror
// Remove redundant 'try' block
def example() =
println("hello")
```

```scala sc:compile sc-opts:-Werror
// Alternative: Add a catch block to handle exceptions
import scala.util.control.NonFatal

def example() =
try
println("hello")
catch
case NonFatal(e) => println(s"Caught: $e")
```

```scala sc:compile sc-opts:-Werror
// Alternative: Add a finally block for cleanup
def example() =
try
println("hello")
finally
println("cleanup")
```

Loading
Loading