Skip to content

Fix #4037: Format sources with scalafmt #4260

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

Open
wants to merge 5 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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Migrate code style to scalafmt
0e1a937278abf0e36c787b693748a62c8bbb942a
7 changes: 7 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version = 2.7.4
preset = Scala.js
maxColumn = 100
newlines.alwaysBeforeMultilineDef = false
rewrite.rules = [AvoidInfix, PreferCurlyFors]
docstrings.style = AsteriskSpace
danglingParentheses.ctrlSite = false
46 changes: 3 additions & 43 deletions CODINGSTYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
The Scala.js project has a strict policy regarding coding style.
This is one of the cornerstones that has allowed Scala.js to maintain a clean, consistent and maintainable codebase.

This document tries to document the style in use as much as possible to make it easier for everyone to contribute.
Most coding style rules can be applied automatically by scalafmt. It is recommended that you configure your editor to reformat on save; see the [scalafmt instructions](https://scalameta.org/scalafmt/docs/installation.html) for how to configure this.

A *few* of these rules are checked automatically using Scalastyle, but most of them are too complex to teach to an automated tool.
While most rules are checked automatically by scalafmt and Scalastyle, but there are also a few rules that are too complex to teach to an automated tool.
This document tries to document the manually enforced rules as much as possible to make it easier for everyone to contribute.
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize now that this is problematic: scalafmt's formatting on the current codebase breaks certain of the rules even though they were followed before (what I have seen is mostly blocks not being present). IMHO this is quite serious :-/

Is there any scalafmt mode that is much more aggressive and formats everything in a consistent manner (not maybe the manner we have right now)? IMO that might be a better alternative.

Choose a reason for hiding this comment

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


The Scala.js core team has decided to designate a single developer to drive and "maintain" the project's coding style.
This allows us to maintain a consistent, yet flexible style.
Expand All @@ -32,10 +33,6 @@ In some cases, if breaking a line makes it significantly less readable, it can g
Rationale: when reviewing on GitHub, only 120 characters are visible; when reviewing on a mobile phone, only 80 characters are visible.
And we do review on mobile phone quite a lot.

#### Where to break a line

A line can be broken after either a `,` or a `(`, or possibly after a binary operator in a long expression.

### Indentation

In general, indentation is 2 spaces, except continuation lines, which are indented 4 spaces.
Expand Down Expand Up @@ -113,26 +110,6 @@ def abs(x: Int): Int =
-x
```

#### Long expressions with binary operators

Very long expressions consisting of binary operators at their "top-level" can be broken *without indentation* if they are alone in their brace-delimited block or their actual parameter.
This happens mostly for long chains of `&&`s, `||`s, or string concatenations.
Here is an example:

```scala
val isValidIdent = {
ident != "" &&
ident.charAt(0).isUnicodeIdentifierStart &&
ident.tail.forall(_.isUnicodeIdentifierPart)
}

if (!isValidIdent) {
reportError(
"This string is very long and will " +
"span several lines.")
}
```

#### Braces in lambdas

In lambdas (anonymous functions), the opening brace must be placed before the formal arguments, and not after the `=>`:
Expand Down Expand Up @@ -160,21 +137,6 @@ val f = (x: Int) => body
val ys = xs.map(x => x + 1)
```

### Spaces

There must not be any space before the following tokens: `:` `,` `;` `)`

There must be exactly one space after the following tokens: `:` `,` `;` `if` `for` `while`

There must be exactly one space before the tokens `=` and `=>`, and either exactly one space or a new line after them.
Exception: `=>` may be vertically aligned instead in some scenarios: see [the "Pattern matching" section](#pattern-matching).

There must be exactly one space before and after `{` and `}`.
With the exception of partial import, where there is no space on either side.

Binary operators must have a single space on both sides.
Unary operators must not be followed by a space.

### Method call style

Usually, parentheses should be used for actual parameters to a method call.
Expand Down Expand Up @@ -214,8 +176,6 @@ Procedure syntax must not be used.
Side-effect-free methods without formal parameters should be declared without `()`, unless either a) it overrides a method defined with `()` (such as `toString()`) or b) it implements a Java method in the Java libraries.

The signature of a method is technically a single line, and hence, if it has to be broken due to line length reasons, subsequent lines should be indented 4 spaces.
As a reminder, the line can be broken right after a `,` or a `(` (and not, for example, after `implicit` or `:`).
You should avoid breaking the line between the last parameter and the result type; going over the 80 characters limit is preferred in that case.

### `for` comprehensions

Expand Down
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,14 @@ in a single line, the commit message should be "Fix #xxx: Title of the ticket.".
If the commit is a small fix, the first line can be enough.
Otherwise, following the single line description should be a blank line
followed by details of the commit, in the form of free text, or bulleted list.

## Git blame

In `git blame`s, to ignore the commit that formatted all source files with
scalafmt, run the following:

