Skip to content

[WIP] consolidate entity serialization #2074

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

Merged
merged 2 commits into from
Mar 15, 2017

Conversation

vlthr
Copy link
Contributor

@vlthr vlthr commented Mar 10, 2017

Related issue: #1950

Refactored the various implicit classes that handled serialization of entities into a single class. Entities are now serialized by unioning together the result of serializing their individual subtypes/inherited traits.

I also added a test to statically ensure that the implicit conversions work, and that the serializations are successful (but not whether the result is correct).

Review by @felixmulder

- refactored JavaEntity to be DRY with respect to repeating the
serialization of fields shared by many types of entities
- added tests
Copy link
Contributor

@felixmulder felixmulder left a comment

Choose a reason for hiding this comment

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

Great job, pretty much exactly what we want - as well as finding a few spots where we missed fields. Just a few nitpicks to fix and then we can merge 🎉

case ent: Val => ent.asJava
case ent: TypeAlias => ent.asJava
case _ => Map.empty.asJava
private def parseEntity(ent: Entity, extras: Map[String, _]): JMap[String, _] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we still using the extras or can we kill that parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, looks like none of the current callers actually pass any extras. I'll remove it!

case _ => Map.empty.asJava
private def parseEntity(ent: Entity, extras: Map[String, _]): JMap[String, _] = {
val entity = Map(
"kind" -> ent.kind,
Copy link
Contributor

Choose a reason for hiding this comment

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

let's kill vertical alignment now that we're writing code that may change:

+    val entity = Map(
+      "kind" -> ent.kind,

)
case _ => Map.empty
}
(entity ++ members ++ superTypes ++ modifiers ++ typeParams ++ constructors ++ companion ++ returnValue ++ implicitlyAddedEntity ++ typeAlias ++ trt ++ df).asJava
Copy link
Contributor

Choose a reason for hiding this comment

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

let's make this a:

{
  entity ++
  members ++
  ...
  df
}.asJava

import model.references.{ConstantReference, TypeReference, NoLink}
import model.comment.Comment
import dotty.tools.dotc.core.Symbols.NoSymbol
import _root_.java.util.{Optional => JOptional, Map => JMap}
Copy link
Contributor

Choose a reason for hiding this comment

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

we can get rid of _root_ now, it was needed previously for historic (i.e. dumb) reasons.

import model.JavaConverters._

@Test def entityConversions = {
trait TestEntity extends Entity {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to create all these intermediate subclasses of entities.scala - see below

trait TestTrait extends TestModifiers with TestTypeParams with TestSuperTypes with TestMembers with TestCompanion {
def traitParams = List()
}
val ent = new TestEntity {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of extending and instantiating, just do:

val ent = new Entity {
  val symbol = ...
  val name = ...
}
...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a way to do this without repeating the fields of Entity (and some others) in each anonymous trait instantiation? I ended up making a bunch of TestX traits implementing each entity trait just to avoid repeating common fields, but I agree that it does feel a little verbose.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe, but there are only a few concrete instances of the entities, eg:

  • Package
  • CaseClass
  • Class
  • Object
  • Trait
  • Def
  • Val
  • TypeAlias

See internal.scala. No need to instantiate Entity with Members et al

val implicitlyAddedEntity_serialized = implicitlyAddedEntity.asJava()
val typeAlias_serialized = typeAlias.asJava()
val def_serialized = df.asJava()
val trait_serialized = trt.asJava()
Copy link
Contributor

Choose a reason for hiding this comment

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

here we're not really testing the result of the conversion, just that the conversions apply to all entities, we could be doing:

val df = new Def { val name = "foo"; ... }.asJava
assertEquals("foo", df.get("name"))

100% test coverage here might be overkill, but perhaps testing a that (pseudo code):

(Foo extends Bar).asJava.get("superTypes").asScala.toList == List(Foo)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, the tests are pretty weak as they are. I'll add some checking of the actual results - especially the low-hanging fruit.

@felixmulder
Copy link
Contributor

Feel free to include feedback as a second commit, no probs

vlthr added a commit to vlthr/dotty that referenced this pull request Mar 14, 2017
- Added tests to ensure that the results of serialization match their
  pre-serialization values.
- Removed unused parameter `extras` from Entity.asJava() implicit methods.
- Removed _root_ imports
- Fixed several code style issues
@vlthr
Copy link
Contributor Author

vlthr commented Mar 14, 2017

Contents of the second commit:

  • Added tests to ensure that the results of serialization match their
    pre-serialization values.
  • Removed unused parameter extras from Entity.asJava() implicit methods.
  • Removed _root_ imports
  • Fixed several code style issues

The tests are fairly comprehensive (with the most notable omission being no testing of Comment serialization), but at the same time they are fairly repetitive, which I'm worried could lead to them being out of sync with the serialization code in the future. What are your thoughts on using reflection or macros to DRY up the test code? Too much unnecessary complexity?

assertSerializedCorrectly(pkg, pkg.asJava())
}

def assertEach[E, A, C[E] <: Seq[E]](expected: C[E], serialized: Any, pairwiseAssertion: (E, A) => Unit) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we separate this into two parameter lists, do we still need the type annotation at call site?

Copy link
Contributor

Choose a reason for hiding this comment

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

e.g:

def map[A,B](a: A, f: A => B) = f(a)
map(1, x => x * 2) // does not compile, needs type annotation

def map2[A,B](a: A)(f: A => B) = f(a)
map(1)(x => x * 2) // compiles

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, looks like we don't! Removing the type annotations significantly improves the readability of the calls.

Can I amend the commit, or would that confuse the system?

@felixmulder
Copy link
Contributor

Well it looks like you had fun with the testing ^^

I'll have a look in the morning, for now just one feedback point already posted.

Cheers,
Felix

@felixmulder
Copy link
Contributor

felixmulder commented Mar 14, 2017 via email

@vlthr vlthr force-pushed the dottydoc-entity-implicits branch from 28f7823 to 6c92428 Compare March 14, 2017 20:46
- Added tests to ensure that the results of serialization match their
  pre-serialization values.
- Removed unused parameter `extras` from Entity.asJava() implicit methods.
- Removed _root_ imports
- Fixed several code style issues
Copy link
Contributor

@felixmulder felixmulder left a comment

Choose a reason for hiding this comment

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

LGTM

assertSameSeq(expected.path, actual.get("path"))
assertEquals(expected.hasShortenedDocstring, actual.get("hasShortenedDocstring"))
// Only test if a comment is present
expected.comment match {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is fine, but the idiomatic way to do this would be:

expected.comment.foreach(assertNotEquals(null, actual.get("comment")))

@felixmulder felixmulder merged commit c321653 into scala:master Mar 15, 2017
@felixmulder
Copy link
Contributor

Thanks @vlthr! Happy to get this merged 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants