Repository: scalameta/scalameta (scala)

Issue:
"failed to generate semanticdb for" error for Scala 3 syntax in Scala 2 (with `-Xsource:3` enabled)
Minimal repro:

### build.sbt:
```scala
inThisBuild(
  List(
    scalaVersion := "2.13.6",
    semanticdbEnabled := true,
    scalacOptions += "-Xsource:3"
  )
)
```

### Main.scala
```scala
object Hello {
  val x = "hello"
     ++ "world"
}
```

This syntax compiles fine on Scala 2 with `-Xsource:3` enabled, but when also enabling SemanticDB, results in the following error:
```
.../Main.scala:8: error: ; expected but string constant found
    ++ "world"
       ^
```

### Full stack trace:
<details>

```
scalac: failed to generate semanticdb for /Users/hmemcpy/git/sbt-playground/src/main/scala/app/Main.scala:
/Users/hmemcpy/git/sbt-playground/src/main/scala/app/Main.scala:8: error: ; expected but string constant found
    ++ "world"
       ^
	at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:16)
	at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:16)
	at scala.meta.internal.parsers.Reporter$$anon$1.syntaxError(Reporter.scala:22)
	at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:17)
	at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:17)
	at scala.meta.internal.parsers.Reporter$$anon$1.syntaxError(Reporter.scala:22)
	at scala.meta.internal.parsers.ScalametaParser.syntaxErrorExpected(ScalametaParser.scala:941)
	at scala.meta.internal.parsers.ScalametaParser.accept(ScalametaParser.scala:947)
	at scala.meta.internal.parsers.ScalametaParser.acceptStatSep(ScalametaParser.scala:957)
	at scala.meta.internal.parsers.ScalametaParser.acceptStatSepOpt(ScalametaParser.scala:961)
	at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5060)
	at scala.meta.internal.parsers.ScalametaParser.templateStats(ScalametaParser.scala:5130)
	at scala.meta.internal.parsers.ScalametaParser.templateStatSeq(ScalametaParser.scala:5110)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateBody$1(ScalametaParser.scala:4945)
	at scala.meta.internal.parsers.ScalametaParser.inBraces(ScalametaParser.scala:800)
	at scala.meta.internal.parsers.ScalametaParser.templateBody(ScalametaParser.scala:4945)
	at scala.meta.internal.parsers.ScalametaParser.templateBodyOpt(ScalametaParser.scala:4953)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateOpt$1(ScalametaParser.scala:4938)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.templateOpt(ScalametaParser.scala:4923)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$objectDef$1(ScalametaParser.scala:4682)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.objectDef(ScalametaParser.scala:4674)
	at scala.meta.internal.parsers.ScalametaParser.tmplDef(ScalametaParser.scala:4558)
	at scala.meta.internal.parsers.ScalametaParser.topLevelTmplDef(ScalametaParser.scala:4540)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat$1.applyOrElse(ScalametaParser.scala:5079)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat$1.applyOrElse(ScalametaParser.scala:5067)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
	at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5057)
	at scala.meta.internal.parsers.ScalametaParser.topStatSeq(ScalametaParser.scala:5066)
	at scala.meta.internal.parsers.ScalametaParser.bracelessPackageStats$1(ScalametaParser.scala:5289)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$batchSource$12(ScalametaParser.scala:5295)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$batchSource$1(ScalametaParser.scala:5295)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.batchSource(ScalametaParser.scala:5256)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$source$1(ScalametaParser.scala:5239)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.source(ScalametaParser.scala:5238)
	at scala.meta.internal.parsers.ScalametaParser.entrypointSource(ScalametaParser.scala:5244)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$parseSource$2(ScalametaParser.scala:143)
	at scala.meta.internal.parsers.ScalametaParser.parseRule(ScalametaParser.scala:53)
	at scala.meta.internal.parsers.ScalametaParser.parseSource(ScalametaParser.scala:143)
	at scala.meta.parsers.Parse$.$anonfun$parseSource$1(Parse.scala:29)
	at scala.meta.internal.parsers.ScalametaParser$$anon$264.apply(ScalametaParser.scala:5308)
	at scala.meta.parsers.Api$XtensionParseDialectInput.parse(Api.scala:25)
	at scala.meta.internal.semanticdb.scalac.ParseOps$XtensionCompilationUnitSource.toSource(ParseOps.scala:17)
	at scala.meta.internal.semanticdb.scalac.TextDocumentOps$XtensionCompilationUnitDocument.toTextDocument(TextDocumentOps.scala:201)
	at scala.meta.internal.semanticdb.scalac.TextDocumentOps$XtensionCompilationUnitDocument.toTextDocument(TextDocumentOps.scala:52)
	at scala.meta.internal.semanticdb.scalac.SemanticdbPipeline$SemanticdbTyperComponent$ComputeSemanticdbPhase.saveSemanticdbForCompilationUnit(SemanticdbPipeline.scala:60)
	at scala.meta.internal.semanticdb.scalac.SemanticdbPipeline$SemanticdbTyperComponent$ComputeSemanticdbPhase.apply(SemanticdbPipeline.scala:67)
	at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:454)
	at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:401)
	at scala.meta.internal.semanticdb.scalac.SemanticdbPipeline$SemanticdbTyperComponent$ComputeSemanticdbPhase.run(SemanticdbPipeline.scala:77)
	at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1519)
	at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1503)
	at scala.tools.nsc.Global$Run.compileSources(Global.scala:1495)
	at scala.tools.nsc.Global$Run.compileFiles(Global.scala:1609)
	at xsbt.CachedCompiler0.run(CompilerBridge.scala:163)
	at xsbt.CachedCompiler0.run(CompilerBridge.scala:134)
	at xsbt.CompilerBridge.run(CompilerBridge.scala:39)
	at sbt.internal.inc.AnalyzingCompiler.compile(AnalyzingCompiler.scala:91)
	at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:57)
	at org.jetbrains.jps.incremental.scala.local.LocalServer.doCompile(LocalServer.scala:52)
	at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:30)
	at org.jetbrains.jps.incremental.scala.remote.Main$.compileLogic(Main.scala:207)
	at org.jetbrains.jps.incremental.scala.remote.Main$.$anonfun$handleCommand$1(Main.scala:190)
	at org.jetbrains.jps.incremental.scala.remote.Main$.decorated$1(Main.scala:180)
	at org.jetbrains.jps.incremental.scala.remote.Main$.handleCommand(Main.scala:187)
	at org.jetbrains.jps.incremental.scala.remote.Main$.serverLogic(Main.scala:163)
	at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:103)
	at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at com.martiansoftware.nailgun.NGSession.run(NGSession.java:319)
```
</details>

Pull request:
Original PR in compiler - https://github.com/scala/scala/pull/8419
Fixes #2739.

Patch:
diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
index 21b4c9b35e..4a1eeadb7a 100644
--- a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
+++ b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
@@ -150,7 +150,9 @@ final class Dialect private (
     // import a.b.c.{ given, _} used for -X:source3
     val allowGivenImports: Boolean,
     // Scala 3 uses proper precedence rules for infix types, unlike Scala 2
-    val useInfixTypePrecedence: Boolean
+    val useInfixTypePrecedence: Boolean,
+    // Scala213Source3 and Scala3 allow infix operator being placed after nl
+    val allowInfixOperatorAfterNL: Boolean
 ) extends Product with Serializable {
 
   // NOTE(olafur) checklist for adding a new dialect field in a binary compatible way:
@@ -239,7 +241,8 @@ final class Dialect private (
       allowPlusMinusUnderscoreAsIdent = false,
       allowPlusMinusUnderscoreAsPlaceholder = false,
       allowGivenImports = false,
-      useInfixTypePrecedence = false
+      useInfixTypePrecedence = false,
+      allowInfixOperatorAfterNL = false
       // NOTE(olafur): declare the default value for new fields above this comment.
     )
   }
@@ -429,6 +432,10 @@ final class Dialect private (
     privateCopy(useInfixTypePrecedence = newValue)
   }
 
+  def withAllowInfixOperatorAfterNL(newValue: Boolean): Dialect = {
+    privateCopy(allowInfixOperatorAfterNL = newValue)
+  }
+
   // NOTE(olafur): add the next `withX()` method above this comment. Please try
   // to use consistent formatting, use `newValue` as the parameter name and wrap
   // the body inside curly braces.
