Skip to content

Commit

Permalink
Merge pull request #1376 from xeno-by/topic/scalalib
Browse files Browse the repository at this point in the history
More assorted fixes
  • Loading branch information
olafurpg committed Feb 28, 2018
2 parents 32de4fa + 452b259 commit 5235bd2
Show file tree
Hide file tree
Showing 9 changed files with 557 additions and 32 deletions.
1 change: 1 addition & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ project.excludeFilters = [
contrib/package.scala
scalac-plugin.xml
SemanticdbAnalyzer.scala
Generator.scala
.java
.proto
.md
Expand Down
36 changes: 32 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,36 @@ lazy val metapJVM = metap.jvm
lazy val metapJS = metap.js
lazy val metapNative = metap.native

lazy val scalalibGen = project
.in(file("semanticdb/scalalib/gen"))
.settings(
sharedSettings,
nonPublishableSettings,
description := "SemanticDB generator for scalalib",
mainClass := Some("scala.meta.internal.scalalib.Generator")
)
// NOTE: workaround for https://github.com/sbt/sbt-core-next/issues/8
.disablePlugins(BackgroundRunPlugin)
.enablePlugins(BuildInfoPlugin)
.dependsOn(metacp)

lazy val scalalib = project
.in(file("semanticdb/scalalib/lib"))
.settings(
publishableSettings,
compatibilityPolicyViolation("https://github.com/scalameta/scalameta/issues/1377"),
description := "SemanticDB payloads for definitions that are missing from scala-library.jar",
resourceGenerators.in(Compile) += Def.taskDyn {
val outDir = resourceManaged.in(Compile).value
Def.task {
run.in(scalalibGen, Compile).toTask(s" $outDir").value
val semanticdbs = (outDir ** "*.semanticdb").get
val semanticidx = (outDir ** "*.semanticidx").get
semanticdbs ++ semanticidx
}
}.taskValue
)

/** ======================== LANGMETA ======================== **/

lazy val langmeta = crossProject(JSPlatform, JVMPlatform)
Expand Down Expand Up @@ -436,6 +466,8 @@ lazy val tests = crossProject(JSPlatform, JVMPlatform)
fullClasspath.in(Test) := {
val semanticdbScalacJar = Keys.`package`.in(semanticdbScalacPlugin, Compile).value.getAbsolutePath
sys.props("sbt.paths.semanticdb-scalac-plugin.compile.jar") = semanticdbScalacJar
val scalalibJar = Keys.`package`.in(scalalib, Compile).value.getAbsolutePath
sys.props("sbt.paths.scalalib.compile.jar") = scalalibJar
fullClasspath.in(Test).value
},
buildInfoKeys := Seq[BuildInfoKey](
Expand Down Expand Up @@ -553,10 +585,6 @@ lazy val sharedSettings = Def.settings(
crossScalaVersions := LanguageVersions,
organization := "org.scalameta",
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full),
scalacOptions ++= {
if (scalaVersion.value.startsWith("2.10")) Nil
else Seq("-target:jvm-1.8")
},
scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked"),
scalacOptions.in(Compile, doc) ++= Seq("-skip-packages", ""),
scalacOptions.in(Compile, doc) ++= Seq("-implicits", "-implicits-hide:."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ class Main(settings: Settings, out: PrintStream, err: PrintStream) {
}

private val symCache = new WeakHashMap[TextDocument, immutable.Map[String, SymbolInformation]]
private def pprint(sym: String, role: Role, doc: TextDocument): Unit = {
private def pprint(sym: String, role: Role, doc: TextDocument): List[String] = {
val buf = List.newBuilder[String]
buf += sym
var infos = symCache.get(doc)
if (infos == null) {
infos = doc.symbols.map(info => (info.symbol, info)).toMap
Expand All @@ -143,8 +145,14 @@ class Main(settings: Settings, out: PrintStream, err: PrintStream) {
case DEFINITION =>
// NOTE: This mode is only used to print symbols that are part
// of complex types, so we don't need to fully support all symbols here.
rep(info.annotations, " ", " ")(pprint(_, doc))
opt(info.accessibility)(pprint(_, doc))
rep(info.annotations, " ", " ") { ann =>
val syms = pprint(ann, doc)
syms.foreach(buf.+=)
}
opt(info.accessibility) { acc =>
if (acc.symbol.nonEmpty) buf += acc.symbol
pprint(acc, doc)
}
if ((info.properties & COVARIANT.value) != 0) out.print("+")
if ((info.properties & CONTRAVARIANT.value) != 0) out.print("-")
info.kind match {
Expand All @@ -166,11 +174,14 @@ class Main(settings: Settings, out: PrintStream, err: PrintStream) {
out.print(" ")
case _ =>
out.print("<?>")
return
return buf.result
}
info.tpe match {
case Some(tpe) => pprint(tpe, doc)
case None => out.print("<?>")
case Some(tpe) =>
val syms = pprint(tpe, doc)
syms.foreach(buf.+=)
case None =>
out.print("<?>")
}
case UNKNOWN_ROLE | Role.Unrecognized(_) =>
()
Expand All @@ -194,17 +205,18 @@ class Main(settings: Settings, out: PrintStream, err: PrintStream) {
case UNKNOWN_ROLE | Role.Unrecognized(_) => ()
}
}
buf.result
}

private def pprint(tpe: Type, doc: TextDocument): List[String] = {
val buf = List.newBuilder[String]
def ref(sym: String): Unit = {
buf += sym
pprint(sym, REFERENCE, doc)
val syms = pprint(sym, REFERENCE, doc)
syms.foreach(buf.+=)
}
def defn(sym: String): Unit = {
buf += sym
pprint(sym, DEFINITION, doc)
val syms = pprint(sym, DEFINITION, doc)
syms.foreach(buf.+=)
}
def prefix(tpe: Type): Unit = {
tpe.tag match {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package scala.meta.internal.scalalib

import java.nio.file._
import scala.meta.internal.metacp._
import scala.meta.internal.{semanticdb3 => s}
import scala.meta.internal.semanticdb3.Accessibility.{Tag => a}
import scala.meta.internal.semanticdb3.SymbolInformation.{Kind => k}
import scala.meta.internal.semanticdb3.SymbolInformation.{Property => p}
import scala.meta.internal.semanticdb3.Type.{Tag => t}

object Generator {
def main(args: Array[String]): Unit = {
val Array(outDir) = args
val settings = Settings(d = outDir)
val index = new Index
val synthetics = List(any, anyVal, anyRef, nothing)
synthetics.foreach { infos =>
index.append(infos)
infos.save(settings)
}
index.save(settings)
}

def any: ToplevelInfos = {
val symbols = List(
builtinMethod("Any", List(p.ABSTRACT), "equals", Nil, List("that" -> "_root_.scala.Any#"), "_root_.scala.Boolean#"),
builtinMethod("Any", List(p.FINAL), "==", Nil, List("that" -> "_root_.scala.Any#"), "_root_.scala.Boolean#"),
builtinMethod("Any", List(p.FINAL), "!=", Nil, List("that" -> "_root_.scala.Any#"), "_root_.scala.Boolean#"),
builtinMethod("Any", List(p.ABSTRACT), "hashCode", Nil, Nil, "_root_.scala.Int#"),
builtinMethod("Any", List(p.FINAL), "##", Nil, Nil, "_root_.scala.Int#"),
builtinMethod("Any", List(p.ABSTRACT), "toString", Nil, Nil, "_root_.java.lang.String#"),
// TODO: Return type of getClass can't be expressed in the SemanticDB type system.
// The method is special-cased in both the Java and Scala compilers, so we'll slack a little bit too for the time being.
builtinMethod("Any", List(p.FINAL), "getClass", Nil, Nil, "_root_.java.lang.Class#"),
builtinMethod("Any", List(p.FINAL), "isInstanceOf", List("A"), Nil, "_root_.scala.Boolean#"),
builtinMethod("Any", List(p.FINAL), "asInstanceOf", List("A"), Nil, "_root_.scala.Any.asInstanceOf(A).[A]"))
builtinClass(List(p.ABSTRACT), "Any", Nil, symbols.flatten)
}

def anyVal: ToplevelInfos = {
builtinClass(List(p.ABSTRACT), "AnyVal", List("_root_.scala.Any#"), Nil)
}

def anyRef: ToplevelInfos = {
// TODO: We're not including methods from java.lang.Object here.
// The relationship between AnyRef and Object needs more thinking.
val symbols = List(
builtinMethod("AnyRef", List(p.FINAL), "eq", Nil, List("that" -> "_root_.scala.AnyRef#"), "_root_.scala.Boolean#"),
builtinMethod("AnyRef", List(p.FINAL), "ne", Nil, List("that" -> "_root_.scala.AnyRef#"), "_root_.scala.Boolean#"),
builtinMethod("AnyRef", List(p.FINAL), "synchronized", List("T"), List("body" -> "_root_.scala.AnyRef.synchronized(T).[T]"), "_root_.scala.AnyRef.synchronized(T).[T]"))
builtinClass(Nil, "AnyRef", List("_root_.scala.Any#"), symbols.flatten)
}

def nothing: ToplevelInfos = {
builtinClass(List(p.ABSTRACT, p.FINAL), "Nothing", List("_root_.scala.Any#"), Nil)
}

private def builtinClass(
props: List[s.SymbolInformation.Property],
name: String,
bases: List[String],
symbols: List[s.SymbolInformation]): ToplevelInfos = {
val parents = bases.map { base =>
s.Type(tag = t.TYPE_REF, typeRef = Some(s.TypeRef(None, base, Nil)))
}
val symbol = "_root_.scala." + name + "#"
val builtinTpe = s.Type(tag = t.TYPE_REF, typeRef = Some(s.TypeRef(None, symbol, Nil)))
val ctorSig = s.MethodType(Nil, List(s.MethodType.ParameterList(Nil)), Some(builtinTpe))
val ctor = s.SymbolInformation(
symbol = symbol + "`<init>`().",
language = Some(builtinLanguage),
kind = k.PRIMARY_CONSTRUCTOR,
name = "<init>",
tpe = Some(s.Type(tag = t.METHOD_TYPE, methodType = Some(ctorSig))),
accessibility = Some(s.Accessibility(a.PUBLIC)),
owner = symbol
)
val builtinSig = {
val decls = symbols.filter(_.kind == k.DEF)
val tpe = s.ClassInfoType(Nil, parents, ctor.symbol +: decls.map(_.symbol))
s.Type(tag = t.CLASS_INFO_TYPE, classInfoType = Some(tpe))
}
val builtin = s.SymbolInformation(
symbol = symbol,
language = Some(builtinLanguage),
kind = k.CLASS,
properties = props.foldLeft(0)((acc, prop) => acc | prop.value),
name = name,
tpe = Some(builtinSig),
accessibility = Some(s.Accessibility(a.PUBLIC)),
owner = "_root_.scala."
)
val syntheticBase = Paths.get(".")
val syntheticPath = syntheticBase.resolve("scala/" + name + ".class")
val syntheticClassfile = ToplevelClassfile(syntheticBase, syntheticPath, null)
ToplevelInfos(syntheticClassfile, List(builtin), ctor +: symbols)
}

def builtinMethod(
className: String,
props: List[s.SymbolInformation.Property],
methodName: String,
tparamDsls: List[String],
paramDsls: List[(String, String)],
retTpeSymbol: String): List[s.SymbolInformation] = {
val classSymbol = "_root_.scala." + className + "#"
val encodedMethodName = {
if (Character.isJavaIdentifierStart(methodName.head)) methodName
else "`" + methodName + "`"
}
val disambiguator = {
val paramTypeDescriptors = paramDsls.map(_._2).map { symbol =>
// TODO: It would be nice to have a symbol parser in semanticdb3.
val _ :+ last = symbol.split("[\\.|#]").toList
val last1 = last.stripPrefix("(").stripPrefix("[")
val last2 = last1.stripSuffix(")").stripSuffix("]").stripSuffix("#")
last2.stripPrefix("`").stripSuffix("`")
}
paramTypeDescriptors.mkString(",")
}
val methodSymbol = classSymbol + encodedMethodName + "(" + disambiguator + ")."
val tparams = tparamDsls.map { tparamName =>
val tparamSymbol = methodSymbol + "[" + tparamName + "]"
val tparamSig = s.Type(tag = t.TYPE_TYPE, typeType = Some(s.TypeType()))
s.SymbolInformation(
symbol = tparamSymbol,
language = Some(builtinLanguage),
kind = k.TYPE_PARAMETER,
properties = 0,
name = tparamName,
tpe = Some(tparamSig),
accessibility = None,
owner = methodSymbol)
}
val params = paramDsls.map {
case (paramName, paramTpeSymbol) =>
val paramSymbol = methodSymbol + "(" + paramName + ")"
val paramSig = s.Type(tag = t.TYPE_REF, typeRef = Some(s.TypeRef(None, paramTpeSymbol, Nil)))
s.SymbolInformation(
symbol = paramSymbol,
language = Some(builtinLanguage),
kind = k.PARAMETER,
properties = 0,
name = paramName,
tpe = Some(paramSig),
owner = methodSymbol)
}
val methodSig = {
val paramSymbols = params.map(_.symbol)
val returnType = s.Type(tag = t.TYPE_REF, typeRef = Some(s.TypeRef(None, retTpeSymbol, Nil)))
val methodType = s.MethodType(Nil, List(s.MethodType.ParameterList(paramSymbols)), Some(returnType))
s.Type(tag = t.METHOD_TYPE, methodType = Some(methodType))
}
val method = s.SymbolInformation(
symbol = methodSymbol,
language = Some(builtinLanguage),
kind = k.DEF,
properties = props.foldLeft(0)((acc, prop) => acc | prop.value),
name = methodName,
tpe = Some(methodSig),
accessibility = Some(s.Accessibility(a.PUBLIC)),
owner = classSymbol)
List(method) ++ tparams ++ params
}

def builtinLanguage: s.Language = {
s.Language("Scala")
}
}

0 comments on commit 5235bd2

Please sign in to comment.