Add a pending test that shows a problem with instability of representing
self variables. This test covers the bug described in #2504.
In order to test API representation of a class declared either in source
file or unpickled from a class file, ScalaCompilerForUnitTesting has been
extended to extract APIs from multiple compiler instances sharing a
classpath.
The previous logic was wrong and included all members of traits in their
API hash, which degraded the performance of the incremental compiler.
This commit changes this logic so that only the non-public members of
traits are included into their API hash.
Fixes#2436
Add a new field in `Source` that indicates whether this `Source`
represents a package object. This field is used in the incrmental
compiler to perform recompilation of package objects rather than relying
on the file name being `package.scala`.
Mark pending test `source-dependencies/package-object-name` as passing.
Motivated because we want to make it more robust & configurable.
Original motivation was to diagnose a cyclic type representation,
likely due to an f-bounded existential type, as illustrated by the following:
```
class Dep {
// The API representation for `bla`'s result type contains a cycle
// (an existential's type variable's bound is the existential type itself)
// This results in a stack overflow while showing the API diff.
// Note that the actual result type in the compiler is not cyclic
// (the f-bounded existential for Comparable is truncated)
def bla(c: Boolean) = if (c) new Value else "bla"
}
class Value extends java.lang.Comparable[Value] { def compareTo(that: Value): Int = 1 }
```
Limit nesting (`-Dsbt.inc.apidiff.depth=N`, where N defaults to `2`),
and number of declarations shown for a class/structural type
(via `sbt.inc.apidiff.decls`, which defaults to `0` -- no limit).
Limiting nesting is crucial in keeping the size of api diffs of large programs
within a reasonable amount of RAM...
For example, compiling the Scala library, the API diff with nesting at `4`
exhausts 4G of RAM...
The NameHashing classes assumed that extracted API data structure have
private members filtered out already. That assumption was wrong and
resulted in bug #2324.
We fix the bug by simply reusing the same logic as used by SameAPI class.
Fixes#2324.
Add a pending test that shows a bug in calculation of name hashes.
By design, only non-private members should contribute to name hashes. The
test shows that name hashes are calcuclated for strictly private members
too.
During compilation, scalac generates getters and setters for the fields
of traits, regardless of their access modifiers. Therefore, when a
field of a trait is modified, all its implementors must be recompiled to
take these changes into account.
Private fields of traits were not included in the API hash of traits,
and their implementors were thus not recompiled when modified.
This commit changes the way the API hash is computed for traits only, so
that the generated hash includes the private members of traits.
Fixessbt/sbt#2155
When dealing with Java APIs (Java reflection in case of ClassToAPI), one
should always go through Option.apply that performs null check.
This is supposed to fix NPE reported in #1617. We don't have a reproduction
so this is just an educated guess.
Add scala 2.11 test/build verification.
* Add 2.11 build configuratoin to travis ci
* Create command which runs `safe` unit tests
* Create command to test the scala 2.11 build
* Update scalacheck to 1.11.4
* Update specs2 to 2.3.11
* Fix various 2.11/deprecation removals
and other changes.
Fix eval test failure in scala 2.11 with XML not existing.
It should have been there from the beginning because NameHashing is tied
to internals of the API subproject.
It was added to incremental subproject by mistake.
Prior to this commit, a class that inherited a macro from another
class was considered by incremental compiler as having a macro.
Now, only classes that explicitly define a macro are considered as having
a macro. This influences decision whether to invalidate (recompile)
dependencies of a file that inherits a macro upon a whitespace change.
From now on, we don't invalidate dependencies in such case which
results in much better incremental compiler experience when macros are
being involved. Check #1142 for detailed discussion.
The change to the behavior is reflected by marking the
source-dependencies/inherited-macros test as passing.
The source-dependencies/macro test covers the case of defining the macro
directly in source file. Therefore we know that the desired behavior of
invalidating dependencies of macros is preserved.
Fixes#1142
A hash for given name in a source file is computed by combining
hashes of all definitions with given name. When hashing a single
definition we take into account all information about it except nested
definitions. For example, if we have following definition
class Foo[T] {
def bar(x: Int): Int = ???
}
hash sum for `Foo` will include the fact that we have a class with
a single type parameter but it won't include hash sum of `bar` method.
Computed hash sums are location-sensitive. Each definition is hashed along
with its location so we properly detect cases when definition's signature
stays the same but it's moved around in the same compilation unit.
The location is defined as sequence of selections. Each selection consists
of a name and name type. The name type is either term name or type name.
Scala specification (9.2) guarantees that each publicly visible definition
is uniquely identified by a sequence of such selectors.
For example, if we have:
object Foo {
class Bar { def abc: Int }
}
then location of `abc` is Seq((TermName, Foo), (TypeName, Bar))
It's worth mentioning that we track name-hash pairs separately for
regular (non implicit) and implicit members. That's required for name
hashing algorithm because it does not apply its heuristic when implicit
members are being modified.
Another important characteristic is that we include all inherited members
when computing name hashes.
Here comes the detailed list of changes made in this commit:
* HashAPI has new parameter `includeDefinitions` that allows
shallow hashing of Structures (where we do not compute hashes
recursively)
* HashAPI exposes `finalizeHash` method that allow one to capture
current hash at any time. This is useful if you want to hash a list of
definitions and not just whole `SourceAPI`.
* NameHashing implements actual extraction of public definitions,
grouping them by simple name and computing hash sums for each group
using HashAPI
* `Source` class (defined in interface/other file) has been extended to
include `_internalOnly_nameHashes` field. This field stores
NameHashes data structure for given source file. The NameHashes
stores two separate collections of name-hash pairs for regular and
implicit members.
The prefix `_internalOnly_` is used to indicate that this is not an
official incremental compiler's or sbt's API and it's for use by
incremental compiler internals only. We had to use such a prefix
because the `datatype` code generator doesn't support emitting access
modifiers
* `AnalysisCallback` implementation has been modified to gather all
name hashes and store them in the Source object
* TestCaseGenerators has been modified to implement generation of
NameHashes
* The NameHashingSpecification contains a few unit tests that make sure
that the basic functionality works properly
The main motivation behind this commit is to reify information about
api changes that incremental compiler considers. We introduce a new
sealed class `APIChange` that has (at the moment) two subtypes:
* APIChangeDueToMacroDefinition - as the name explains, this represents
the case where incremental compiler considers an api to be changed
just because given source file contains a macro definition
* SourceAPIChange - this represents the case of regular api change;
at the moment it's just a simple wrapper around value representing
source file but in the future it will get expanded to contain more
detailed information about API changes (e.g. collection of changed
name hashes)
The APIChanges becomes just a collection of APIChange instances.
In particular, I removed `names` field that seems to be a dead code in
incremental compiler. The `NameChanges` class and methods that refer to
it in `SameAPI` has been deprecated.
The Incremental.scala has been adapted to changed signature of APIChanges
class. The `sameSource` method returns representation of APIChange
(if there's one) instead of just simple boolean. One notable change is
that information about APIChanges is pushed deeper into invalidation logic.
This will allow us to treat the APIChangeDueToMacroDefinition case properly
once name hashing scheme arrives.
This commit shouldn't change any behavior and is purely a refactoring.
Fix the problem with unstable names synthesized for existential
types (declared with underscore syntax) by renaming type variables
to a scheme that is guaranteed to be stable no matter where given
the existential type appears.
The sheme we use are De Bruijn-like indices that capture both position
of type variable declarion within single existential type and nesting
level of nested existential type. This way we properly support nested
existential types by avoiding name clashes.
In general, we can perform renamings like that because type variables
declared in existential types are scoped to those types so the renaming
operation is local.
There's a specs2 unit test covering instability of existential types.
The test is included in compiler-interface project and the build
definition has been modified to enable building and executing tests
in compiler-interface project. Some dependencies has been modified:
* compiler-interface project depends on api project for testing
(test makes us of SameAPI)
* dependency on junit has been introduced because it's needed
for `@RunWith` annotation which declares that specs2 unit
test should be ran with JUnitRunner
SameAPI has been modified to expose a method that allows us to
compare two definitions.
This commit also adds `ScalaCompilerForUnitTesting` class that allows
to compile a piece of Scala code and inspect information recorded
callbacks defined in `AnalysisCallback` interface. That class uses
existing ConsoleLogger for logging. I considered doing the same for
ConsoleReporter. There's LoggingReporter defined which would fit our
usecase but it's defined in compile subproject that compiler-interface
doesn't depend on so we roll our own.
ScalaCompilerForUnit testing uses TestCallback from compiler-interface
subproject for recording information passed to callbacks. In order
to be able to access TestCallback from compiler-interface
subproject I had to tweak dependencies between interface and
compiler-interface so test classes from the former are visible in the
latter. I also modified the TestCallback itself to accumulate apis in
a HashMap instead of a buffer of tuples for easier lookup.
An integration test has been added which tests scenario
mentioned in #823.
This commit fixes#823.
This reverts commit 9dd5f076ea which was a revert
itself so we are back to the state before those two reverts.
The problem that caused revert that happened in 9dd5f076ea
has been fixed in 88061dd262.
In `ClassToAPI` both `Private` and `Protected` vals had forward
reference to `Unqualified` so they would get `null` as a result.
Fixed that by rearranging the order of vals being declared.
This fixes a problem described in 9dd5f076ea.
Showing inherited members of a structure was disabled so we would not
run into cycles. To best of my knowledge, we can run into cycles only
if inherited member is `ClassLike`. We filter out those and let
anything else to be shown.
As described in sbt/sbt#676, arguments to `mergeMap` and `merge` methods
were swapped causing wrong structures being created for Java compiled
class files.
This commit fixessbt/sbt#676 and makes pending test to pass now.