@@ -491,8 +498,8 @@ final class Dialect private (
       allowPlusMinusUnderscoreAsIdent: Boolean = this.allowPlusMinusUnderscoreAsIdent,
       allowPlusMinusUnderscoreAsPlaceholder: Boolean = this.allowPlusMinusUnderscoreAsPlaceholder,
       allowGivenImports: Boolean = this.allowGivenImports,
-      useInfixTypePrecedence: Boolean = this.useInfixTypePrecedence
-
+      useInfixTypePrecedence: Boolean = this.useInfixTypePrecedence,
+      allowInfixOperatorAfterNL: Boolean = this.allowInfixOperatorAfterNL
       // NOTE(olafur): add the next parameter above this comment.
   ): Dialect = {
     new Dialect(
@@ -552,7 +559,8 @@ final class Dialect private (
       allowPlusMinusUnderscoreAsIdent,
       allowPlusMinusUnderscoreAsPlaceholder,
       allowGivenImports,
-      useInfixTypePrecedence
+      useInfixTypePrecedence,
+      allowInfixOperatorAfterNL
       // NOTE(olafur): add the next argument above this comment.
     )
   }
diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
index ae60af29c3..485239891b 100644
--- a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
+++ b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
@@ -65,6 +65,7 @@ package object dialects {
     .withAllowPostfixStarVarargSplices(true)
     .withAllowPlusMinusUnderscoreAsIdent(true)
     .withAllowGivenImports(true)
+    .withAllowInfixOperatorAfterNL(true)
 
   /**
    * Dialect starting with Scala 2.12.14 for `-Xsource:3` option
@@ -145,6 +146,7 @@ package object dialects {
     .withAllowDoWhile(false)
     .withAllowPlusMinusUnderscoreAsPlaceholder(true)
     .withUseInfixTypePrecedence(true)
+    .withAllowInfixOperatorAfterNL(true)
 
   @deprecated("Use Scala3 instead", "4.4.2")
   implicit val Dotty = Scala3
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
index 07ecf90986..3f410211f6 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
@@ -360,7 +360,7 @@ private[parsers] class LazyTokenIterator private (
           case _: RegionIndent | _: RegionIndentEnum => true
           case x: RegionParen => x.canProduceLF
           case _ => false
-        }
+        } && !next.isLeadingInfixOperator
       }
 
       def getIfCanProduceLF =
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
index efc972a6dd..0bf847247c 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
@@ -106,7 +106,7 @@ class ScannerTokens(tokens: Tokens, input: Input)(implicit dialect: Dialect) {
       @inline
       def strictNext: Token = getStrictAfterSafe(nextSafe)
 
-      def isLeadingInfixOperator: Boolean = dialect.allowSignificantIndentation && {
+      def isLeadingInfixOperator: Boolean = dialect.allowInfixOperatorAfterNL && {
         val text = token.text
         @tailrec
         def iter(idx: Int, nonEmpty: Boolean): Boolean = {


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/Scala213Suite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/Scala213Suite.scala
index 54db44d154..a761059f31 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/Scala213Suite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/Scala213Suite.scala
@@ -1,6 +1,8 @@
 package scala.meta.tests
 package parsers
 
+import scala.meta.Dialect
+import scala.meta.dialects
 import scala.meta.dialects.Scala213
 
 class Scala213Suite extends ParseSuite {
@@ -78,9 +80,18 @@ class Scala213Suite extends ParseSuite {
     runAssert("try ()", """Term.Try(Lit.Unit(()), Nil, None)""")
   }
 
-  private def runAssert(code: String, expected: String): Unit = {
+  test("infix-at-line-start") {
+    runAssert(
+      """|val x = "hello"
+         |  ++ "world"
+         |""".stripMargin,
+      """Defn.Val(Nil, List(Pat.Var(Term.Name("x"))), None, Term.ApplyInfix(Lit.String("hello"), Term.Name("++"), Nil, List(Lit.String("world"))))"""
+    )(dialects.Scala213Source3)
+  }
+
+  private def runAssert(code: String, expected: String)(implicit d: Dialect): Unit = {
     assertNoDiff(
-      templStat(code).structure,
+      templStat(code)(d).structure,
       expected
     )
   }