```
git config blame.ignoreRevsFile .git-blame-ignore-revs
```

Note that this requires having Git 2.23+.
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ def Tasks = [
testAdapter$v/mimaReportBinaryIssues \
sbtPlugin/mimaReportBinaryIssues &&
sbt ++$scala scalastyleCheck &&
sbt ++$scala scalafmtCheckAll &&
sbt ++$scala ir$v/compile:doc \
linkerInterface$v/compile:doc linker$v/compile:doc \
testAdapter$v/compile:doc \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ trait CompatComponent {
def isTraitOrInterface: Boolean = self.isTrait || self.isInterface
}

implicit final class GlobalCompat(
self: CompatComponent.this.global.type) {
implicit final class GlobalCompat(self: CompatComponent.this.global.type) {

object originalOwner {
def getOrElse(sym: Symbol, orElse: => Symbol): Symbol = infiniteLoop()
Expand Down Expand Up @@ -79,8 +78,7 @@ trait CompatComponent {
// SAMFunction was introduced in 2.12 for LMF-capable SAM types

object SAMFunctionAttachCompatDef {
case class SAMFunction(samTp: Type, sam: Symbol, synthCls: Symbol)
extends PlainAttachment
case class SAMFunction(samTp: Type, sam: Symbol, synthCls: Symbol) extends PlainAttachment
}

object SAMFunctionAttachCompat {
Expand Down
27 changes: 13 additions & 14 deletions compiler/src/main/scala/org/scalajs/nscplugin/ExplicitInnerJS.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G)
private def isApplicableOwner(sym: Symbol): Boolean = {
!sym.isStaticOwner || (
sym.isModuleClass &&
sym.hasAnnotation(JSTypeAnnot) &&
!sym.hasAnnotation(JSNativeAnnotation)
sym.hasAnnotation(JSTypeAnnot) &&
!sym.hasAnnotation(JSNativeAnnotation)
)
}

Expand Down Expand Up @@ -169,12 +169,14 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G)
tp
} else {
def addAnnots(sym: Symbol, symForName: Symbol): Unit = {
symForName.getAnnotation(JSNameAnnotation).fold {
sym.addAnnotation(JSNameAnnotation,
Literal(Constant(jsInterop.defaultJSNameOf(symForName))))
} { annot =>
sym.addAnnotation(annot)
}
symForName
.getAnnotation(JSNameAnnotation)
.fold {
sym.addAnnotation(JSNameAnnotation,
Literal(Constant(jsInterop.defaultJSNameOf(symForName))))
} { annot =>
sym.addAnnotation(annot)
}
sym.addAnnotation(ExposedJSMemberAnnot)
}

Expand Down Expand Up @@ -234,8 +236,7 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G)
tp
}

class ExplicitInnerJSTransformer(unit: CompilationUnit)
extends TypingTransformer(unit) {
class ExplicitInnerJSTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {

/** Execute the whole transformation in the future, exiting this phase. */
override def transformUnit(unit: CompilationUnit): Unit = {
Expand Down Expand Up @@ -268,10 +269,8 @@ abstract class ExplicitInnerJS[G <: Global with Singleton](val global: G)
} else {
val parentTpe =
extractSuperTpeFromImpl(decl.asInstanceOf[ClassDef].impl)
val superClassCtor = gen.mkNullaryCall(
JSPackage_constructorOf, List(parentTpe))
gen.mkMethodCall(Runtime_createInnerJSClass,
List(clazzValue, superClassCtor))
val superClassCtor = gen.mkNullaryCall(JSPackage_constructorOf, List(parentTpe))
gen.mkMethodCall(Runtime_createInnerJSClass, List(clazzValue, superClassCtor))
}
}

Expand Down
44 changes: 16 additions & 28 deletions compiler/src/main/scala/org/scalajs/nscplugin/ExplicitLocalJS.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ import scala.collection.mutable
* its own JS class.
*/
abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
extends plugins.PluginComponent with Transform with TypingTransformers
with CompatComponent {
extends plugins.PluginComponent with Transform with TypingTransformers with CompatComponent {

val jsAddons: JSGlobalAddons {
val global: ExplicitLocalJS.this.global.type
Expand Down Expand Up @@ -179,8 +178,7 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
!isJSLambda
}

class ExplicitLocalJSTransformer(unit: CompilationUnit)
extends TypingTransformer(unit) {
class ExplicitLocalJSTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {

private val nestedObject2superClassTpe = mutable.Map.empty[Symbol, Type]
private val localClass2jsclassVal = mutable.Map.empty[Symbol, TermSymbol]
Expand Down Expand Up @@ -209,8 +207,7 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
decl match {
case ClassDef(_, _, _, impl)
if decl.symbol.isModuleClass && isInnerJSClassOrObject(decl.symbol) =>
nestedObject2superClassTpe(decl.symbol) =
extractSuperTpeFromImpl(impl)
nestedObject2superClassTpe(decl.symbol) = extractSuperTpeFromImpl(impl)
case _ =>
}
}
Expand Down Expand Up @@ -242,9 +239,8 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
val argss = ctor.tpe.paramss.map { params =>
List.fill(params.size)(gen.mkAttributedRef(Predef_???))
}
argss.tail.foldLeft(
global.NewFromConstructor(ctor, argss.head: _*))(
Apply(_, _))
argss.tail
.foldLeft(global.NewFromConstructor(ctor, argss.head: _*))(Apply(_, _))
}
typer.typed(ArrayValue(TypeTree(AnyRefTpe), elems))
}
Expand All @@ -271,10 +267,8 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
}
}

case ClassDef(_, _, _, impl)
if isLocalJSClassOrObject(stat.symbol) =>
nestedObject2superClassTpe(stat.symbol) =
extractSuperTpeFromImpl(impl)
case ClassDef(_, _, _, impl) if isLocalJSClassOrObject(stat.symbol) =>
nestedObject2superClassTpe(stat.symbol) = extractSuperTpeFromImpl(impl)
newStats += transform(stat)

case _ =>
Expand Down Expand Up @@ -312,15 +306,15 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
*/
case Apply(fun @ Select(sup: Super, _), _)
if !fun.symbol.isConstructor &&
isInnerOrLocalJSClass(sup.symbol.superClass) =>
isInnerOrLocalJSClass(sup.symbol.superClass) =>
wrapWithContextualJSClassValue(sup.symbol.superClass.tpe_*) {
super.transform(tree)
}

// Same for a super call with type parameters
case Apply(TypeApply(fun @ Select(sup: Super, _), _), _)
if !fun.symbol.isConstructor &&
isInnerOrLocalJSClass(sup.symbol.superClass) =>
isInnerOrLocalJSClass(sup.symbol.superClass) =>
wrapWithContextualJSClassValue(sup.symbol.superClass.tpe_*) {
super.transform(tree)
}
Expand All @@ -329,13 +323,12 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
case Apply(TypeApply(ctorOfTree, List(tpeArg)), Nil)
if ctorOfTree.symbol == JSPackage_constructorOf =>
val newTpeArg = transform(tpeArg)
gen.mkAttributedCast(genJSConstructorOf(tree, newTpeArg.tpe),
JSDynamicClass.tpe)
gen.mkAttributedCast(genJSConstructorOf(tree, newTpeArg.tpe), JSDynamicClass.tpe)

// Translate x.isInstanceOf[T] for inner and local JS classes
case Apply(TypeApply(fun @ Select(obj, _), List(tpeArg)), Nil)
if fun.symbol == Any_isInstanceOf &&
isInnerOrLocalJSClass(tpeArg.tpe.typeSymbol) =>
isInnerOrLocalJSClass(tpeArg.tpe.typeSymbol) =>
val newObj = transform(obj)
val newTpeArg = transform(tpeArg)
val jsCtorOf = genJSConstructorOf(tree, newTpeArg.tpe)
Expand All @@ -358,7 +351,7 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
// This should not have passed the checks in PrepJSInterop
assert(!clazz.isTrait && !clazz.isModuleClass,
s"non-trait class type required but $tpe found for " +
s"genJSConstructorOf at ${tree.pos}")
s"genJSConstructorOf at ${tree.pos}")

localTyper.typed {
atPos(tree.pos) {
Expand All @@ -372,8 +365,7 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
val qual = gen.mkAttributedQualifier(prefix)
gen.mkAttributedSelect(qual, jsclassAccessorFor(clazz))
} else {
reporter.error(tree.pos,
s"stable reference to a JS class required but $tpe found")
reporter.error(tree.pos, s"stable reference to a JS class required but $tpe found")
gen.mkAttributedRef(Predef_???)
}
} else if (isLocalJSClass(clazz)) {
Expand All @@ -390,20 +382,16 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
}
}

private def wrapWithContextualJSClassValue(jsClassType: Type)(
tree: Tree): Tree = {
private def wrapWithContextualJSClassValue(jsClassType: Type)(tree: Tree): Tree = {
wrapWithContextualJSClassValue(genJSConstructorOf(tree, jsClassType)) {
tree
}
}

private def wrapWithContextualJSClassValue(jsClassValue: Tree)(
tree: Tree): Tree = {
private def wrapWithContextualJSClassValue(jsClassValue: Tree)(tree: Tree): Tree = {
atPos(tree.pos) {
localTyper.typed {
gen.mkMethodCall(
Runtime_withContextualJSClassValue,
List(tree.tpe),
gen.mkMethodCall(Runtime_withContextualJSClassValue, List(tree.tpe),
List(jsClassValue, tree))
}
}
Expand Down
Loading