Version 2.0.0-dev.28.0

Merge commit '38698fcf90fe531f574d47944271c3dcb548692b' into dev
diff --git a/docs/language/dart.sty b/docs/language/dart.sty
index b935898..6f5a870 100644
--- a/docs/language/dart.sty
+++ b/docs/language/dart.sty
@@ -17,6 +17,7 @@
 \def\EXPORT{\builtinId{export}}
 \def\EXTERNAL{\builtinId{external}}
 \def\FACTORY{\builtinId{factory}}
+\def\FUNCTION{\builtinId{Function}}
 \def\GET{\builtinId{get}}
 \def\IMPLEMENTS{\builtinId{implements}}
 \def\IMPORT{\builtinId{import}}
diff --git a/docs/language/dartLangSpec.tex b/docs/language/dartLangSpec.tex
index 8e7874d..51a6dc7 100644
--- a/docs/language/dartLangSpec.tex
+++ b/docs/language/dartLangSpec.tex
@@ -40,6 +40,9 @@
 %   abstractly, to eliminate certain diagnostic messages.
 % - Add generic functions and methods to the language.
 % - Don't cause warning if a non-system library import shadows a system library.
+% - Update mixin application forwarding constructors to correctly handle
+%   optional parameters and const constructors.
+% - Specify `call` for Dart 2 (no function type given to enclosing class).
 %
 % 1.15
 % - Change how language specification describes control flow.
@@ -1118,10 +1121,10 @@
 \code{<$X_1\ B_1, \ldots,\ X_m\ B_m$>($T_1, \ldots,\ T_n, $ \{$T_{n+1}\ p_{n+1}, \ldots,\ T_{n+k}\ p_{n+k}$\}) $ \rightarrow T_0$}.
 
 \LMHash{}
-The run-time type of a function object always implements the class \code{Function}.
+The run-time type of a function object always implements the class \FUNCTION{}.
 
 \commentary{
-One cannot assume, based on the above, that given a function \code{f}, \code{f.runtimeType} will actually be \code{Function}, or that any two distinct function objects necessarily have the same run-time type.
+One cannot assume, based on the above, that given a function \code{f}, \code{f.runtimeType} will actually be \FUNCTION{}, or that any two distinct function objects necessarily have the same run-time type.
 }
 
 \rationale{
@@ -1129,7 +1132,7 @@
 For example, consider that a closure produced via property extraction treats equality different from ordinary closures, and is therefore likely a different class.
 Implementations may also use different classes for functions based on arity and or type.
 Arity may be implicitly affected by whether a function is an instance method (with an implicit receiver parameter) or not.
-The variations are manifold, and so this specification only guarantees that function objects are instances of some class that is considered to implement \code{Function}.
+The variations are manifold, and so this specification only guarantees that function objects are instances of some class that is considered to implement \FUNCTION{}.
 }
 
 
@@ -2215,7 +2218,7 @@
 \LMLabel{superclasses}
 
 \LMHash{}
-The superclass of a class $C$ that has a with clause \code{\WITH{} $M_1, \ldots,\ M_k$} and an extends clause \code{\EXTENDS{} S} is the application of mixin (\ref{mixins}) $M_k* \cdots * M_1$ to S.
+The superclass $S^\prime$ of a class $C$ that has a with clause \code{\WITH{} $M_1, \ldots,\ M_k$} and an extends clause \code{\EXTENDS{} $S$} is the application of mixin composition (\ref{mixins}) $M_k* \cdots * M_1$ to $S$. The name $S^\prime$ is a fresh identifier.
 If no \WITH{} clause is specified then the \EXTENDS{} clause of a class $C$ specifies its superclass.
 If no \EXTENDS{} clause is specified, then either:
 \begin{itemize}
@@ -2554,7 +2557,8 @@
 A mixin is always derived from an existing class declaration.
 
 \LMHash{}
-It is a compile-time error if a declared or derived mixin explicitly declares a constructor which is not a factory constructor.
+It is a compile-time error to derive a mixin from a class which explicitly declares a generative constructor.
+It is a compile-time error to derive a mixin from a class which has a superclass other than \code{Object}.
 
 \rationale{
 This restriction is temporary.
@@ -2569,8 +2573,8 @@
 
 \LMHash{}
 A mixin may be applied to a superclass, yielding a new class.
-Mixin application occurs when a mixin is mixed into a class declaration via its \WITH{} clause.
-The mixin application may be used to extend a class per section (\ref{classes}); alternately, a class may be defined as a mixin application as described in this section.
+Mixin application occurs when one or more mixins are mixed into a class declaration via its \WITH{} clause.
+The mixin application may be used to extend a class per section (\ref{classes}); alternatively, a class may be defined as a mixin application as described in this section.
 It is a compile-time error if the \WITH{} clause of a mixin application $C$ includes a type variable (\ref{generics}), a type alias (\ref{typedef}), an enumerated type (\ref{enums}), a malformed type, or a deferred type (\ref{staticTypes}).
 
 \begin{grammar}
@@ -2582,29 +2586,73 @@
 \end{grammar}
 
 \LMHash{}
-A mixin application of the form \code{$S$ \WITH{} $M$;} defines a class $C$ with superclass $S$.
+A mixin application of the form \code{$S$ \WITH{} $M$;} for the name $N$ defines a class $C$ with superclass $S$ and name $N$.
 
 \LMHash{}
-A mixin application of the form \code{$S$ \WITH{} $M_1, \ldots,\ M_k$;} defines a class $C$ whose superclass is the application of the mixin composition (\ref{mixinComposition}) $M_{k-1} * \ldots * M_1$ to $S$.
+A mixin application of the form \code{$S$ \WITH{} $M_1,\ \ldots, M_k$;} for the name $N$ defines a class $C$ whose superclass is the application of the mixin composition (\ref{mixinComposition}) $M_{k-1} * \ldots * M_1$ to $S$ of a name that is a fresh identifer, and whose name is $N$.
+\rationale{The name of the resulting class is necessary because it is part of the names of the introduced constructors.}
 
 \LMHash{}
 In both cases above, $C$ declares the same instance members as $M$ (respectively, $M_k$).
-If any of the instance variables of $M$ (respectively, $M_k$) have initializers, they are executed in the scope of $M$ (respectively, $M_k$) to initialize the corresponding instance variables of $C$.
+If any of the instance variables of $M$ (respectively, $M_k$) have initializers,
+they are executed in the instance scope of $M$ (respectively, $M_k$)
+to initialize the corresponding instance variables of $C$.
 
 \LMHash{}
-Let $L_M$ be the library in which $M$ is declared.
-For each generative constructor named
-$q_i(T_{i1}\ a_{i1}, \ldots,\ T_{ik_i}\ a_{ik_i}), i \in 1 .. n$
-of $S$ that is accessible to $L_M$,
-$C$ has an implicitly declared constructor named
-$q'_i = [C/S]q_i$ of the form
+Let $L_C$ be the library containing the mixin application.
+\commentary{That is, the library containing the clause \code{$S$ \WITH{} $M$}
+or the clause \code{$S_0$ \WITH{} $M_1$, \ldots,\ $M_k$, $M$}.}
 
-\code{$q'_i$($a_{i1}, \ldots,\ a_{ik_i}$): \SUPER($a_{i1}, \ldots,\ a_{ik_i}$);}.
+Let $N_C$ be the name of the mixin application class $C$,
+let $S$ be the superclass of $C$, and let $S_N$ be the name of $S$.
 
-%super.id
+For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, $\ldots$, $T_{k}$ $a_{k}$)} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form
+\begin{dartCode}
+$C_q$($T_{1}$ $a_{1}$, \ldots, $T_{k}$ $a_{k}$):$\SUPER_q$($a_{1}$, $\ldots$, $a_{k}$);
+\end{dartCode}
+
+\noindent
+where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$,
+which denote the superclass, by $N_C$, and $\SUPER_q$ is obtained from $S_q$ by
+replacing occurrences of $S_N$ which denote the superclass by \SUPER{}.
+If $S_q$ is a generative const constructor, and $M$ does not declare any
+fields, $C_q$ is also a const constructor.
 
 \LMHash{}
-If the mixin application declares support for interfaces, the resulting class implements those interfaces.
+For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, [$T_{k+1}$ $a_{k+1}$ = $d_1$, \ldots , $T_{k+p}$ $a_{k+p}$ = $d_p$])} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form
+\begin{dartCode}
+$C_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, [$T_{k+1}$ $a_{k+1}$ = $d'_{1}$, \ldots , $T_{k+p}$ $a_{k+p}$ = $d'_p$])
+    : $\SUPER_q$($a_{1}$, \ldots , $a_{k}$, $a_{k+1}$, \ldots, $a_p$);
+\end{dartCode}
+
+\noindent
+where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$,
+which denote the superclass, by $N_C$,
+$\SUPER_q$ is obtained from $S_q$ by replacing occurrences of $S_N$
+which denote the superclass by \SUPER{},
+and $d'_i$, $i \in 1..p$, is a compile-time constant expression evaluating
+to the same value as $d_i$.
+If $S_q$ is a generative const constructor, and $M$ does not declare any
+fields, $C_q$ is also a const constructor.
+
+\LMHash{}
+For each generative constructor of the form \code{$S_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, \{$T_{k+1}$ $a_{k+1}$ = $d_1$, \ldots , $T_{k+n}$ $a_{k+n}$ = $d_n$\})} of $S$ that is accessible to $L_C$, $C$ has an implicitly declared constructor of the form
+\begin{dartCode}
+$C_q$($T_{1}$ $a_{1}$, \ldots , $T_{k}$ $a_{k}$, \{$T_{k+1}$ $a_{k+1}$ = $d'_1$, \ldots , $T_{k+n}$ $a_{k+n}$ = $d'_n$\})
+    : $\SUPER_q$($a_{1}$, \ldots , $a_{k}$, $a_{k+1}$: $a_{k+1}$, \ldots, $a_p$: $a_p$);
+\end{dartCode}
+
+\noindent
+where $C_q$ is obtained from $S_q$ by replacing occurrences of $S_N$
+which denote the superclass by $N_C$,
+$\SUPER_q$ is obtained from $S_q$ by replacing occurrences of $S_N$
+which denote the superclass by \SUPER{},
+and $d'_i$, $i \in 1..n$, is a compile-time constant expression evaluating to the same value as $d_i$.
+If $S_q$ is a generative const constructor, and $M$ does not declare any
+fields, $C_q$ is also a const constructor.
+
+\LMHash{}
+If the mixin application class declares interfaces, the resulting class also implements those interfaces.
 
 \LMHash{}
 It is a compile-time error if $S$ is an enumerated type (\ref{enums}) or a malformed type.
@@ -2621,7 +2669,7 @@
 }
 
 \LMHash{}
-The effect of a class definition of the form \code{\CLASS{} $C$ = $M$; } or the form \code{\CLASS{} $C$<$T_1, \ldots,\ T_n$> = $M$; } in library $L$ is to introduce the name $C$ into the scope of $L$, bound to the class (\ref{classes}) defined by the mixin application $M$.
+The effect of a class definition of the form \code{\CLASS{} $C$ = $M$; } or the form \code{\CLASS{} $C<T_1, \ldots,\ T_n>$ = $M$; } in library $L$ is to introduce the name $C$ into the scope of $L$, bound to the class (\ref{classes}) defined by the mixin application $M$ for the name $C$.
 The name of the class is also set to $C$.
 If{}f the class is prefixed by the built-in identifier \ABSTRACT{}, the class being defined is an abstract class.
 
@@ -2649,11 +2697,11 @@
 
 $M_1<T_1, \ldots, T_{k_{M_1}}> * M_2<U_1, \ldots, U_{k_{M_2}}>$
 
-to $S<V_1, \ldots, V_{k_S}>$ is equivalent to
+to $S<V_1, \ldots, V_{k_S}>$ for the name $C$ is equivalent to
 
 \begin{dartCode}
-\ABSTRACT{} \CLASS{} $Id_1<T_1, \ldots, T_{k_{M_1}}, U_1, \ldots, U_{k_{M_2}}, V_1, \ldots, V_{k_S}> = $
-      $Id_2<U_1, \ldots, U_{k_{M_2}}, V_1, \ldots, V_{k_S}>$ \WITH{} $M_1 <T_1, \ldots, T_{k_{M_1}}>$;
+\ABSTRACT{} \CLASS{} $C<T_1, \ldots, T_{k_{M_1}}, U_1, \ldots, U_{k_{M_2}}, V_1, \ldots, V_{k_S}> = $
+      $Id_2<U_1, \ldots, U_{k_{M_2}}, V_1 \ldots V_{k_S}>$ \WITH{} $M_1 <T_1, \ldots, T_{k_{M_1}}>$;
 \end{dartCode}
 
 where $Id_2$ denotes
@@ -2663,10 +2711,10 @@
                          $S<V_1, \ldots, V_{k_S}>$ \WITH{} $M_2<U_1, \ldots, U_{k_{M_2}}>$;
 \end{dartCode}
 
-and $Id_1$ and $Id_2$ are unique identifiers that do not exist anywhere in the program.
+and $Id_2$ is a unique identifier that does not exist anywhere in the program.
 
 \rationale{
-The classes produced by mixin composition are regarded as abstract because they cannot be instantiated independently.
+The intermediate classes produced by mixin composition are regarded as abstract because they cannot be instantiated independently.
 They are only introduced as anonymous superclasses of ordinary class declarations and mixin applications.
 Consequently, no warning is given if a mixin composition includes abstract members, or incompletely implements an interface.
 }
@@ -2676,7 +2724,7 @@
 
 \commentary{
 Note that any subset of $M_1$, $M_2$ and $S$ may or may not be generic.
-For any non-generic declaration, the corresponding type parameters may be elided, and if no type parameters remain in the derived declarations $Id_1$ and/or $Id_2$ then the those declarations need not be generic either.
+For any non-generic declaration, the corresponding type parameters may be elided, and if no type parameters remain in the derived declarations $C$ and/or $Id_2$ then the those declarations need not be generic either.
 }
 
 
@@ -3841,7 +3889,7 @@
 %% specifically for each relevant type of situation.
 
 \LMHash{}
-The class of a function literal implements the built-in class \code{Function}.
+The class of a function literal implements the built-in class \FUNCTION{}.
 
 \LMHash{}
 The static type of a function literal of the form
@@ -4665,7 +4713,7 @@
 
 \LMHash{}
 Let $T_i$ be the static type of $a_i$.
-If the static type of $f$ is \DYNAMIC{} or the built-in class \code{Function},
+If the static type of $f$ is \DYNAMIC{} or the built-in class \FUNCTION{},
 no further static checks are performed.
 Otherwise, it is a static type warning if the static type of $f$ is not a function type.
 
@@ -4768,30 +4816,54 @@
 }
 
 \LMHash{}
-Otherwise:
-
-Execution of a function expression invocation
+Otherwise, evaluation of a function expression invocation
 
 \code{$e_f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}
 
 \noindent
-is equivalent to execution of
+proceeds to evaluate $e_f$, yielding an object $o$.
+Let $f$ be a fresh variable bound to $o$.
+If $o$ is a function object then the following function invocation is evaluated
+and its result is the result of evaluating $i$:
 
-\code{$e_f$.call<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}.
+\code{$f$<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}.
 
-\commentary{
-The implication of this definition, and the other definitions involving the method \code{call()}, is that user defined types can be used as function values provided they define a \CALL{} method.
-The method \CALL{} is special in this regard.
-The signature of the \CALL{} method determines the signature used when using the object via the built-in invocation syntax.
-}
+\noindent
+Otherwise, if $o$ has a method named \CALL{},
+the following method invocation is evaluated and its result is the result of evaluating $i$:
+
+\code{$f$.call<$A_1, \ldots,\ A_r$>($a_1, \ldots,\ a_n,\ x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $a_{n+k}$)}.
+
+\noindent
+Otherwise, when $o$ has no method named \CALL{},
+a new instance $im$ of the predefined class \code{Invocation} is created, such that:
+\begin{itemize}
+\item \code{im.isMethod} evaluates to \code{\TRUE{}}.
+\item \code{im.memberName} evaluates to the symbol \code{\#call}.
+\item \code{im.positionalArguments} evaluates to an unmodifiable list with the values
+resulting from evaluation of
+\code{<Object>[$a_1, \ldots,\ a_n$]}.
+\item \code{im.namedArguments} evaluates to an unmodifiable map
+with the keys and values resulting from evaluation of
+
+\code{<Symbol, Object>\{$\#x_{n+1}$: $a_{n+1}, \ldots,\ x_{n+k}$: $\#a_{n+k}$\}}.
+\item \code{im.typeArguments} evaluates to an unmodifiable list
+with the values resulting from evaluation of
+\code{<Type>[$A_1, \ldots,\ A_r$]}.
+\end{itemize}
+
+\LMHash{}
+Then the method invocation \code{f.noSuchMethod($im$)} is evaluated,
+and its result is the result of evaluating $i$.
 
 \LMHash{}
 Let $F$ be the static type of $e_f$.
 It is a static warning unless one of the following conditions is satisfied:
-\begin{itemize}
+\begin{enumerate}
 \item $F$ is \DYNAMIC{}.
-\item $F$ is \code{Function}.
-\item $F$ is a function type
+\item $F$ is \FUNCTION{}.
+\item\label{InvocationTypeListNamed}
+$F$ is a function type
 
 \code{<$X_1\ \EXTENDS\ B_1, \ldots,\ A_s\ \EXTENDS\ B_s$>}
 
@@ -4801,7 +4873,8 @@
 where $r = s$, and the static type of $a_j$ is assignable to
 $[A_1/X_1, \ldots, A_r/X_s]T_j$, for all $j \in 1 .. n+k$.
 
-\item $k$ is zero, $0 \leq m \leq n$, and $F$ is a function type
+\item\label{InvocationTypeListPositional}
+$k$ is zero, $0 \leq m \leq n$, and $F$ is a function type
 
 \code{<$X_1\ \EXTENDS\ B_1, \ldots,\ A_s\ \EXTENDS\ B_s$>}
 
@@ -4811,14 +4884,31 @@
 where $r = s$, and the static type of $a_j$ is assignable to
 $[A_1/X_1, \ldots, A_r/X_s]T_j$,
 for all $j \in 1 .. n$.
-\end{itemize}
+
+\item\label{InvocationTypeListCall}
+$F$ is an interface type that has a method named \CALL{}, and then,
+considering $F$ to denote the function type of that method,
+criterion~\ref{InvocationTypeListNamed} or~\ref{InvocationTypeListPositional}
+in this list is satisfied.
+\end{enumerate}
 
 \commentary{
-That is, the types of the actual arguments must match the declared types of the corresponding parameters where the formal type parameters have been replaced by the actual type arguments.
+This means that the types of the actual arguments must match the declared types of the corresponding parameters,
+where the formal type parameters have been replaced by the actual type arguments.
+%
 Note that the type parameter lists are omitted when $s = 0$ (\ref{generics}),
 and that checks on the number of type arguments and their bounds is specified elsewhere (\ref{generics}).
+%
+Criterion~\ref{InvocationTypeListCall} ensures that
+a function expression invocation may amount to an invocation of the instance method \CALL{}
+when such a method is statically known,
+and the run-time semantics ensures that
+a function invocation may amount to an invocation of the instance method \CALL{}
+also for an invocation of a function of type \DYNAMIC{} or \FUNCTION{},
+but that an interface type with a method named \CALL{} is not itself a subtype of any function type.
 }
 
+\LMHash{}
 If $F$ is not a function type or $r \not= s$, the static type of $i$ is \DYNAMIC{}.
 Otherwise, the static type of $i$ is the return type
 $[A_1/X_1, \ldots, A_r/X_s]T_0$ of $F$.
@@ -5047,6 +5137,7 @@
 \commentary{
 It is not possible to override the \code{noSuchMethod} of class \code{Object}
 in such a way that it cannot be invoked with one argument of type \code{Invocation}.
+% .. leaving `noSuchMethod(covariant Null n) => n;` as an exercise for the reader.
 }
 
 \commentary{
@@ -5060,13 +5151,15 @@
 \item $T$ is \code{Type}, $e$ is a constant type literal,
 and the class corresponding to $e$ has a static getter named $m$.
 Or
-\item $T$ is \code{Function} and $m$ is \CALL.
-\rationale {
-The type \code{Function} is treated as if it has a \code{call} method for any possible signature of \CALL.
-The expectation is that any concrete subclass of \code{Function} will implement \CALL.
-Note that a warning will be issued if this is not the case.
-Furthermore, any use of \CALL{} on a subclass of \code{Function} that fails to implement \CALL{} will also provoke a warning,
-as this exemption is limited to type \code{Function}, and does not apply to its subtypes.
+\item $T$ is \FUNCTION{} and $m$ is \CALL.
+\rationale{
+This means that for invocations of an instance method named \code{call},
+a receiver of type \FUNCTION{} is treated like a receiver of type \DYNAMIC{}.
+The expectation is that any concrete subclass of \FUNCTION{} will implement \CALL,
+but there is no method signature which can be assumed for \CALL{} in \FUNCTION{}
+because every signature will conflict with some potential overriding declarations.
+Note that any use of \CALL{} on a subclass of \FUNCTION{} that fails to implement \CALL{} will provoke a warning,
+as this exemption is limited to type \FUNCTION{}, and does not apply to its subtypes.
 }
 \end{itemize}
 
@@ -6039,7 +6132,7 @@
 
 
 \subsection{If-null Expressions}
-\label{ifNull}
+\LMLabel{ifNull}
 
 \LMHash{}
 An {\em if-null expression} evaluates an expression and if the result is the null object (\ref{null}), evaluates another.
@@ -6677,6 +6770,7 @@
   \EXPORT{};
   \EXTERNAL{};
   \FACTORY{};
+  \FUNCTION{};
   \GET{};
   \IMPLEMENTS{};
   \IMPORT{};
@@ -9022,7 +9116,7 @@
 \item $T$ is of the form \code{$I$<$T_1, \ldots,\ T_n$>} and $S$ is of the form \code{$I$<$S_1, \ldots,\ S_n$>} and:
 $T_i << S_i, 1 \le i \le n$
 \item $T$ and $S$ are both function types, and $T << S$ under the rules of section \ref{functionTypes}.
-\item $T$ is a function type and $S$ is \code{Function}.
+\item $T$ is a function type and $S$ is \FUNCTION{}.
 \item $T << U$ and $U << S$.
 \end{itemize}
 
@@ -9223,11 +9317,9 @@
 A function type $T$ may be assigned to a function type $S$, written $T \Longleftrightarrow S$, if{}f $T <: S$.
 
 \LMHash{}
-% ensure that Object and dynamic may be assign dot a function type
-A function is always an instance of some class that implements the class \code{Function} and implements a \CALL{} method with the same signature as the function.
-All function types are subtypes of \code{Function}.
-If a type $I$ includes an instance method named \CALL{}, and the type of \CALL{} is the function type $F$, then $I$ is considered to be more specific than $F$.
-It is a static warning if a concrete class implements \code{Function} and does not have a concrete method named \CALL{} unless that class has a concrete \code{noSuchMethod()} distinct from the one declared in class \code{Object}.
+A function is always an instance of some class that implements the class \FUNCTION{}.
+All function types are subtypes of \FUNCTION{}.
+It is a static warning if a concrete class implements \FUNCTION{} and does not have a concrete method named \CALL{} unless that class has a concrete \code{noSuchMethod()} distinct from the one declared in class \code{Object}.
 
 %\commentary{Need to specify how a function values dynamic type is derived from its static signature.}
 
@@ -9285,7 +9377,7 @@
 \end{enumerate}
 
 \LMHash{}
-Furthermore, if $F$ is a function type, $F << \code{Function}$.
+Furthermore, if $F$ is a function type, $F << \FUNCTION{}$.
 
 
 \subsection{Type \DYNAMIC{}}
@@ -9611,10 +9703,10 @@
 % Function types
 
 \LMHash{}
-The least upper bound of a function type and an interface type $T$ is the least upper bound of \code{Function} and $T$.
+The least upper bound of a function type and an interface type $T$ is the least upper bound of \FUNCTION{} and $T$.
 Let $F$ and $G$ be function types.
 If $F$ and $G$ differ in their number of required parameters,
-then the least upper bound of $F$ and $G$ is \code{Function}.
+then the least upper bound of $F$ and $G$ is \FUNCTION{}.
 Otherwise:
 \begin{itemize}
 \item If
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index a459c82..cf6afc5 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -1563,6 +1563,7 @@
   
   
   
+  
 <h3>Requests</h3><dl><dt class="request"><a name="request_search.findElementReferences">search.findElementReferences</a></dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "search.findElementReferences"
@@ -2541,6 +2542,7 @@
   
   
   
+  
 <dl><dt class="typeDefinition"><a name="type_AddContentOverlay">AddContentOverlay: object</a></dt><dd>
     <p>
       A directive to begin overlaying the contents of a file. The supplied
@@ -3078,6 +3080,48 @@
           The type parameter list for the element. If the element doesn't have
           type parameters, this field will not be defined.
         </p>
+      </dd></dl></dd><dt class="typeDefinition"><a name="type_ElementDeclaration">ElementDeclaration: object</a></dt><dd>
+    <p>
+      A declaration - top-level (class, field, etc) or a class member (method,
+      field, etc).
+    </p>
+    
+  <dl><dt class="field"><b>name: String</b></dt><dd>
+        
+        <p>
+          The name of the declaration.
+        </p>
+      </dd><dt class="field"><b>kind: <a href="#type_ElementKind">ElementKind</a></b></dt><dd>
+        
+        <p>
+          The kind of the element that corresponds to the declaration.
+        </p>
+      </dd><dt class="field"><b>fileIndex: int</b></dt><dd>
+        
+        <p>
+          The index of the file (in the enclosing response).
+        </p>
+      </dd><dt class="field"><b>offset: int</b></dt><dd>
+        
+        <p>
+          The offset of the declaration name in the file.
+        </p>
+      </dd><dt class="field"><b>line: int</b></dt><dd>
+        
+        <p>
+          The one-based index of the line containing the declaration name.
+        </p>
+      </dd><dt class="field"><b>column: int</b></dt><dd>
+        
+        <p>
+          The one-based index of the column containing the declaration name.
+        </p>
+      </dd><dt class="field"><b>className: String<span style="color:#999999"> (optional)</span></b></dt><dd>
+        
+        <p>
+          The name of the class enclosing this declaration. If the declaration
+          is not a class member, this field will be absent.
+        </p>
       </dd></dl></dd><dt class="typeDefinition"><a name="type_ElementKind">ElementKind: String</a></dt><dd>
     <p>
       An enumeration of the kinds of elements.
@@ -4742,7 +4786,7 @@
   TODO: TBD
 </p>
 <h2 class="domain"><a name="index">Index</a></h2>
-<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
+<h3>Domains</h3><h4>server (<a href="#domain_server">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_server.getVersion">getVersion</a></li><li><a href="#request_server.shutdown">shutdown</a></li><li><a href="#request_server.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_server.connected">connected</a></li><li><a href="#notification_server.error">error</a></li><li><a href="#notification_server.status">status</a></li></ul></div></div><h4>analysis (<a href="#domain_analysis">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_analysis.getErrors">getErrors</a></li><li><a href="#request_analysis.getHover">getHover</a></li><li><a href="#request_analysis.getLibraryDependencies">getLibraryDependencies</a></li><li><a href="#request_analysis.getNavigation">getNavigation</a></li><li><a href="#request_analysis.getReachableSources">getReachableSources</a></li><li><a href="#request_analysis.reanalyze">reanalyze</a></li><li><a href="#request_analysis.setAnalysisRoots">setAnalysisRoots</a></li><li><a href="#request_analysis.setGeneralSubscriptions">setGeneralSubscriptions</a></li><li><a href="#request_analysis.setPriorityFiles">setPriorityFiles</a></li><li><a href="#request_analysis.setSubscriptions">setSubscriptions</a></li><li><a href="#request_analysis.updateContent">updateContent</a></li><li><a href="#request_analysis.updateOptions">updateOptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_analysis.analyzedFiles">analyzedFiles</a></li><li><a href="#notification_analysis.closingLabels">closingLabels</a></li><li><a href="#notification_analysis.errors">errors</a></li><li><a href="#notification_analysis.flushResults">flushResults</a></li><li><a href="#notification_analysis.folding">folding</a></li><li><a href="#notification_analysis.highlights">highlights</a></li><li><a href="#notification_analysis.implemented">implemented</a></li><li><a href="#notification_analysis.invalidate">invalidate</a></li><li><a href="#notification_analysis.navigation">navigation</a></li><li><a href="#notification_analysis.occurrences">occurrences</a></li><li><a href="#notification_analysis.outline">outline</a></li><li><a href="#notification_analysis.overrides">overrides</a></li></ul></div></div><h4>completion (<a href="#domain_completion">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_completion.getSuggestions">getSuggestions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_completion.results">results</a></li></ul></div></div><h4>search (<a href="#domain_search">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_search.findElementReferences">findElementReferences</a></li><li><a href="#request_search.findMemberDeclarations">findMemberDeclarations</a></li><li><a href="#request_search.findMemberReferences">findMemberReferences</a></li><li><a href="#request_search.findTopLevelDeclarations">findTopLevelDeclarations</a></li><li><a href="#request_search.getTypeHierarchy">getTypeHierarchy</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_search.results">results</a></li></ul></div></div><h4>edit (<a href="#domain_edit">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_edit.format">format</a></li><li><a href="#request_edit.getAssists">getAssists</a></li><li><a href="#request_edit.getAvailableRefactorings">getAvailableRefactorings</a></li><li><a href="#request_edit.getFixes">getFixes</a></li><li><a href="#request_edit.getRefactoring">getRefactoring</a></li><li><a href="#request_edit.sortMembers">sortMembers</a></li><li><a href="#request_edit.organizeDirectives">organizeDirectives</a></li></ul></div><h4>execution (<a href="#domain_execution">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_execution.createContext">createContext</a></li><li><a href="#request_execution.deleteContext">deleteContext</a></li><li><a href="#request_execution.mapUri">mapUri</a></li><li><a href="#request_execution.setSubscriptions">setSubscriptions</a></li></ul><h5>Notifications</h5><div class="subindex"><ul><li><a href="#notification_execution.launchData">launchData</a></li></ul></div></div><h4>diagnostic (<a href="#domain_diagnostic">↑</a>)</h4><div class="subindex"><h5>Requests</h5><ul><li><a href="#request_diagnostic.getDiagnostics">getDiagnostics</a></li><li><a href="#request_diagnostic.getServerPort">getServerPort</a></li></ul></div><h3>Types (<a href="#types">↑</a>)</h3><div class="subindex"><ul><li><a href="#type_AddContentOverlay">AddContentOverlay</a></li><li><a href="#type_AnalysisError">AnalysisError</a></li><li><a href="#type_AnalysisErrorFixes">AnalysisErrorFixes</a></li><li><a href="#type_AnalysisErrorSeverity">AnalysisErrorSeverity</a></li><li><a href="#type_AnalysisErrorType">AnalysisErrorType</a></li><li><a href="#type_AnalysisOptions">AnalysisOptions</a></li><li><a href="#type_AnalysisService">AnalysisService</a></li><li><a href="#type_AnalysisStatus">AnalysisStatus</a></li><li><a href="#type_ChangeContentOverlay">ChangeContentOverlay</a></li><li><a href="#type_ClosingLabel">ClosingLabel</a></li><li><a href="#type_CompletionId">CompletionId</a></li><li><a href="#type_CompletionSuggestion">CompletionSuggestion</a></li><li><a href="#type_CompletionSuggestionKind">CompletionSuggestionKind</a></li><li><a href="#type_ContextData">ContextData</a></li><li><a href="#type_Element">Element</a></li><li><a href="#type_ElementDeclaration">ElementDeclaration</a></li><li><a href="#type_ElementKind">ElementKind</a></li><li><a href="#type_ExecutableFile">ExecutableFile</a></li><li><a href="#type_ExecutableKind">ExecutableKind</a></li><li><a href="#type_ExecutionContextId">ExecutionContextId</a></li><li><a href="#type_ExecutionService">ExecutionService</a></li><li><a href="#type_FileKind">FileKind</a></li><li><a href="#type_FilePath">FilePath</a></li><li><a href="#type_FoldingKind">FoldingKind</a></li><li><a href="#type_FoldingRegion">FoldingRegion</a></li><li><a href="#type_GeneralAnalysisService">GeneralAnalysisService</a></li><li><a href="#type_HighlightRegion">HighlightRegion</a></li><li><a href="#type_HighlightRegionType">HighlightRegionType</a></li><li><a href="#type_HoverInformation">HoverInformation</a></li><li><a href="#type_ImplementedClass">ImplementedClass</a></li><li><a href="#type_ImplementedMember">ImplementedMember</a></li><li><a href="#type_ImportedElements">ImportedElements</a></li><li><a href="#type_KytheEntry">KytheEntry</a></li><li><a href="#type_KytheVName">KytheVName</a></li><li><a href="#type_LinkedEditGroup">LinkedEditGroup</a></li><li><a href="#type_LinkedEditSuggestion">LinkedEditSuggestion</a></li><li><a href="#type_LinkedEditSuggestionKind">LinkedEditSuggestionKind</a></li><li><a href="#type_Location">Location</a></li><li><a href="#type_NavigationRegion">NavigationRegion</a></li><li><a href="#type_NavigationTarget">NavigationTarget</a></li><li><a href="#type_Occurrences">Occurrences</a></li><li><a href="#type_Outline">Outline</a></li><li><a href="#type_OverriddenMember">OverriddenMember</a></li><li><a href="#type_Override">Override</a></li><li><a href="#type_Position">Position</a></li><li><a href="#type_PostfixTemplateDescriptor">PostfixTemplateDescriptor</a></li><li><a href="#type_PubStatus">PubStatus</a></li><li><a href="#type_RefactoringFeedback">RefactoringFeedback</a></li><li><a href="#type_RefactoringKind">RefactoringKind</a></li><li><a href="#type_RefactoringMethodParameter">RefactoringMethodParameter</a></li><li><a href="#type_RefactoringMethodParameterKind">RefactoringMethodParameterKind</a></li><li><a href="#type_RefactoringOptions">RefactoringOptions</a></li><li><a href="#type_RefactoringProblem">RefactoringProblem</a></li><li><a href="#type_RefactoringProblemSeverity">RefactoringProblemSeverity</a></li><li><a href="#type_RemoveContentOverlay">RemoveContentOverlay</a></li><li><a href="#type_RequestError">RequestError</a></li><li><a href="#type_RequestErrorCode">RequestErrorCode</a></li><li><a href="#type_SearchId">SearchId</a></li><li><a href="#type_SearchResult">SearchResult</a></li><li><a href="#type_SearchResultKind">SearchResultKind</a></li><li><a href="#type_ServerService">ServerService</a></li><li><a href="#type_SourceChange">SourceChange</a></li><li><a href="#type_SourceEdit">SourceEdit</a></li><li><a href="#type_SourceFileEdit">SourceFileEdit</a></li><li><a href="#type_TypeHierarchyItem">TypeHierarchyItem</a></li></ul></div><h3>Refactorings (<a href="#refactorings">↑</a>)</h3><div class="subindex"><ul><li><a href="#refactoring_CONVERT_GETTER_TO_METHOD">CONVERT_GETTER_TO_METHOD</a></li><li><a href="#refactoring_CONVERT_METHOD_TO_GETTER">CONVERT_METHOD_TO_GETTER</a></li><li><a href="#refactoring_EXTRACT_LOCAL_VARIABLE">EXTRACT_LOCAL_VARIABLE</a></li><li><a href="#refactoring_EXTRACT_METHOD">EXTRACT_METHOD</a></li><li><a href="#refactoring_INLINE_LOCAL_VARIABLE">INLINE_LOCAL_VARIABLE</a></li><li><a href="#refactoring_INLINE_METHOD">INLINE_METHOD</a></li><li><a href="#refactoring_MOVE_FILE">MOVE_FILE</a></li><li><a href="#refactoring_RENAME">RENAME</a></li></ul></div>
 
 
 </body></html>
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
index 30c42f7..2ad30a4 100644
--- a/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
+++ b/pkg/analysis_server/lib/plugin/protocol/protocol_dart.dart
@@ -155,6 +155,9 @@
         closeOptionalString = ']';
       }
     }
+    if (parameter.isRequired) {
+      sb.write('@required ');
+    }
     parameter.appendToWithoutDelimiters(sb);
   }
   sb.write(closeOptionalString);
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 2a33b08..e061b39b 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -234,6 +234,8 @@
 const String SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS =
     'search.findTopLevelDeclarations';
 const String SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS_PATTERN = 'pattern';
+const String SEARCH_REQUEST_GET_ELEMENT_DECLARATIONS =
+    'search.getElementDeclarations';
 const String SEARCH_REQUEST_GET_TYPE_HIERARCHY = 'search.getTypeHierarchy';
 const String SEARCH_REQUEST_GET_TYPE_HIERARCHY_FILE = 'file';
 const String SEARCH_REQUEST_GET_TYPE_HIERARCHY_OFFSET = 'offset';
@@ -243,6 +245,9 @@
 const String SEARCH_RESPONSE_FIND_MEMBER_DECLARATIONS_ID = 'id';
 const String SEARCH_RESPONSE_FIND_MEMBER_REFERENCES_ID = 'id';
 const String SEARCH_RESPONSE_FIND_TOP_LEVEL_DECLARATIONS_ID = 'id';
+const String SEARCH_RESPONSE_GET_ELEMENT_DECLARATIONS_DECLARATIONS =
+    'declarations';
+const String SEARCH_RESPONSE_GET_ELEMENT_DECLARATIONS_FILES = 'files';
 const String SEARCH_RESPONSE_GET_TYPE_HIERARCHY_HIERARCHY_ITEMS =
     'hierarchyItems';
 const String SERVER_NOTIFICATION_CONNECTED = 'server.connected';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index b78ce28..3c54e20 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -8724,6 +8724,242 @@
 }
 
 /**
+ * ElementDeclaration
+ *
+ * {
+ *   "name": String
+ *   "kind": ElementKind
+ *   "fileIndex": int
+ *   "offset": int
+ *   "line": int
+ *   "column": int
+ *   "className": optional String
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class ElementDeclaration implements HasToJson {
+  String _name;
+
+  ElementKind _kind;
+
+  int _fileIndex;
+
+  int _offset;
+
+  int _line;
+
+  int _column;
+
+  String _className;
+
+  /**
+   * The name of the declaration.
+   */
+  String get name => _name;
+
+  /**
+   * The name of the declaration.
+   */
+  void set name(String value) {
+    assert(value != null);
+    this._name = value;
+  }
+
+  /**
+   * The kind of the element that corresponds to the declaration.
+   */
+  ElementKind get kind => _kind;
+
+  /**
+   * The kind of the element that corresponds to the declaration.
+   */
+  void set kind(ElementKind value) {
+    assert(value != null);
+    this._kind = value;
+  }
+
+  /**
+   * The index of the file (in the enclosing response).
+   */
+  int get fileIndex => _fileIndex;
+
+  /**
+   * The index of the file (in the enclosing response).
+   */
+  void set fileIndex(int value) {
+    assert(value != null);
+    this._fileIndex = value;
+  }
+
+  /**
+   * The offset of the declaration name in the file.
+   */
+  int get offset => _offset;
+
+  /**
+   * The offset of the declaration name in the file.
+   */
+  void set offset(int value) {
+    assert(value != null);
+    this._offset = value;
+  }
+
+  /**
+   * The one-based index of the line containing the declaration name.
+   */
+  int get line => _line;
+
+  /**
+   * The one-based index of the line containing the declaration name.
+   */
+  void set line(int value) {
+    assert(value != null);
+    this._line = value;
+  }
+
+  /**
+   * The one-based index of the column containing the declaration name.
+   */
+  int get column => _column;
+
+  /**
+   * The one-based index of the column containing the declaration name.
+   */
+  void set column(int value) {
+    assert(value != null);
+    this._column = value;
+  }
+
+  /**
+   * The name of the class enclosing this declaration. If the declaration is
+   * not a class member, this field will be absent.
+   */
+  String get className => _className;
+
+  /**
+   * The name of the class enclosing this declaration. If the declaration is
+   * not a class member, this field will be absent.
+   */
+  void set className(String value) {
+    this._className = value;
+  }
+
+  ElementDeclaration(String name, ElementKind kind, int fileIndex, int offset,
+      int line, int column,
+      {String className}) {
+    this.name = name;
+    this.kind = kind;
+    this.fileIndex = fileIndex;
+    this.offset = offset;
+    this.line = line;
+    this.column = column;
+    this.className = className;
+  }
+
+  factory ElementDeclaration.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      String name;
+      if (json.containsKey("name")) {
+        name = jsonDecoder.decodeString(jsonPath + ".name", json["name"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "name");
+      }
+      ElementKind kind;
+      if (json.containsKey("kind")) {
+        kind = new ElementKind.fromJson(
+            jsonDecoder, jsonPath + ".kind", json["kind"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "kind");
+      }
+      int fileIndex;
+      if (json.containsKey("fileIndex")) {
+        fileIndex =
+            jsonDecoder.decodeInt(jsonPath + ".fileIndex", json["fileIndex"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "fileIndex");
+      }
+      int offset;
+      if (json.containsKey("offset")) {
+        offset = jsonDecoder.decodeInt(jsonPath + ".offset", json["offset"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "offset");
+      }
+      int line;
+      if (json.containsKey("line")) {
+        line = jsonDecoder.decodeInt(jsonPath + ".line", json["line"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "line");
+      }
+      int column;
+      if (json.containsKey("column")) {
+        column = jsonDecoder.decodeInt(jsonPath + ".column", json["column"]);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "column");
+      }
+      String className;
+      if (json.containsKey("className")) {
+        className = jsonDecoder.decodeString(
+            jsonPath + ".className", json["className"]);
+      }
+      return new ElementDeclaration(name, kind, fileIndex, offset, line, column,
+          className: className);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "ElementDeclaration", json);
+    }
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["name"] = name;
+    result["kind"] = kind.toJson();
+    result["fileIndex"] = fileIndex;
+    result["offset"] = offset;
+    result["line"] = line;
+    result["column"] = column;
+    if (className != null) {
+      result["className"] = className;
+    }
+    return result;
+  }
+
+  @override
+  String toString() => JSON.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is ElementDeclaration) {
+      return name == other.name &&
+          kind == other.kind &&
+          fileIndex == other.fileIndex &&
+          offset == other.offset &&
+          line == other.line &&
+          column == other.column &&
+          className == other.className;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, name.hashCode);
+    hash = JenkinsSmiHash.combine(hash, kind.hashCode);
+    hash = JenkinsSmiHash.combine(hash, fileIndex.hashCode);
+    hash = JenkinsSmiHash.combine(hash, offset.hashCode);
+    hash = JenkinsSmiHash.combine(hash, line.hashCode);
+    hash = JenkinsSmiHash.combine(hash, column.hashCode);
+    hash = JenkinsSmiHash.combine(hash, className.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * ExecutableFile
  *
  * {
@@ -15063,6 +15299,154 @@
 }
 
 /**
+ * search.getElementDeclarations params
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class SearchGetElementDeclarationsParams implements RequestParams {
+  @override
+  Map<String, dynamic> toJson() => <String, dynamic>{};
+
+  @override
+  Request toRequest(String id) {
+    return new Request(id, "search.getElementDeclarations", null);
+  }
+
+  @override
+  bool operator ==(other) {
+    if (other is SearchGetElementDeclarationsParams) {
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    return 653510116;
+  }
+}
+
+/**
+ * search.getElementDeclarations result
+ *
+ * {
+ *   "declarations": List<ElementDeclaration>
+ *   "files": List<FilePath>
+ * }
+ *
+ * Clients may not extend, implement or mix-in this class.
+ */
+class SearchGetElementDeclarationsResult implements ResponseResult {
+  List<ElementDeclaration> _declarations;
+
+  List<String> _files;
+
+  /**
+   * The list of declarations.
+   */
+  List<ElementDeclaration> get declarations => _declarations;
+
+  /**
+   * The list of declarations.
+   */
+  void set declarations(List<ElementDeclaration> value) {
+    assert(value != null);
+    this._declarations = value;
+  }
+
+  /**
+   * The list of the paths of files with declarations.
+   */
+  List<String> get files => _files;
+
+  /**
+   * The list of the paths of files with declarations.
+   */
+  void set files(List<String> value) {
+    assert(value != null);
+    this._files = value;
+  }
+
+  SearchGetElementDeclarationsResult(
+      List<ElementDeclaration> declarations, List<String> files) {
+    this.declarations = declarations;
+    this.files = files;
+  }
+
+  factory SearchGetElementDeclarationsResult.fromJson(
+      JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<ElementDeclaration> declarations;
+      if (json.containsKey("declarations")) {
+        declarations = jsonDecoder.decodeList(
+            jsonPath + ".declarations",
+            json["declarations"],
+            (String jsonPath, Object json) =>
+                new ElementDeclaration.fromJson(jsonDecoder, jsonPath, json));
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "declarations");
+      }
+      List<String> files;
+      if (json.containsKey("files")) {
+        files = jsonDecoder.decodeList(
+            jsonPath + ".files", json["files"], jsonDecoder.decodeString);
+      } else {
+        throw jsonDecoder.mismatch(jsonPath, "files");
+      }
+      return new SearchGetElementDeclarationsResult(declarations, files);
+    } else {
+      throw jsonDecoder.mismatch(
+          jsonPath, "search.getElementDeclarations result", json);
+    }
+  }
+
+  factory SearchGetElementDeclarationsResult.fromResponse(Response response) {
+    return new SearchGetElementDeclarationsResult.fromJson(
+        new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+        "result",
+        response.result);
+  }
+
+  @override
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    result["declarations"] =
+        declarations.map((ElementDeclaration value) => value.toJson()).toList();
+    result["files"] = files;
+    return result;
+  }
+
+  @override
+  Response toResponse(String id) {
+    return new Response(id, result: toJson());
+  }
+
+  @override
+  String toString() => JSON.encode(toJson());
+
+  @override
+  bool operator ==(other) {
+    if (other is SearchGetElementDeclarationsResult) {
+      return listEqual(declarations, other.declarations,
+              (ElementDeclaration a, ElementDeclaration b) => a == b) &&
+          listEqual(files, other.files, (String a, String b) => a == b);
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    hash = JenkinsSmiHash.combine(hash, declarations.hashCode);
+    hash = JenkinsSmiHash.combine(hash, files.hashCode);
+    return JenkinsSmiHash.finish(hash);
+  }
+}
+
+/**
  * search.getTypeHierarchy params
  *
  * {
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 9b18275..6ea4034 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -118,7 +118,12 @@
     ClassElement classElement = element;
     // prepare type
     HighlightRegionType type;
-    if (classElement.isEnum) {
+    if (node.parent is TypeName &&
+        node.parent.parent is ConstructorName &&
+        node.parent.parent.parent is InstanceCreationExpression) {
+      // new Class()
+      type = HighlightRegionType.CONSTRUCTOR;
+    } else if (classElement.isEnum) {
       type = HighlightRegionType.ENUM;
     } else {
       type = HighlightRegionType.CLASS;
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
index 90cc3ba..fb97fdd 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
@@ -122,7 +122,12 @@
     ClassElement classElement = element;
     // prepare type
     HighlightRegionType type;
-    if (classElement.isEnum) {
+    if (node.parent is TypeName &&
+        node.parent.parent is ConstructorName &&
+        node.parent.parent.parent is InstanceCreationExpression) {
+      // new Class()
+      type = HighlightRegionType.CONSTRUCTOR;
+    } else if (classElement.isEnum) {
       type = HighlightRegionType.ENUM;
     } else {
       type = HighlightRegionType.CLASS;
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
index d8523a7..ab4dad7 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/override_contributor.dart
@@ -64,7 +64,7 @@
    * Return a template for an override of the given [element]. If selected, the
    * template will replace [targetId].
    */
-  Future<String> _buildReplacementText(
+  Future<DartChangeBuilder> _buildReplacementText(
       AnalysisResult result,
       SimpleIdentifier targetId,
       ExecutableElement element,
@@ -77,7 +77,7 @@
             displayTextBuffer: displayTextBuffer);
       });
     });
-    return builder.sourceChange.edits[0].edits[0].replacement.trim();
+    return builder;
   }
 
   /**
@@ -87,20 +87,23 @@
   Future<CompletionSuggestion> _buildSuggestion(DartCompletionRequest request,
       SimpleIdentifier targetId, ExecutableElement element) async {
     StringBuffer displayTextBuffer = new StringBuffer();
-    String completion = await _buildReplacementText(
+    DartChangeBuilder builder = await _buildReplacementText(
         request.result, targetId, element, displayTextBuffer);
+    String completion =
+        builder.sourceChange.edits[0].edits[0].replacement.trim();
     if (completion == null || completion.length == 0) {
       return null;
     }
 
-    SourceRange selectionRange = _computeSelectionRange(completion);
+    SourceRange selectionRange = builder.selectionRange;
+    int offsetDelta = range.node(targetId).offset;
     String displayText =
         displayTextBuffer.isNotEmpty ? displayTextBuffer.toString() : null;
     CompletionSuggestion suggestion = new CompletionSuggestion(
         CompletionSuggestionKind.OVERRIDE,
         DART_RELEVANCE_HIGH,
         completion,
-        selectionRange.offset,
+        selectionRange.offset - offsetDelta,
         selectionRange.length,
         element.isDeprecated,
         false,
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index f1d3c51..ee2f5b0 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -1677,7 +1677,6 @@
         builder.write(': ');
         builder.write(widgetSrc);
         builder.write(')');
-        builder.selectHere();
       });
     });
     _addAssistFromBuilder(changeBuilder, kind);
@@ -1717,6 +1716,8 @@
         @required String parentClassName}) async {
       ClassElement parentClassElement =
           await _getExportedClass(parentLibraryUri, parentClassName);
+      ClassElement widgetClassElement =
+          await _getExportedClass(flutter.WIDGETS_LIBRARY_URI, 'Widget');
 
       DartChangeBuilder changeBuilder = new DartChangeBuilder(session);
       await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
@@ -1731,7 +1732,9 @@
 
           builder.write(eol);
           builder.write(indentNew1);
-          builder.write('children: [');
+          builder.write('children: <');
+          builder.writeType(widgetClassElement.type);
+          builder.write('>[');
           builder.write(eol);
 
           String newSrc = _replaceSourceIndent(src, indentOld, indentNew2);
@@ -2246,7 +2249,6 @@
         builder.write(eol);
         builder.write(indentOld);
         builder.write(']');
-        builder.selectHere();
       });
     });
     _addAssistFromBuilder(changeBuilder, DartAssistKind.FLUTTER_WRAP_GENERIC);
@@ -3024,7 +3026,6 @@
         });
         builder.write(outerIndent);
         builder.write(')');
-        builder.selectHere();
       });
     });
     _addAssistFromBuilder(changeBuilder, assistKind);
diff --git a/pkg/analysis_server/test/analysis/notification_highlights_test.dart b/pkg/analysis_server/test/analysis/notification_highlights_test.dart
index 16df572..931a9ca 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights_test.dart
@@ -496,22 +496,44 @@
     assertHasRegion(HighlightRegionType.COMMENT_BLOCK, '/* b', 19);
   }
 
-  test_CONSTRUCTOR() async {
+  test_CONSTRUCTOR_explicitNew() async {
     addTestFile('''
-class AAA {
+class AAA<T> {
   AAA() {}
   AAA.name(p) {}
 }
 main() {
-  new AAA();
-  new AAA.name(42);
+  new AAA<int>();
+  new AAA<int>.name(42);
 }
 ''');
     await prepareHighlights();
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
     assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(p)');
     assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
-    assertNoRegion(HighlightRegionType.CONSTRUCTOR, 'AAA() {}');
-    assertNoRegion(HighlightRegionType.CONSTRUCTOR, 'AAA();');
+  }
+
+  test_CONSTRUCTOR_implicitNew() async {
+    addTestFile('''
+class AAA<T> {
+  AAA() {}
+  AAA.name(p) {}
+}
+main() {
+  AAA<int>();
+  AAA<int>.name(42);
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(p)');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
   }
 
   test_DIRECTIVE() async {
diff --git a/pkg/analysis_server/test/analysis/notification_highlights_test2.dart b/pkg/analysis_server/test/analysis/notification_highlights_test2.dart
index 56491fe..9ba96bc 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights_test2.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights_test2.dart
@@ -497,22 +497,44 @@
     assertHasRegion(HighlightRegionType.COMMENT_BLOCK, '/* b', 19);
   }
 
-  test_CONSTRUCTOR() async {
+  test_CONSTRUCTOR_explicitNew() async {
     addTestFile('''
-class AAA {
+class AAA<T> {
   AAA() {}
   AAA.name(p) {}
 }
 main() {
-  new AAA();
-  new AAA.name(42);
+  new AAA<int>();
+  new AAA<int>.name(42);
 }
 ''');
     await prepareHighlights();
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
     assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(p)');
     assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
-    assertNoRegion(HighlightRegionType.CONSTRUCTOR, 'AAA() {}');
-    assertNoRegion(HighlightRegionType.CONSTRUCTOR, 'AAA();');
+  }
+
+  test_CONSTRUCTOR_implicitNew() async {
+    addTestFile('''
+class AAA<T> {
+  AAA() {}
+  AAA.name(p) {}
+}
+main() {
+  AAA<int>();
+  AAA<int>.name(42);
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'AAA<int>.name(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>(');
+    assertHasRegion(HighlightRegionType.CLASS, 'int>.name(');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(p)');
+    assertHasRegion(HighlightRegionType.CONSTRUCTOR, 'name(42)');
   }
 
   test_DIRECTIVE() async {
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 8c45789..95bce6d 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -116,7 +116,8 @@
     //
     // Create server
     //
-    AnalysisServerOptions options = new AnalysisServerOptions();
+    AnalysisServerOptions options = new AnalysisServerOptions()
+      ..previewDart2 = true;
     return new AnalysisServer(
         serverChannel,
         resourceProvider,
diff --git a/pkg/analysis_server/test/domain_analysis_test.dart b/pkg/analysis_server/test/domain_analysis_test.dart
index f79993c..66f1a23 100644
--- a/pkg/analysis_server/test/domain_analysis_test.dart
+++ b/pkg/analysis_server/test/domain_analysis_test.dart
@@ -353,7 +353,7 @@
         serverChannel,
         resourceProvider,
         new MockPackageMapProvider(),
-        new AnalysisServerOptions(),
+        new AnalysisServerOptions()..previewDart2 = true,
         new DartSdkManager('/', false),
         InstrumentationService.NULL_SERVICE);
     handler = new AnalysisDomainHandler(server);
diff --git a/pkg/analysis_server/test/integration/coverage.md b/pkg/analysis_server/test/integration/coverage.md
index 108ef38..8ab1806 100644
--- a/pkg/analysis_server/test/integration/coverage.md
+++ b/pkg/analysis_server/test/integration/coverage.md
@@ -62,6 +62,7 @@
 - [x] search.findMemberDeclarations
 - [x] search.findMemberReferences
 - [x] search.findTopLevelDeclarations
+- [ ] search.getElementDeclarations
 - [x] search.getTypeHierarchy
 - [ ] search.results
 
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index 46763b5..35eca2f 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -1197,6 +1197,27 @@
   }
 
   /**
+   * Return top-level and class member declarations.
+   *
+   * Returns
+   *
+   * declarations: List<ElementDeclaration>
+   *
+   *   The list of declarations.
+   *
+   * files: List<FilePath>
+   *
+   *   The list of the paths of files with declarations.
+   */
+  Future<SearchGetElementDeclarationsResult>
+      sendSearchGetElementDeclarations() async {
+    var result = await server.send("search.getElementDeclarations", null);
+    ResponseDecoder decoder = new ResponseDecoder(null);
+    return new SearchGetElementDeclarationsResult.fromJson(
+        decoder, 'result', result);
+  }
+
+  /**
    * Return the type hierarchy of the class declared or referenced at the given
    * location.
    *
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 91b0f0d..3801787 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -323,6 +323,31 @@
         }));
 
 /**
+ * ElementDeclaration
+ *
+ * {
+ *   "name": String
+ *   "kind": ElementKind
+ *   "fileIndex": int
+ *   "offset": int
+ *   "line": int
+ *   "column": int
+ *   "className": optional String
+ * }
+ */
+final Matcher isElementDeclaration =
+    new LazyMatcher(() => new MatchesJsonObject("ElementDeclaration", {
+          "name": isString,
+          "kind": isElementKind,
+          "fileIndex": isInt,
+          "offset": isInt,
+          "line": isInt,
+          "column": isInt
+        }, optionalFields: {
+          "className": isString
+        }));
+
+/**
  * ElementKind
  *
  * enum {
@@ -2639,6 +2664,25 @@
         "search.findTopLevelDeclarations result", {"id": isSearchId}));
 
 /**
+ * search.getElementDeclarations params
+ */
+final Matcher isSearchGetElementDeclarationsParams = isNull;
+
+/**
+ * search.getElementDeclarations result
+ *
+ * {
+ *   "declarations": List<ElementDeclaration>
+ *   "files": List<FilePath>
+ * }
+ */
+final Matcher isSearchGetElementDeclarationsResult = new LazyMatcher(() =>
+    new MatchesJsonObject("search.getElementDeclarations result", {
+      "declarations": isListOf(isElementDeclaration),
+      "files": isListOf(isFilePath)
+    }));
+
+/**
  * search.getTypeHierarchy params
  *
  * {
diff --git a/pkg/analysis_server/test/plugin/protocol_dart_test.dart b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
index 3e486d5..5eccc11 100644
--- a/pkg/analysis_server/test/plugin/protocol_dart_test.dart
+++ b/pkg/analysis_server/test/plugin/protocol_dart_test.dart
@@ -181,7 +181,7 @@
         findElementInUnit(unit, 'myConstructor');
     // create notification Element
     Element element = convertElement(engineElement);
-    expect(element.parameters, '(int a, {int c, int b})');
+    expect(element.parameters, '(int a, {@required int c, int b})');
   }
 
   // Verify parameter re-ordering for required params
@@ -198,7 +198,8 @@
         findElementInUnit(unit, 'myConstructor');
     // create notification Element
     Element element = convertElement(engineElement);
-    expect(element.parameters, '(int a, {int d, int c, int b})');
+    expect(element.parameters,
+        '(int a, {@required int d, @required int c, int b})');
   }
 
   // Verify parameter re-ordering for required params
@@ -215,7 +216,8 @@
         findElementInUnit(unit, 'myConstructor');
     // create notification Element
     Element element = convertElement(engineElement);
-    expect(element.parameters, '(int a, {int d, int c, int b, int a})');
+    expect(element.parameters,
+        '(int a, {@required int d, @required int c, int b, int a})');
   }
 
   void test_fromElement_dynamic() {
diff --git a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
index 22fa3d4..1c37c8f 100644
--- a/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/override_contributor_test.dart
@@ -41,36 +41,36 @@
     _assertOverride('''@override
   A suggested1(int x) {
     // TODO: implement suggested1
-    return null;
+    return super.suggested1(x);
   }''',
-        displayText: 'suggested1(int x) { ... }',
+        displayText: 'suggested1(int x) { … }',
+        selectionOffset: 74,
+        selectionLength: 27);
+    _assertOverride(
+        '''@override\n  A suggested1(int x) {\n    // TODO: implement suggested1\n    return super.suggested1(x);\n  }''',
+        displayText: 'suggested1(int x) { … }',
+        selectionOffset: 74,
+        selectionLength: 27);
+    _assertOverride(
+        '''@override\n  B suggested2(String y) {\n    // TODO: implement suggested2\n    return super.suggested2(y);\n  }''',
+        displayText: 'suggested2(String y) { … }',
+        selectionOffset: 77,
+        selectionLength: 27);
+    _assertOverride(
+        '''@override\n  C suggested3([String z]) {\n    // TODO: implement suggested3\n    return super.suggested3(z);\n  }''',
+        displayText: 'suggested3([String z]) { … }',
         selectionOffset: 79,
-        selectionLength: 4);
+        selectionLength: 27);
     _assertOverride(
-        '''@override\n  A suggested1(int x) {\n    // TODO: implement suggested1\n    return null;\n  }''',
-        displayText: 'suggested1(int x) { ... }',
-        selectionOffset: 79,
-        selectionLength: 4);
+        '''@override\n  void suggested4() {\n    // TODO: implement suggested4\n    super.suggested4();\n  }''',
+        displayText: 'suggested4() { … }',
+        selectionOffset: 72,
+        selectionLength: 19);
     _assertOverride(
-        '''@override\n  B suggested2(String y) {\n    // TODO: implement suggested2\n    return null;\n  }''',
-        displayText: 'suggested2(String y) { ... }',
-        selectionOffset: 82,
-        selectionLength: 4);
-    _assertOverride(
-        '''@override\n  C suggested3([String z]) {\n    // TODO: implement suggested3\n    return null;\n  }''',
-        displayText: 'suggested3([String z]) { ... }',
-        selectionOffset: 84,
-        selectionLength: 4);
-    _assertOverride(
-        '''@override\n  void suggested4() {\n    // TODO: implement suggested4\n  }''',
-        displayText: 'suggested4() { ... }',
-        selectionOffset: 32,
-        selectionLength: 0);
-    _assertOverride(
-        '''// TODO: implement suggested5\n  @override\n  int get suggested5 => null;''',
-        displayText: 'suggested5 => ...',
-        selectionOffset: 66,
-        selectionLength: 4);
+        '''@override\n  // TODO: implement suggested5\n  int get suggested5 => super.suggested5;''',
+        displayText: 'suggested5 => …',
+        selectionOffset: 68,
+        selectionLength: 16);
   }
 
   test_fromPart() async {
@@ -102,23 +102,23 @@
     _assertOverride('''@override
   A suggested1(int x) {
     // TODO: implement suggested1
-    return null;
-  }''', displayText: 'suggested1(int x) { ... }');
+    return super.suggested1(x);
+  }''', displayText: 'suggested1(int x) { … }');
     _assertOverride(
-        '''@override\n  A suggested1(int x) {\n    // TODO: implement suggested1\n    return null;\n  }''',
-        displayText: 'suggested1(int x) { ... }',
+        '''@override\n  A suggested1(int x) {\n    // TODO: implement suggested1\n    return super.suggested1(x);\n  }''',
+        displayText: 'suggested1(int x) { … }',
+        selectionOffset: 74,
+        selectionLength: 27);
+    _assertOverride(
+        '''@override\n  B suggested2(String y) {\n    // TODO: implement suggested2\n    return super.suggested2(y);\n  }''',
+        displayText: 'suggested2(String y) { … }',
+        selectionOffset: 77,
+        selectionLength: 27);
+    _assertOverride(
+        '''@override\n  C suggested3([String z]) {\n    // TODO: implement suggested3\n    return super.suggested3(z);\n  }''',
+        displayText: 'suggested3([String z]) { … }',
         selectionOffset: 79,
-        selectionLength: 4);
-    _assertOverride(
-        '''@override\n  B suggested2(String y) {\n    // TODO: implement suggested2\n    return null;\n  }''',
-        displayText: 'suggested2(String y) { ... }',
-        selectionOffset: 82,
-        selectionLength: 4);
-    _assertOverride(
-        '''@override\n  C suggested3([String z]) {\n    // TODO: implement suggested3\n    return null;\n  }''',
-        displayText: 'suggested3([String z]) { ... }',
-        selectionOffset: 84,
-        selectionLength: 4);
+        selectionLength: 27);
   }
 
   CompletionSuggestion _assertOverride(String completion,
diff --git a/pkg/analysis_server/test/services/correction/assist_test.dart b/pkg/analysis_server/test/services/correction/assist_test.dart
index 5518646..6865152 100644
--- a/pkg/analysis_server/test/services/correction/assist_test.dart
+++ b/pkg/analysis_server/test/services/correction/assist_test.dart
@@ -3278,7 +3278,7 @@
   main() {
     return new Container(
       child: new Column(
-        children: [
+        children: <Widget>[
           new /*caret*/Text('aaa'),
         ],
       ),
@@ -3316,7 +3316,7 @@
       new Text('aaa'),
 // start
       new Column(
-        children: [
+        children: <Widget>[
           new Text('bbb'),
           new Text('ccc'),
         ],
@@ -3395,7 +3395,7 @@
       new Text('aaa'),
 // start
       new Row(
-        children: [
+        children: <Widget>[
           new Text('bbb'),
           new Text('ccc'),
         ],
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index 7b4f441..1c3bc3b 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -730,6 +730,13 @@
   public void search_findTopLevelDeclarations(String pattern, FindTopLevelDeclarationsConsumer consumer);
 
   /**
+   * {@code search.getElementDeclarations}
+   *
+   * Return top-level and class member declarations.
+   */
+  public void search_getElementDeclarations(GetElementDeclarationsConsumer consumer);
+
+  /**
    * {@code search.getTypeHierarchy}
    *
    * Return the type hierarchy of the class declared or referenced at the given location.
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/ElementDeclaration.java b/pkg/analysis_server/tool/spec/generated/java/types/ElementDeclaration.java
new file mode 100644
index 0000000..da38ec3
--- /dev/null
+++ b/pkg/analysis_server/tool/spec/generated/java/types/ElementDeclaration.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ *
+ * This file has been automatically generated.  Please do not edit it manually.
+ * To regenerate the file, use the script "pkg/analysis_server/tool/spec/generate_files".
+ */
+package org.dartlang.analysis.server.protocol;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import com.google.common.collect.Lists;
+import com.google.dart.server.utilities.general.JsonUtilities;
+import com.google.dart.server.utilities.general.ObjectUtilities;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * A declaration - top-level (class, field, etc) or a class member (method, field, etc).
+ *
+ * @coverage dart.server.generated.types
+ */
+@SuppressWarnings("unused")
+public class ElementDeclaration {
+
+  public static final ElementDeclaration[] EMPTY_ARRAY = new ElementDeclaration[0];
+
+  public static final List<ElementDeclaration> EMPTY_LIST = Lists.newArrayList();
+
+  /**
+   * The name of the declaration.
+   */
+  private final String name;
+
+  /**
+   * The kind of the element that corresponds to the declaration.
+   */
+  private final String kind;
+
+  /**
+   * The index of the file (in the enclosing response).
+   */
+  private final int fileIndex;
+
+  /**
+   * The offset of the declaration name in the file.
+   */
+  private final int offset;
+
+  /**
+   * The one-based index of the line containing the declaration name.
+   */
+  private final int line;
+
+  /**
+   * The one-based index of the column containing the declaration name.
+   */
+  private final int column;
+
+  /**
+   * The name of the class enclosing this declaration. If the declaration is not a class member, this
+   * field will be absent.
+   */
+  private final String className;
+
+  /**
+   * Constructor for {@link ElementDeclaration}.
+   */
+  public ElementDeclaration(String name, String kind, int fileIndex, int offset, int line, int column, String className) {
+    this.name = name;
+    this.kind = kind;
+    this.fileIndex = fileIndex;
+    this.offset = offset;
+    this.line = line;
+    this.column = column;
+    this.className = className;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof ElementDeclaration) {
+      ElementDeclaration other = (ElementDeclaration) obj;
+      return
+        ObjectUtilities.equals(other.name, name) &&
+        ObjectUtilities.equals(other.kind, kind) &&
+        other.fileIndex == fileIndex &&
+        other.offset == offset &&
+        other.line == line &&
+        other.column == column &&
+        ObjectUtilities.equals(other.className, className);
+    }
+    return false;
+  }
+
+  public static ElementDeclaration fromJson(JsonObject jsonObject) {
+    String name = jsonObject.get("name").getAsString();
+    String kind = jsonObject.get("kind").getAsString();
+    int fileIndex = jsonObject.get("fileIndex").getAsInt();
+    int offset = jsonObject.get("offset").getAsInt();
+    int line = jsonObject.get("line").getAsInt();
+    int column = jsonObject.get("column").getAsInt();
+    String className = jsonObject.get("className") == null ? null : jsonObject.get("className").getAsString();
+    return new ElementDeclaration(name, kind, fileIndex, offset, line, column, className);
+  }
+
+  public static List<ElementDeclaration> fromJsonArray(JsonArray jsonArray) {
+    if (jsonArray == null) {
+      return EMPTY_LIST;
+    }
+    ArrayList<ElementDeclaration> list = new ArrayList<ElementDeclaration>(jsonArray.size());
+    Iterator<JsonElement> iterator = jsonArray.iterator();
+    while (iterator.hasNext()) {
+      list.add(fromJson(iterator.next().getAsJsonObject()));
+    }
+    return list;
+  }
+
+  /**
+   * The name of the class enclosing this declaration. If the declaration is not a class member, this
+   * field will be absent.
+   */
+  public String getClassName() {
+    return className;
+  }
+
+  /**
+   * The one-based index of the column containing the declaration name.
+   */
+  public int getColumn() {
+    return column;
+  }
+
+  /**
+   * The index of the file (in the enclosing response).
+   */
+  public int getFileIndex() {
+    return fileIndex;
+  }
+
+  /**
+   * The kind of the element that corresponds to the declaration.
+   */
+  public String getKind() {
+    return kind;
+  }
+
+  /**
+   * The one-based index of the line containing the declaration name.
+   */
+  public int getLine() {
+    return line;
+  }
+
+  /**
+   * The name of the declaration.
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * The offset of the declaration name in the file.
+   */
+  public int getOffset() {
+    return offset;
+  }
+
+  @Override
+  public int hashCode() {
+    HashCodeBuilder builder = new HashCodeBuilder();
+    builder.append(name);
+    builder.append(kind);
+    builder.append(fileIndex);
+    builder.append(offset);
+    builder.append(line);
+    builder.append(column);
+    builder.append(className);
+    return builder.toHashCode();
+  }
+
+  public JsonObject toJson() {
+    JsonObject jsonObject = new JsonObject();
+    jsonObject.addProperty("name", name);
+    jsonObject.addProperty("kind", kind);
+    jsonObject.addProperty("fileIndex", fileIndex);
+    jsonObject.addProperty("offset", offset);
+    jsonObject.addProperty("line", line);
+    jsonObject.addProperty("column", column);
+    if (className != null) {
+      jsonObject.addProperty("className", className);
+    }
+    return jsonObject;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("[");
+    builder.append("name=");
+    builder.append(name + ", ");
+    builder.append("kind=");
+    builder.append(kind + ", ");
+    builder.append("fileIndex=");
+    builder.append(fileIndex + ", ");
+    builder.append("offset=");
+    builder.append(offset + ", ");
+    builder.append("line=");
+    builder.append(line + ", ");
+    builder.append("column=");
+    builder.append(column + ", ");
+    builder.append("className=");
+    builder.append(className);
+    builder.append("]");
+    return builder.toString();
+  }
+
+}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 09fcb67..e448ad2 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -1574,6 +1574,29 @@
       </field>
     </result>
   </request>
+  <request method="getElementDeclarations" experimental="true">
+    <p>
+      Return top-level and class member declarations.
+    </p>
+    <result>
+      <field name="declarations">
+        <list>
+          <ref>ElementDeclaration</ref>
+        </list>
+        <p>
+          The list of declarations.
+        </p>
+      </field>
+      <field name="files">
+        <list>
+          <ref>FilePath</ref>
+        </list>
+        <p>
+          The list of the paths of files with declarations.
+        </p>
+      </field>
+    </result>
+  </request>
   <request method="getTypeHierarchy">
     <p>
       Return the type hierarchy of the class declared or
@@ -2880,6 +2903,57 @@
       </field>
     </object>
   </type>
+  <type name="ElementDeclaration">
+    <p>
+      A declaration - top-level (class, field, etc) or a class member (method,
+      field, etc).
+    </p>
+    <object>
+      <field name="name">
+        <ref>String</ref>
+        <p>
+          The name of the declaration.
+        </p>
+      </field>
+      <field name="kind">
+        <ref>ElementKind</ref>
+        <p>
+          The kind of the element that corresponds to the declaration.
+        </p>
+      </field>
+      <field name="fileIndex">
+        <ref>int</ref>
+        <p>
+          The index of the file (in the enclosing response).
+        </p>
+      </field>
+      <field name="offset">
+        <ref>int</ref>
+        <p>
+          The offset of the declaration name in the file.
+        </p>
+      </field>
+      <field name="line">
+        <ref>int</ref>
+        <p>
+          The one-based index of the line containing the declaration name.
+        </p>
+      </field>
+      <field name="column">
+        <ref>int</ref>
+        <p>
+          The one-based index of the column containing the declaration name.
+        </p>
+      </field>
+      <field name="className" optional="true">
+        <ref>String</ref>
+        <p>
+          The name of the class enclosing this declaration. If the declaration
+          is not a class member, this field will be absent.
+        </p>
+      </field>
+    </object>
+  </type>
   <type name="ExecutableFile">
     <p>
       A description of an executable file.
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 7e44b83..f9a7103 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -26,6 +26,40 @@
 }
 
 /**
+ * An element declaration.
+ */
+class Declaration {
+  final int fileIndex;
+  final String name;
+  final DeclarationKind kind;
+  final int offset;
+  final int line;
+  final int column;
+  final String className;
+
+  Declaration(this.fileIndex, this.name, this.kind, this.offset, this.line,
+      this.column, this.className);
+}
+
+/**
+ * The kind of a [Declaration].
+ */
+enum DeclarationKind {
+  CLASS,
+  CLASS_TYPE_ALIAS,
+  CONSTRUCTOR,
+  ENUM,
+  ENUM_CONSTANT,
+  FIELD,
+  FUNCTION,
+  FUNCTION_TYPE_ALIAS,
+  GETTER,
+  METHOD,
+  SETTER,
+  VARIABLE
+}
+
+/**
  * Search support for an [AnalysisDriver].
  */
 class Search {
@@ -60,6 +94,104 @@
   }
 
   /**
+   * Return top-level and class member declarations.
+   *
+   * The path of each file with at least one declaration is added to [files].
+   * The list is for searched, there might be duplicates, but this is OK,
+   * we just want reduce amount of data, not to make it absolute minimum.
+   */
+  Future<List<Declaration>> declarations(List<String> files) async {
+    List<Declaration> declarations = <Declaration>[];
+
+    DeclarationKind getExecutableKind(
+        UnlinkedExecutable executable, bool topLevel) {
+      switch (executable.kind) {
+        case UnlinkedExecutableKind.constructor:
+          return DeclarationKind.CONSTRUCTOR;
+        case UnlinkedExecutableKind.functionOrMethod:
+          if (topLevel) {
+            return DeclarationKind.FUNCTION;
+          }
+          return DeclarationKind.METHOD;
+        case UnlinkedExecutableKind.getter:
+          return DeclarationKind.GETTER;
+          break;
+        default:
+          return DeclarationKind.SETTER;
+      }
+    }
+
+    for (String path in _driver.addedFiles) {
+      FileState file = _driver.fsState.getFileForPath(path);
+      int fileIndex;
+
+      void addDeclaration(String name, DeclarationKind kind, int offset,
+          [String className]) {
+        if (fileIndex == null) {
+          fileIndex = files.length;
+          files.add(file.path);
+        }
+        if (name.endsWith('=')) {
+          name = name.substring(0, name.length - 1);
+        }
+        var location = file.lineInfo.getLocation(offset);
+        declarations.add(new Declaration(fileIndex, name, kind, offset,
+            location.lineNumber, location.columnNumber, className));
+      }
+
+      for (var class_ in file.unlinked.classes) {
+        String className = class_.name;
+        addDeclaration(
+            className,
+            class_.isMixinApplication
+                ? DeclarationKind.CLASS_TYPE_ALIAS
+                : DeclarationKind.CLASS,
+            class_.nameOffset);
+
+        for (var field in class_.fields) {
+          addDeclaration(
+              field.name, DeclarationKind.FIELD, field.nameOffset, className);
+        }
+
+        for (var executable in class_.executables) {
+          if (executable.name.isNotEmpty) {
+            addDeclaration(
+                executable.name,
+                getExecutableKind(executable, false),
+                executable.nameOffset,
+                className);
+          }
+        }
+      }
+
+      for (var enum_ in file.unlinked.enums) {
+        addDeclaration(enum_.name, DeclarationKind.ENUM, enum_.nameOffset);
+        for (var value in enum_.values) {
+          addDeclaration(
+              value.name, DeclarationKind.ENUM_CONSTANT, value.nameOffset);
+        }
+      }
+
+      for (var executable in file.unlinked.executables) {
+        addDeclaration(executable.name, getExecutableKind(executable, true),
+            executable.nameOffset);
+      }
+
+      for (var typedef_ in file.unlinked.typedefs) {
+        addDeclaration(typedef_.name, DeclarationKind.FUNCTION_TYPE_ALIAS,
+            typedef_.nameOffset);
+      }
+
+      for (var variable in file.unlinked.variables) {
+        addDeclaration(
+            variable.name, DeclarationKind.VARIABLE, variable.nameOffset);
+      }
+    }
+
+    return declarations;
+  }
+
+  /**
    * Returns references to the [element].
    */
   Future<List<SearchResult>> references(Element element) async {
diff --git a/pkg/analyzer/lib/src/fasta/resolution_applier.dart b/pkg/analyzer/lib/src/fasta/resolution_applier.dart
index 3780770..4d2414f 100644
--- a/pkg/analyzer/lib/src/fasta/resolution_applier.dart
+++ b/pkg/analyzer/lib/src/fasta/resolution_applier.dart
@@ -1064,7 +1064,9 @@
       throw new StateError('No type information for $entity at $entityOffset');
     }
     int typeOffset = _typeOffsets[_typeIndex];
-    if (typeOffset != UNKNOWN_OFFSET && entityOffset != typeOffset) {
+    if (typeOffset != UNKNOWN_OFFSET &&
+        entity != null &&
+        entityOffset != typeOffset) {
       throw new StateError('Expected a type for $entity at $entityOffset; '
           'got one for kernel offset $typeOffset');
     }
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 41a029f5..c3376a6 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -1705,6 +1705,7 @@
           _astFactory.constructorName(typeName, node.operator, node.methodName);
     } else {
       // A()
+      node.methodName.staticElement = classElement;
       typeName = _astFactory.typeName(node.methodName, node.typeArguments);
       typeName.type = classType;
       constructorName = _astFactory.constructorName(typeName, null, null);
@@ -1719,6 +1720,9 @@
     instanceCreationExpression.staticElement = constructorElt;
     instanceCreationExpression.staticType = classType;
 
+    node.argumentList.correspondingStaticParameters =
+        _computeCorrespondingParameters(node.argumentList, constructorElt.type);
+
     // Finally, do the node replacement, true is returned iff the replacement
     // was successful, only return the new node if it was successful.
     if (NodeReplacer.replace(node, instanceCreationExpression)) {
@@ -1746,7 +1750,7 @@
         classElement = elt;
       }
     } else {
-      LibraryElementImpl libraryElement = _getImportedLibrary(node.target);
+      LibraryElement libraryElement = _getImportedLibrary(node.target);
       if (libraryElement == null) {
         // We cannot resolve the import to find the library, so we won't be able
         // to find the class to see whether the method is actually a constructor.
@@ -1807,6 +1811,7 @@
     } else {
       // p.A()
       // libraryPrefixId is a SimpleIdentifier in this case
+      node.methodName.staticElement = classElement;
       PrefixedIdentifier prefixedIdentifier = _astFactory.prefixedIdentifier(
           libraryPrefixId, node.operator, node.methodName);
       typeName = _astFactory.typeName(prefixedIdentifier, node.typeArguments);
@@ -1823,6 +1828,9 @@
     instanceCreationExpression.staticElement = constructorElt;
     instanceCreationExpression.staticType = classType;
 
+    node.argumentList.correspondingStaticParameters =
+        _computeCorrespondingParameters(node.argumentList, constructorElt.type);
+
     // Finally, do the node replacement, true is returned iff the replacement
     // was successful, only return the new node if it was successful.
     if (NodeReplacer.replace(node, instanceCreationExpression)) {
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 16db292..41b1d43 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -31,8 +31,6 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/task/dart.dart';
-import 'package:analyzer/src/task/strong/checker.dart' as checker
-    show hasStrictArrow;
 
 /**
  * A visitor used to traverse an AST structure looking for additional errors and
@@ -6341,11 +6339,6 @@
   bool _expressionIsAssignableAtType(Expression expression,
       DartType actualStaticType, DartType expectedStaticType,
       {isDeclarationCast: false}) {
-    bool concrete = _options.strongMode && checker.hasStrictArrow(expression);
-    if (concrete && actualStaticType is FunctionType) {
-      actualStaticType =
-          _typeSystem.functionTypeToConcreteType(actualStaticType);
-    }
     return _typeSystem.isAssignableTo(actualStaticType, expectedStaticType,
         isDeclarationCast: isDeclarationCast);
   }
@@ -6802,10 +6795,6 @@
           FunctionType requiredMemberFT =
               inheritanceManager.substituteTypeArgumentsInMemberFromInheritance(
                   requiredMemberType, memberName, enclosingType);
-          foundConcreteFT =
-              typeSystem.functionTypeToConcreteType(foundConcreteFT);
-          requiredMemberFT =
-              typeSystem.functionTypeToConcreteType(requiredMemberFT);
 
           // Strong mode does override checking for types in CodeChecker, so
           // we can skip it here. Doing it here leads to unnecessary duplicate
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index b23b0c1..7bc0435 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -9795,11 +9795,12 @@
 
   @override
   Object visitGenericFunctionType(GenericFunctionType node) {
-    GenericFunctionTypeElementImpl element =
-        node.type.element as GenericFunctionTypeElementImpl;
-    super.visitGenericFunctionType(node);
-    element.returnType =
-        _computeReturnType(node.returnType) ?? DynamicTypeImpl.instance;
+    GenericFunctionTypeElementImpl element = node.type?.element;
+    if (element != null) {
+      super.visitGenericFunctionType(node);
+      element.returnType =
+          _computeReturnType(node.returnType) ?? DynamicTypeImpl.instance;
+    }
     return null;
   }
 
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index bdefbd8..4ddd3d4 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -19,7 +19,7 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/task/strong/checker.dart'
-    show getDefiniteType, getReadType;
+    show getExpressionType, getReadType;
 
 /**
  * Instances of the class `StaticTypeAnalyzer` perform two type-related tasks. First, they
@@ -1451,8 +1451,8 @@
   void _analyzeLeastUpperBound(
       Expression node, Expression expr1, Expression expr2,
       {bool read: false}) {
-    DartType staticType1 = _getDefiniteType(expr1, read: read);
-    DartType staticType2 = _getDefiniteType(expr2, read: read);
+    DartType staticType1 = _getExpressionType(expr1, read: read);
+    DartType staticType2 = _getExpressionType(expr2, read: read);
     if (staticType1 == null) {
       // TODO(brianwilkerson) Determine whether this can still happen.
       staticType1 = _dynamicType;
@@ -1661,11 +1661,11 @@
    * the most precise type is desired, for example computing the least upper
    * bound.
    *
-   * See [getDefiniteType] for more information. Without strong mode, this is
+   * See [getExpressionType] for more information. Without strong mode, this is
    * equivalent to [_getStaticType].
    */
-  DartType _getDefiniteType(Expression expr, {bool read: false}) =>
-      getDefiniteType(expr, _typeSystem, _typeProvider, read: read);
+  DartType _getExpressionType(Expression expr, {bool read: false}) =>
+      getExpressionType(expr, _typeSystem, _typeProvider, read: read);
 
   /**
    * If the given element name can be mapped to the name of a class defined within the given
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index cf55a77..41f0e15 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -21,19 +21,17 @@
 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
 import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind;
 
-bool _isBottom(DartType t, {bool dynamicIsBottom: false}) {
-  return (t.isDynamic && dynamicIsBottom) ||
-      t.isBottom ||
+bool _isBottom(DartType t) {
+  return t.isBottom ||
       t.isDartCoreNull ||
       identical(t, UnknownInferredType.instance);
 }
 
-bool _isTop(DartType t, {bool dynamicIsBottom: false}) {
-  // TODO(leafp): Document the rules in play here
+bool _isTop(DartType t) {
   if (t.isDartAsyncFutureOr) {
     return _isTop((t as InterfaceType).typeArguments[0]);
   }
-  return (t.isDynamic && !dynamicIsBottom) ||
+  return t.isDynamic ||
       t.isObject ||
       t.isVoid ||
       identical(t, UnknownInferredType.instance);
@@ -107,22 +105,14 @@
     return ft.parameters.any((p) => predicate(p.type));
   }
 
-  @override
-  FunctionType functionTypeToConcreteType(FunctionType t) {
-    // TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function
-    // type? That would allow us to implement this in the subtype relation.
-    // TODO(jmesserly): we'll need to factor this differently if we want to
-    // move CodeChecker's functionality into existing analyzer. Likely we can
-    // let the Expression have a strict arrow, then in places were we do
-    // inference, convert back to a fuzzy arrow.
-
+  FunctionType _replaceDynamicParameters(FunctionType t, DartType replaceWith) {
     if (!t.parameters.any((p) => p.type.isDynamic)) {
       return t;
     }
     ParameterElement shave(ParameterElement p) {
       if (p.type.isDynamic) {
         return new ParameterElementImpl.synthetic(
-            p.name, typeProvider.objectType, p.parameterKind);
+            p.name, replaceWith, p.parameterKind);
       }
       return p;
     }
@@ -136,16 +126,11 @@
     return function.type = new FunctionTypeImpl(function);
   }
 
-  /**
-   * Given a type t, if t is an interface type with a call method
-   * defined, return the definite function type for the call method,
-   * otherwise return null.
-   */
-  FunctionType getCallMethodDefiniteType(DartType t) {
-    var type = getCallMethodType(t);
-    if (type == null) return type;
-    return functionTypeToConcreteType(type);
-  }
+  FunctionType functionTypeToConcreteType(FunctionType t) =>
+      _replaceDynamicParameters(t, typeProvider.objectType);
+
+  FunctionType functionTypeToFuzzyType(FunctionType t) =>
+      _replaceDynamicParameters(t, typeProvider.nullType);
 
   /**
    * Given a type t, if t is an interface type with a call method
@@ -160,8 +145,7 @@
   }
 
   /// Computes the greatest lower bound of [type1] and [type2].
-  DartType getGreatestLowerBound(DartType type1, DartType type2,
-      {dynamicIsBottom: false}) {
+  DartType getGreatestLowerBound(DartType type1, DartType type2) {
     // The greatest lower bound relation is reflexive.
     if (identical(type1, type2)) {
       return type1;
@@ -177,19 +161,16 @@
 
     // For the purpose of GLB, we say some Tops are subtypes (less toppy) than
     // the others. Return the least toppy.
-    if (_isTop(type1, dynamicIsBottom: dynamicIsBottom) &&
-        _isTop(type2, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type1) && _isTop(type2)) {
       return _getTopiness(type1) < _getTopiness(type2) ? type1 : type2;
     }
 
     // The GLB of top and any type is just that type.
     // Also GLB of bottom and any type is bottom.
-    if (_isTop(type1, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(type2, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type1) || _isBottom(type2)) {
       return type2;
     }
-    if (_isTop(type2, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(type1, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type2) || _isBottom(type1)) {
       return type1;
     }
 
@@ -232,8 +213,7 @@
    * Compute the least upper bound of two types.
    */
   @override
-  DartType getLeastUpperBound(DartType type1, DartType type2,
-      {bool dynamicIsBottom: false}) {
+  DartType getLeastUpperBound(DartType type1, DartType type2) {
     if (isNullableType(type1) && isNonNullableType(type2)) {
       assert(type2 is InterfaceType);
       type2 = getLeastNullableSupertype(type2 as InterfaceType);
@@ -242,8 +222,7 @@
       assert(type1 is InterfaceType);
       type1 = getLeastNullableSupertype(type1 as InterfaceType);
     }
-    return super
-        .getLeastUpperBound(type1, type2, dynamicIsBottom: dynamicIsBottom);
+    return super.getLeastUpperBound(type1, type2);
   }
 
   /**
@@ -442,13 +421,17 @@
   @override
   bool isAssignableTo(DartType fromType, DartType toType,
       {bool isDeclarationCast = false}) {
-    // TODO(leafp): Document the rules in play here
-
     // An actual subtype
     if (isSubtypeOf(fromType, toType)) {
       return true;
     }
 
+    // A fuzzy arrow subtype
+    if (toType is FunctionType &&
+        isSubtypeOf(fromType, functionTypeToFuzzyType(toType))) {
+      return true;
+    }
+
     if (isDeclarationCast) {
       if (!declarationCasts) {
         return false;
@@ -485,6 +468,14 @@
       return true;
     }
 
+    // A reverse fuzzy arrow subtype.  We want to disallow this soon, but
+    // we have to let this pass for now because of
+    // https://github.com/dart-lang/sdk/issues/32114
+    if (fromType is FunctionType &&
+        isSubtypeOf(toType, functionTypeToFuzzyType(fromType))) {
+      return true;
+    }
+
     return false;
   }
 
@@ -499,7 +490,7 @@
 
     if (t is FunctionType) {
       if (!_isTop(t.returnType) ||
-          anyParameterType(t, (pt) => !_isBottom(pt, dynamicIsBottom: true))) {
+          anyParameterType(t, (pt) => !_isBottom(pt))) {
         return false;
       } else {
         return true;
@@ -623,14 +614,6 @@
     return null;
   }
 
-  @override
-  DartType typeToConcreteType(DartType t) {
-    if (t is FunctionType) {
-      return functionTypeToConcreteType(t);
-    }
-    return t;
-  }
-
   /// Given a [type] T that may have an unknown type `?`, returns a type
   /// R such that T <: R for any type substituted for `?`.
   ///
@@ -668,7 +651,7 @@
       DartType paramType;
       if (fType != null && gType != null) {
         // If both functions have this parameter, include both of their types.
-        paramType = getLeastUpperBound(fType, gType, dynamicIsBottom: true);
+        paramType = getLeastUpperBound(fType, gType);
       } else {
         paramType = fType ?? gType;
       }
@@ -750,7 +733,7 @@
 
   @override
   DartType _functionParameterBound(DartType f, DartType g) =>
-      getGreatestLowerBound(f, g, dynamicIsBottom: true);
+      getGreatestLowerBound(f, g);
 
   /// Given a type return its name prepended with the URI to its containing
   /// library and separated by a comma.
@@ -832,9 +815,8 @@
   bool _isFunctionSubtypeOf(
       FunctionType f1, FunctionType f2, Set<TypeImpl> visitedTypes) {
     return FunctionTypeImpl.relate(f1, f2, isSubtypeOf, instantiateToBounds,
-        parameterRelation: (p1, p2) => _isSubtypeOf(
-            p2.type, p1.type, visitedTypes,
-            dynamicIsBottom: true));
+        parameterRelation: (p1, p2) =>
+            _isSubtypeOf(p2.type, p1.type, visitedTypes));
   }
 
   bool _isInterfaceSubtypeOf(
@@ -892,8 +874,7 @@
     return false;
   }
 
-  bool _isSubtypeOf(DartType t1, DartType t2, Set<TypeImpl> visitedTypes,
-      {bool dynamicIsBottom: false}) {
+  bool _isSubtypeOf(DartType t1, DartType t2, Set<TypeImpl> visitedTypes) {
     if (identical(t1, t2)) {
       return true;
     }
@@ -907,14 +888,12 @@
     //
     // Note that `?` is treated as a top and a bottom type during inference,
     // so it's also covered here.
-    if (_isTop(t2, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(t1, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(t2) || _isBottom(t1)) {
       return true;
     }
 
     // Trivially false.
-    if (_isTop(t1, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(t2, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(t1) || _isBottom(t2)) {
       return false;
     }
 
@@ -979,7 +958,7 @@
     // the interface type declares a call method with a type
     // which is a super type of the function type.
     if (t1 is InterfaceType && t2 is FunctionType) {
-      var callType = getCallMethodDefiniteType(t1);
+      var callType = getCallMethodType(t1);
       return callType != null &&
           guardedIsFunctionSubtype(callType, t2, visitedTypes);
     }
@@ -992,10 +971,9 @@
     return guardedIsFunctionSubtype(t1, t2, visitedTypes);
   }
 
-  DartType _substituteForUnknownType(DartType type,
-      {bool lowerBound: false, dynamicIsBottom: false}) {
+  DartType _substituteForUnknownType(DartType type, {bool lowerBound: false}) {
     if (identical(type, UnknownInferredType.instance)) {
-      if (lowerBound && !dynamicIsBottom) {
+      if (lowerBound) {
         // TODO(jmesserly): this should be the bottom type, once i can be
         // reified.
         return typeProvider.nullType;
@@ -1015,9 +993,8 @@
       var returnType = type.returnType;
       var newParameters = _transformList(parameters, (ParameterElement p) {
         // Parameters are contravariant, so flip the constraint direction.
-        // Also pass dynamicIsBottom, because this is a fuzzy arrow.
-        var newType = _substituteForUnknownType(p.type,
-            lowerBound: !lowerBound, dynamicIsBottom: true);
+        var newType =
+            _substituteForUnknownType(p.type, lowerBound: !lowerBound);
         return new ParameterElementImpl.synthetic(
             p.name, newType, p.parameterKind);
       });
@@ -1141,28 +1118,9 @@
   TypeProvider get typeProvider;
 
   /**
-   * Make a function type concrete.
-   *
-   * Normally we treat dynamically typed parameters as bottom for function
-   * types. This allows type tests such as `if (f is SingleArgFunction)`.
-   * It also requires a dynamic check on the parameter type to call these
-   * functions.
-   *
-   * When we convert to a strict arrow, dynamically typed parameters become
-   * top. This is safe to do for known functions, like top-level or local
-   * functions and static methods. Those functions must already be essentially
-   * treating dynamic as top.
-   *
-   * Only the outer-most arrow can be strict. Any others must be fuzzy, because
-   * we don't know what function value will be passed there.
-   */
-  FunctionType functionTypeToConcreteType(FunctionType t);
-
-  /**
    * Compute the least upper bound of two types.
    */
-  DartType getLeastUpperBound(DartType type1, DartType type2,
-      {bool dynamicIsBottom: false}) {
+  DartType getLeastUpperBound(DartType type1, DartType type2) {
     // The least upper bound relation is reflexive.
     if (identical(type1, type2)) {
       return type1;
@@ -1178,19 +1136,16 @@
 
     // For the purpose of LUB, we say some Tops are subtypes (less toppy) than
     // the others. Return the most toppy.
-    if (_isTop(type1, dynamicIsBottom: dynamicIsBottom) &&
-        _isTop(type2, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type1) && _isTop(type2)) {
       return _getTopiness(type1) > _getTopiness(type2) ? type1 : type2;
     }
 
     // The least upper bound of top and any type T is top.
     // The least upper bound of bottom and any type T is T.
-    if (_isTop(type1, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(type2, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type1) || _isBottom(type2)) {
       return type1;
     }
-    if (_isTop(type2, dynamicIsBottom: dynamicIsBottom) ||
-        _isBottom(type1, dynamicIsBottom: dynamicIsBottom)) {
+    if (_isTop(type2) || _isBottom(type1)) {
       return type2;
     }
 
@@ -1398,14 +1353,6 @@
       TypeParameterTypeImpl.getTypes(typeFormalsAsElements(type));
 
   /**
-   * Make a type concrete.  A type is concrete if it is not a function
-   * type, or if it is a function type with no dynamic parameters.  A
-   * non-concrete function type is made concrete by replacing dynamic
-   * parameters with Object.
-   */
-  DartType typeToConcreteType(DartType t);
-
-  /**
    * Compute the least upper bound of function types [f] and [g].
    *
    * The spec rules for LUB on function types, informally, are pretty simple
@@ -1522,8 +1469,6 @@
   @override
   bool get isStrong => false;
 
-  FunctionType functionTypeToConcreteType(FunctionType t) => t;
-
   /**
    * Instantiate a parameterized type using `dynamic` for all generic
    * parameters.  Returns the type unchanged if there are no parameters.
@@ -1567,9 +1512,6 @@
   }
 
   @override
-  DartType typeToConcreteType(DartType t) => t;
-
-  @override
   DartType _interfaceLeastUpperBound(InterfaceType type1, InterfaceType type2) {
     InterfaceType result =
         InterfaceTypeImpl.computeLeastUpperBound(type1, type2);
@@ -2098,9 +2040,7 @@
   /// or return type.
   void _matchSubtypeOf(DartType t1, DartType t2, Set<Element> visited,
       _TypeConstraintOrigin origin,
-      {bool covariant, bool dynamicIsBottom: false}) {
-    // TODO(jmesserly): I think we should handle `dynamicIsBottom`
-    // https://github.com/dart-lang/sdk/issues/29041
+      {bool covariant}) {
     if (covariant && t1 is TypeParameterType) {
       var constraints = _constraints[t1.element];
       if (constraints != null) {
@@ -2202,7 +2142,7 @@
     // the interface type declares a call method with a type
     // which is a super type of the function type.
     if (t1 is InterfaceType) {
-      t1 = _typeSystem.getCallMethodDefiniteType(t1);
+      t1 = _typeSystem.getCallMethodType(t1);
       if (t1 == null) return;
     }
 
@@ -2219,7 +2159,7 @@
           _typeSystem.instantiateToBounds,
           parameterRelation: (p1, p2) {
             _matchSubtypeOf(p2.type, p1.type, null, origin,
-                covariant: !covariant, dynamicIsBottom: true);
+                covariant: !covariant);
             return true;
           });
     }
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 4b1f7a8..6d21ed4 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -30,14 +30,7 @@
 
 /// Given an [expression] and a corresponding [typeSystem] and [typeProvider],
 /// gets the known static type of the expression.
-///
-/// Normally when we ask for an expression's type, we get the type of the
-/// storage slot that would contain it. For function types, this is necessarily
-/// a "fuzzy arrow" that treats `dynamic` as bottom. However, if we're
-/// interested in the expression's own type, it can often be a "strict arrow"
-/// because we know it evaluates to a specific, concrete function, and we can
-/// treat "dynamic" as top for that case, which is more permissive.
-DartType getDefiniteType(
+DartType getExpressionType(
     Expression expression, TypeSystem typeSystem, TypeProvider typeProvider,
     {bool read: false}) {
   DartType type;
@@ -47,12 +40,6 @@
     type = expression.staticType;
   }
   type ??= DynamicTypeImpl.instance;
-  if (typeSystem is StrongTypeSystemImpl &&
-      type is FunctionType &&
-      hasStrictArrow(expression)) {
-    // Remove fuzzy arrow if possible.
-    return typeSystem.functionTypeToConcreteType(type);
-  }
   return type;
 }
 
@@ -90,6 +77,19 @@
   return element is FunctionElement || element is MethodElement;
 }
 
+/// Summarizes the assignability relationship between two types
+/// considering both the fuzzy arrow semantics and the sound
+/// semantics.  Summary indicates whether 1) a cast is needed
+/// on assignment, and 2) whether a fuzzy arrow warning should
+/// be emitted.
+enum _FuzzyStatus {
+  needsCastFuzzy, // Needs a cast, relies on fuzzy arrows
+  needsCastSound, // Needs a cast, no fuzzy arrows
+  noCastFuzzy, // No cast, but uses fuzzy arrows
+  noCastSound, // No cast, not fuzzy arrows
+  unrelated // Not assignable as function types
+}
+
 /// Given a generic class [element] find its covariant upper bound, using
 /// the type system [rules].
 ///
@@ -268,7 +268,7 @@
     }
   }
 
-  DartType getType(TypeAnnotation type) {
+  DartType getAnnotatedType(TypeAnnotation type) {
     return type?.type ?? DynamicTypeImpl.instance;
   }
 
@@ -290,7 +290,7 @@
     TokenType operatorType = operator.type;
     if (operatorType == TokenType.EQ ||
         operatorType == TokenType.QUESTION_QUESTION_EQ) {
-      DartType staticType = _getDefiniteType(node.leftHandSide);
+      DartType staticType = _getExpressionType(node.leftHandSide);
       checkAssignment(node.rightHandSide, staticType);
     } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ ||
         operatorType == TokenType.BAR_BAR_EQ) {
@@ -455,7 +455,7 @@
       var sequenceInterface = node.awaitKeyword != null
           ? typeProvider.streamType
           : typeProvider.iterableType;
-      var iterableType = _getDefiniteType(node.iterable);
+      var iterableType = _getExpressionType(node.iterable);
       var elementType =
           rules.mostSpecificTypeArgument(iterableType, sequenceInterface);
 
@@ -477,7 +477,7 @@
       if (elementType != null) {
         // Insert a cast from the sequence's element type to the loop variable's
         // if needed.
-        _checkImplicitCast(loopVariable, _getDefiniteType(loopVariable),
+        _checkImplicitCast(loopVariable, _getExpressionType(loopVariable),
             from: elementType, isDeclarationCast: true);
       }
     }
@@ -767,8 +767,8 @@
       assert(functionType.optionalParameterTypes.isEmpty);
 
       // Refine the return type.
-      var rhsType = _getDefiniteType(expr.rightHandSide);
-      var lhsType = _getDefiniteType(expr.leftHandSide);
+      var rhsType = _getExpressionType(expr.rightHandSide);
+      var lhsType = _getExpressionType(expr.leftHandSide);
       var returnType = rules.refineBinaryExpressionType(
           lhsType, op, rhsType, functionType.returnType);
 
@@ -807,9 +807,7 @@
   /// [to] or is already a subtype of it, does nothing.
   void _checkImplicitCast(Expression expr, DartType to,
       {DartType from, bool opAssign: false, bool isDeclarationCast: false}) {
-    from ??= _getDefiniteType(expr);
-
-    _hintOnFuzzyArrows(expr, to, from);
+    from ??= _getExpressionType(expr);
 
     if (_needsImplicitCast(expr, to,
             from: from, isDeclarationCast: isDeclarationCast) ==
@@ -846,7 +844,7 @@
   }
 
   void _checkRuntimeTypeCheck(AstNode node, TypeAnnotation annotation) {
-    var type = getType(annotation);
+    var type = getAnnotatedType(annotation);
     if (!rules.isGroundType(type)) {
       _recordMessage(node, StrongModeCode.NON_GROUND_TYPE_CHECK_INFO, [type]);
     }
@@ -869,7 +867,7 @@
         // Refine the return type.
         var functionType = element.type;
         var rhsType = typeProvider.intType;
-        var lhsType = _getDefiniteType(operand);
+        var lhsType = _getExpressionType(operand);
         var returnType = rules.refineBinaryExpressionType(
             lhsType, TokenType.PLUS, rhsType, functionType.returnType);
 
@@ -888,8 +886,8 @@
     }
   }
 
-  DartType _getDefiniteType(Expression expr) =>
-      getDefiniteType(expr, rules, typeProvider);
+  DartType _getExpressionType(Expression expr) =>
+      getExpressionType(expr, rules, typeProvider);
 
   /// If we're calling into [member] through the [target], we may need to
   /// insert a caller side check for soundness on the result of the expression
@@ -1091,7 +1089,7 @@
     if (type is FunctionType) {
       return type;
     } else if (type is InterfaceType) {
-      return rules.getCallMethodDefiniteType(type);
+      return rules.getCallMethodType(type);
     }
     return null;
   }
@@ -1099,10 +1097,6 @@
   /// Returns `true` if the expression is a dynamic function call or method
   /// invocation.
   bool _isDynamicCall(InvocationExpression call, FunctionType ft) {
-    // TODO(leafp): This will currently return true if t is Function
-    // This is probably the most correct thing to do for now, since
-    // this code is also used by the back end.  Maybe revisit at some
-    // point?
     if (ft == null) return true;
     // Dynamic as the parameter type is treated as bottom.  A function with
     // a dynamic parameter type requires a dynamic call in general.
@@ -1111,40 +1105,109 @@
     if (hasStrictArrow(call.function)) {
       return false;
     }
+    // TODO(leafp): Get rid of this case when we stop ignoring fuzzy arrows.
+    // We're already not doing this everywhere we would need to for soundness
+    // (because of generics), but for now we catch most cases here.
     return rules.anyParameterType(ft, (pt) => pt.isDynamic);
   }
 
-  void _hintOnFuzzyArrows(Expression expr, DartType to, DartType from) {
-    // If it is a subtype with fuzzy arrows on,
-    // check to see if it still is with them off.
-    if (rules.isSubtypeOf(from, to)) {
-      // Remove fuzzy arrows
-      var cFrom = rules.typeToConcreteType(from);
-      var cTo = rules.typeToConcreteType(to);
-      // If still true, no warning needed
-      if (rules.isSubtypeOf(cFrom, cTo)) return;
-      _recordMessage(expr, StrongModeCode.USES_DYNAMIC_AS_BOTTOM, [from, to]);
+  /// Given an expression [expr] of type [fromType], summarize what casts
+  /// and fuzzy arrow hints (if any) should be emitted when assigning to
+  /// a location of type [to]
+  _FuzzyStatus _checkFuzzyStatus(
+      Expression expr, FunctionType to, DartType fromType) {
+    var strict = hasStrictArrow(expr);
+    var toFuzzy = rules.functionTypeToFuzzyType(to);
+    FunctionType from;
+    if (fromType is FunctionType) {
+      from = fromType;
+    } else if (fromType is InterfaceType) {
+      from = rules.getCallMethodType(fromType);
+      // Methods are always strict
+      strict = true;
     }
+    if (from == null) {
+      return _FuzzyStatus.unrelated;
+    }
+
+    var fromFuzzy = strict ? from : rules.functionTypeToFuzzyType(from);
+
+    if (rules.isSubtypeOf(from, to)) {
+      // Sound subtype, so no fuzzy arrow warning
+      if (rules.isSubtypeOf(fromFuzzy, toFuzzy)) {
+        // No cast needed for fuzzy arrows
+        return _FuzzyStatus.noCastSound;
+      } else {
+        // Sound, but need cast because the from type
+        // is fuzzy, and the to type isn't.
+        return _FuzzyStatus.needsCastSound;
+      }
+    }
+
+    // If it's a subtype in the fuzzy system, don't cast, since we do
+    // dynamic calls for the check.
+    if (rules.isSubtypeOf(fromFuzzy, toFuzzy)) {
+      // A subtype in the fuzzy system, but reverse subtype in sound system
+      // This will eventually become a downcast (which will probably fail).
+      // But for the transition, it is better to issue a fuzzy arrow hint
+      // since we still do the dynamic calls anyway
+      return _FuzzyStatus.noCastFuzzy;
+    }
+
+    if (rules.isSubtypeOf(to, from)) {
+      // Assignable in the sound system, but needs cast
+      // Either unrelated in fuzzy system, or already a cast, so just cast
+      // and don't warn
+      return _FuzzyStatus.needsCastSound;
+    }
+
+    // Only assignable with fuzzy arrows, and it needs a cast
+    if (rules.isSubtypeOf(toFuzzy, fromFuzzy)) {
+      return _FuzzyStatus.needsCastFuzzy;
+    }
+
+    return _FuzzyStatus.unrelated;
   }
 
   /// Returns true if we need an implicit cast of [expr] from [from] type to
   /// [to] type, returns false if no cast is needed, and returns null if the
   /// types are statically incompatible, or the types are compatible but don't
-  /// allow implicit cast (ie, void, which is one form of Top which oill not
+  /// allow implicit cast (ie, void, which is one form of Top which will not
   /// downcast implicitly).
   ///
   /// If [from] is omitted, uses the static type of [expr]
   bool _needsImplicitCast(Expression expr, DartType to,
       {DartType from, bool isDeclarationCast: false}) {
-    from ??= _getDefiniteType(expr);
+    from ??= _getExpressionType(expr);
 
     if (!_checkNonNullAssignment(expr, to, from)) return false;
 
     // Void is considered Top, but may only be *explicitly* cast.
     if (from.isVoid) return null;
 
+    if (to is FunctionType) {
+      switch (_checkFuzzyStatus(expr, to, from)) {
+        case _FuzzyStatus.noCastFuzzy:
+          _recordMessage(
+              expr, StrongModeCode.USES_DYNAMIC_AS_BOTTOM, [from, to]);
+          return false;
+        case _FuzzyStatus.needsCastFuzzy:
+          _recordMessage(
+              expr, StrongModeCode.USES_DYNAMIC_AS_BOTTOM, [from, to]);
+          return true;
+        case _FuzzyStatus.noCastSound:
+          return false;
+        case _FuzzyStatus.needsCastSound:
+          return true;
+        default:
+          break;
+      }
+    }
+
     // fromT <: toT, no coercion needed.
-    if (rules.isSubtypeOf(from, to)) return false;
+    if (rules.isSubtypeOf(from, to)) {
+      return false;
+    }
 
     // Down cast or legal sideways cast, coercion needed.
     if (rules.isAssignableTo(from, to, isDeclarationCast: isDeclarationCast))
@@ -1173,12 +1236,29 @@
     if (target != null) setIsDynamicInvoke(target, true);
   }
 
+  void _markImplicitCast(Expression expr, DartType to, {bool opAssign: false}) {
+    if (opAssign) {
+      setImplicitOperationCast(expr, to);
+    } else {
+      setImplicitCast(expr, to);
+    }
+    _hasImplicitCasts = true;
+  }
+
   /// Records an implicit cast for the [expr] from [from] to [to].
   ///
   /// This will emit the appropriate error/warning/hint message as well as mark
   /// the AST node.
   void _recordImplicitCast(Expression expr, DartType to,
       {DartType from, bool opAssign: false}) {
+    // We place record some legacy casts on places that had them for fuzzy
+    // arrows, but where they aren't required using sound subtyping.  We
+    // don't want to issue any of the warnings below for these.
+    if (rules.isSubtypeOf(from, to)) {
+      _markImplicitCast(expr, to, opAssign: opAssign);
+      return;
+    }
+
     // Inference "casts":
     if (expr is Literal) {
       // fromT should be an exact type - this will almost certainly fail at
@@ -1254,12 +1334,7 @@
           : StrongModeCode.DOWN_CAST_IMPLICIT;
     }
     _recordMessage(expr, errorCode, [from, to]);
-    if (opAssign) {
-      setImplicitOperationCast(expr, to);
-    } else {
-      setImplicitCast(expr, to);
-    }
-    _hasImplicitCasts = true;
+    _markImplicitCast(expr, to, opAssign: opAssign);
   }
 
   void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
diff --git a/pkg/analyzer/test/generated/checked_mode_compile_time_error_code_test.dart b/pkg/analyzer/test/generated/checked_mode_compile_time_error_code_test.dart
index 746ca53..4bd1324 100644
--- a/pkg/analyzer/test/generated/checked_mode_compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/checked_mode_compile_time_error_code_test.dart
@@ -192,7 +192,7 @@
 foo(x) => 1;
 var v = const A(foo);''');
     await computeAnalysisResult(source);
-    assertErrors(source, [StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE]);
+    assertErrors(source, [StrongModeCode.INVALID_CAST_FUNCTION]);
     verify([source]);
   }
 
diff --git a/pkg/analyzer/test/generated/element_resolver_test.dart b/pkg/analyzer/test/generated/element_resolver_test.dart
index aaf88b9..a766bdc4 100644
--- a/pkg/analyzer/test/generated/element_resolver_test.dart
+++ b/pkg/analyzer/test/generated/element_resolver_test.dart
@@ -25,6 +25,7 @@
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../utils.dart';
 import 'analysis_context_factory.dart';
 import 'resolver_test_case.dart';
 import 'test_support.dart';
@@ -37,6 +38,10 @@
   });
 }
 
+const _isClassElement = const isInstanceOf<ClassElement>();
+
+const _isConstructorElement = const isInstanceOf<ConstructorElement>();
+
 /// Wrapper around the test package's `fail` function.
 ///
 /// Unlike the test package's `fail` function, this function is not annotated
@@ -1301,6 +1306,9 @@
 @reflectiveTest
 class PreviewDart2Test extends ResolverTestCase {
   @override
+  bool get enableNewAnalysisDriver => true;
+
+  @override
   void setUp() {
     AnalysisOptionsImpl options = new AnalysisOptionsImpl()
       ..previewDart2 = true;
@@ -1318,20 +1326,24 @@
   A() {}
 }
 main() {
-  var v = new A(); // marker
+  new A();
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName = findMarkedIdentifier(code, unit, "(); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // unnamed constructor:
-    expect(instanceCreationExpression.constructorName.name, isNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+
+    expect(creation.staticElement, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, _isConstructorElement);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name, isNull);
   }
 
   /**
@@ -1344,23 +1356,34 @@
   test_visitMethodInvocations_implicit() async {
     String code = '''
 class A {
-  A() {}
+  A(a, {b}) {}
 }
 main() {
-  var v = A(); // marker
+  A(0, b: 1);
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName = findMarkedIdentifier(code, unit, "(); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // unnamed constructor:
-    expect(instanceCreationExpression.constructorName.name, isNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name, isNull);
+
+    List<Expression> arguments = creation.argumentList.arguments;
+    Expression argumentA = arguments[0];
+    expect(argumentA.staticParameterElement, constructor.parameters[0]);
+    NamedExpression argumentB = arguments[1];
+    expect(argumentB.name.label.staticElement, constructor.parameters[1]);
   }
 
   /**
@@ -1376,21 +1399,25 @@
   A.named() {}
 }
 main() {
-  var v = A.named(); // marker
+  A.named();
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName = findMarkedIdentifier(code, unit, "(); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // named constructor:
-    expect(instanceCreationExpression.constructorName.name.staticElement,
-        isNotNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name.staticElement, constructor);
   }
 
   /**
@@ -1410,20 +1437,25 @@
 import 'fileOne.dart' as one;
 
 main() {
-  var v = one.A(); // marker
+  one.A();
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName = findMarkedIdentifier(code, unit, "(); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // unnamed constructor:
-    expect(instanceCreationExpression.constructorName.name, isNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name, isNull);
   }
 
   /**
@@ -1436,27 +1468,37 @@
   test_visitMethodInvocations_implicit_prefixed_named() async {
     addNamedSource("/fileOne.dart", r'''
 class A {
-  A.named() {}
+  A.named(a, {b}) {}
 }
 ''');
     String code = '''
 import 'fileOne.dart' as one;
 main() {
-  var v = one.A.named(); // marker
+  one.A.named(0, b: 1);
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName = findMarkedIdentifier(code, unit, "(); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // named constructor:
-    expect(instanceCreationExpression.constructorName.name.staticElement,
-        isNotNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name.staticElement, constructor);
+
+    List<Expression> arguments = creation.argumentList.arguments;
+    Expression argumentA = arguments[0];
+    expect(argumentA.staticParameterElement, constructor.parameters[0]);
+    NamedExpression argumentB = arguments[1];
+    expect(argumentB.name.label.staticElement, constructor.parameters[1]);
   }
 
   /**
@@ -1477,21 +1519,25 @@
 import 'fileOne.dart' as one;
 
 main() {
-  var v = one.A<int>(42); // marker
+  one.A<int>(42);
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName =
-        findMarkedIdentifier(code, unit, "<int>(42); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // unnamed constructor:
-    expect(instanceCreationExpression.constructorName.name, isNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name, isNull);
   }
 
   /**
@@ -1508,21 +1554,44 @@
   A(this.x) {}
 }
 main() {
-  var v = A<int>(42); // marker
+  A<int>(42);
 }
     ''';
     CompilationUnit unit = await resolveSource(code);
-    AstNode constructorName =
-        findMarkedIdentifier(code, unit, "<int>(42); // marker");
-    InstanceCreationExpression instanceCreationExpression =
-        constructorName.parent.parent.parent;
-    expect(instanceCreationExpression, isNotNull);
-    expect(instanceCreationExpression.constructorName.type.type, isNotNull);
-    expect(instanceCreationExpression.constructorName.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticElement, isNotNull);
-    expect(instanceCreationExpression.staticType, isNotNull);
-    // unnamed constructor:
-    expect(instanceCreationExpression.constructorName.name, isNull);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    InstanceCreationExpression creation = statement.expression;
+    ConstructorElement constructor = creation.staticElement;
+
+    expect(constructor, _isConstructorElement);
+    expect(creation.staticType, isNotNull);
+
+    expect(creation.constructorName.staticElement, constructor);
+
+    expect(creation.constructorName.type.type, isNotNull);
+    expect(creation.constructorName.type.name.staticElement, _isClassElement);
+
+    expect(creation.constructorName.name, isNull);
+  }
+
+  test_visitMethodInvocations_importPrefix_function() async {
+    String code = '''
+import 'dart:math' as ma;
+main() {
+  ma.max(1, 2); // marker
+}
+''';
+    CompilationUnit unit = await resolveSource(code);
+    var statements = AstFinder.getStatementsInTopLevelFunction(unit, 'main');
+
+    ExpressionStatement statement = statements[0];
+    MethodInvocation invocation = statement.expression;
+
+    SimpleIdentifier prefix = invocation.target;
+    expect(prefix.staticElement, new isInstanceOf<PrefixElement>());
+
+    expect(invocation.methodName.name, 'max');
   }
 
   /**
diff --git a/pkg/analyzer/test/generated/invalid_code_kernel_test.dart b/pkg/analyzer/test/generated/invalid_code_kernel_test.dart
index bf1cd61..70278fd 100644
--- a/pkg/analyzer/test/generated/invalid_code_kernel_test.dart
+++ b/pkg/analyzer/test/generated/invalid_code_kernel_test.dart
@@ -28,4 +28,10 @@
   test_constructorAndMethodNameCollision() async {
     return super.test_constructorAndMethodNameCollision();
   }
+
+  @failingTest
+  @override
+  test_genericFunction_asTypeArgument_ofUnresolvedClass() async {
+    return super.test_genericFunction_asTypeArgument_ofUnresolvedClass();
+  }
 }
diff --git a/pkg/analyzer/test/generated/invalid_code_test.dart b/pkg/analyzer/test/generated/invalid_code_test.dart
index a26eaa0..686290a 100644
--- a/pkg/analyzer/test/generated/invalid_code_test.dart
+++ b/pkg/analyzer/test/generated/invalid_code_test.dart
@@ -43,13 +43,14 @@
 ''');
   }
 
-  Future<Null> _assertCanBeAnalyzed(String text) async {
-    Source source = addSource('''
-class C {
-  var f = { : };
-  @ ();
-}
+  test_genericFunction_asTypeArgument_ofUnresolvedClass() async {
+    await _assertCanBeAnalyzed(r'''
+C<int Function()> c;
 ''');
+  }
+
+  Future<Null> _assertCanBeAnalyzed(String text) async {
+    Source source = addSource(text);
     var analysisResult = await computeAnalysisResult(source);
     expect(analysisResult.errors, isNotEmpty);
   }
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 38ec9be..4644244 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -389,7 +389,7 @@
     String code = r'''
     typedef To Func1<From, To>(From x);
     T f<T extends Func1<S, S>, S>(S x) => null;
-    void test() { var x = f(3)(4); }
+    void test() { var x = f(3)(null); }
    ''';
     Source source = addSource(code);
     TestAnalysisResult analysisResult = await computeAnalysisResult(source);
@@ -507,6 +507,11 @@
     new C<Object>.fact().g;
     new C<Object>.fact().m1;
     new C<Object>.fact().m2();
+
+    new C.fact().f(42);
+    new C.fact().g;
+    new C.fact().m1;
+    new C.fact().m2();
   }
 
   noCasts(T t) {
@@ -544,12 +549,6 @@
     new D().g;
     new D().m1();
     new D().m2();
-
-    // fuzzy arrows are currently checked at the call, they skip this cast.
-    new C.fact().f(42);
-    new C.fact().g;
-    new C.fact().m1;
-    new C.fact().m2();
   }
 }
 class D extends C<num> {
@@ -610,6 +609,12 @@
   (c.m1)();
   c.m1()(obj);
   (c.m2)();
+
+  cD.f;
+  cD.g;
+  cD.m1;
+  cD.m1();
+  cD.m2();
 }
 
 noCasts() {
@@ -631,13 +636,6 @@
   cN.m1;
   cN.m1();
   cN.m2();
-
-  // fuzzy arrows are currently checked at the call, they skip this cast.
-  cD.f;
-  cD.g;
-  cD.m1;
-  cD.m1();
-  cD.m2();
 }
 ''');
     var unit = (await computeAnalysisResult(source)).unit;
@@ -1535,10 +1533,10 @@
 }
  ''');
     await computeAnalysisResult(source);
-    _expectInferenceError(source, [
-      StrongModeCode.COULD_NOT_INFER,
-      StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
-    ], r'''
+    _expectInferenceError(
+        source,
+        [StrongModeCode.COULD_NOT_INFER, StrongModeCode.INVALID_CAST_FUNCTION],
+        r'''
 Couldn't infer type parameter 'T'.
 
 Tried to infer 'dynamic' for 'T' which doesn't work:
@@ -1706,10 +1704,8 @@
 num test(Iterable values) => values.fold(values.first as num, max);
     ''');
     var analysisResult = await computeAnalysisResult(source);
-    assertErrors(source, [
-      StrongModeCode.COULD_NOT_INFER,
-      StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE
-    ]);
+    assertErrors(source,
+        [StrongModeCode.COULD_NOT_INFER, StrongModeCode.INVALID_CAST_FUNCTION]);
     verify([source]);
     var unit = analysisResult.unit;
     var fold = (AstFinder
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index c77c065..df74bd4 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -652,19 +652,6 @@
     _checkGroups(dynamicType, interassignable: interassignable);
   }
 
-  void test_isAssignableTo_fuzzy_arrows() {
-    FunctionType top = TypeBuilder
-        .function(required: <DartType>[dynamicType], result: objectType);
-    FunctionType left = TypeBuilder
-        .function(required: <DartType>[objectType], result: objectType);
-    FunctionType right = TypeBuilder
-        .function(required: <DartType>[dynamicType], result: bottomType);
-    FunctionType bottom = TypeBuilder
-        .function(required: <DartType>[objectType], result: bottomType);
-
-    _checkCrossLattice(top, left, right, bottom);
-  }
-
   void test_isAssignableTo_generics() {
     ClassElementImpl LClass = ElementFactory.classElement2('L', ["T"]);
     InterfaceType LType = LClass.type;
@@ -1328,10 +1315,10 @@
     _checkGreatestLowerBound(type1, type2, expected);
   }
 
-  void test_functionsFuzzyArrows() {
+  void test_functionsFromDynamic() {
     FunctionType type1 = _functionType([dynamicType]);
     FunctionType type2 = _functionType([intType]);
-    FunctionType expected = _functionType([intType]);
+    FunctionType expected = _functionType([dynamicType]);
     _checkGreatestLowerBound(type1, type2, expected);
   }
 
@@ -1507,7 +1494,7 @@
   void test_functionsFuzzyArrows() {
     FunctionType type1 = _functionType([dynamicType]);
     FunctionType type2 = _functionType([intType]);
-    FunctionType expected = _functionType([dynamicType]);
+    FunctionType expected = _functionType([intType]);
     _checkLeastUpperBound(type1, type2, expected);
   }
 
@@ -1702,19 +1689,6 @@
     _checkGroups(voidType, equivalents: equivalents, subtypes: subtypes);
   }
 
-  void test_fuzzy_arrows() {
-    FunctionType top = TypeBuilder
-        .function(required: <DartType>[dynamicType], result: objectType);
-    FunctionType left = TypeBuilder
-        .function(required: <DartType>[objectType], result: objectType);
-    FunctionType right = TypeBuilder
-        .function(required: <DartType>[dynamicType], result: bottomType);
-    FunctionType bottom = TypeBuilder
-        .function(required: <DartType>[objectType], result: bottomType);
-
-    _checkLattice(top, left, right, bottom);
-  }
-
   void test_genericFunction_generic_monomorphic() {
     DartType s = TypeBuilder.variable("S");
     DartType t = TypeBuilder.variable("T", bound: s);
@@ -1729,7 +1703,7 @@
     _checkIsStrictSubtypeOf(
         TypeBuilder.function(types: [s, t], required: [s], result: t),
         TypeBuilder.function(
-            types: [a, b], required: [dynamicType], result: dynamicType));
+            types: [a, b], required: [bottomType], result: dynamicType));
 
     _checkIsNotSubtypeOf(
         TypeBuilder.function(types: [u, v], required: [u], result: v),
diff --git a/pkg/analyzer/test/src/dart/analysis/search_test.dart b/pkg/analyzer/test/src/dart/analysis/search_test.dart
index c5daa6b..5761eb3 100644
--- a/pkg/analyzer/test/src/dart/analysis/search_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/search_test.dart
@@ -4,7 +4,7 @@
 
 import 'dart:async';
 
-import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/ast.dart' hide Declaration;
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
@@ -98,6 +98,71 @@
     expect(await driver.search.classMembers('test'), isEmpty);
   }
 
+  test_declarations_class() async {
+    await _resolveTestUnit('''
+class C {
+  int f;
+  C();
+  C.named();
+  int get g => 0;
+  void set s(_) {}
+  void m() {}
+}
+''');
+    var files = <String>[];
+    List<Declaration> declarations = await driver.search.declarations(files);
+    _assertHasDeclaration(declarations, 'C', DeclarationKind.CLASS, 6);
+    _assertHasDeclaration(declarations, 'f', DeclarationKind.FIELD, 16, 'C');
+    _assertHasDeclaration(
+        declarations, 'named', DeclarationKind.CONSTRUCTOR, 30, 'C');
+    _assertHasDeclaration(declarations, 'g', DeclarationKind.GETTER, 49, 'C');
+    _assertHasDeclaration(declarations, 's', DeclarationKind.SETTER, 68, 'C');
+    _assertHasDeclaration(declarations, 'm', DeclarationKind.METHOD, 83, 'C');
+  }
+
+  test_declarations_enum() async {
+    await _resolveTestUnit('''
+enum E {
+  a, b, c
+}
+''');
+    var files = <String>[];
+    List<Declaration> declarations = await driver.search.declarations(files);
+    _assertHasDeclaration(declarations, 'E', DeclarationKind.ENUM, 5);
+    _assertHasDeclaration(declarations, 'a', DeclarationKind.ENUM_CONSTANT, 11);
+    _assertHasDeclaration(declarations, 'b', DeclarationKind.ENUM_CONSTANT, 14);
+    _assertHasDeclaration(declarations, 'c', DeclarationKind.ENUM_CONSTANT, 17);
+  }
+
+  test_declarations_top() async {
+    await _resolveTestUnit('''
+int get g => 0;
+void set s(_) {}
+void f(int p) {}
+int v;
+typedef void tf1();
+typedef tf2<T> = int Function<S>(T tp, S sp);
+''');
+    var files = <String>[];
+    List<Declaration> declarations = await driver.search.declarations(files);
+    _assertHasDeclaration(declarations, 'g', DeclarationKind.GETTER, 8);
+    _assertHasDeclaration(declarations, 's', DeclarationKind.SETTER, 25);
+    _assertHasDeclaration(declarations, 'f', DeclarationKind.FUNCTION, 38);
+    _assertHasDeclaration(declarations, 'v', DeclarationKind.VARIABLE, 54);
+    _assertHasDeclaration(
+        declarations, 'tf1', DeclarationKind.FUNCTION_TYPE_ALIAS, 70);
+    _assertHasDeclaration(
+        declarations, 'tf2', DeclarationKind.FUNCTION_TYPE_ALIAS, 85);
+    // No declaration for type variables.
+    _assertNoDeclaration(declarations, 'T');
+    _assertNoDeclaration(declarations, 'S');
+    // No declarations for parameters.
+    _assertNoDeclaration(declarations, '_');
+    _assertNoDeclaration(declarations, 'p');
+    _assertNoDeclaration(declarations, 'tp');
+    _assertNoDeclaration(declarations, 'sp');
+  }
+
   test_searchMemberReferences_qualified_resolved() async {
     await _resolveTestUnit('''
 class C {
@@ -1183,6 +1248,32 @@
         unorderedEquals([a, b, c, d, e]));
   }
 
+  void _assertHasDeclaration(List<Declaration> declarations, String name,
+      DeclarationKind kind, int offset,
+      [String className]) {
+    for (var declaration in declarations) {
+      if (declaration.name == name &&
+          declaration.kind == kind &&
+          declaration.offset == offset &&
+          declaration.className == className) {
+        return;
+      }
+    }
+    var actual = declarations
+        .map((d) => '(name=${d.name}, kind=${d.kind}, offset=${d.offset})')
+        .join('\n');
+    fail(
+        'Exected to find (name=$name, kind=$kind, offset=$offset) in\n$actual');
+  }
+
+  void _assertNoDeclaration(List<Declaration> declarations, String name) {
+    for (var declaration in declarations) {
+      if (declaration.name == name) {
+        fail('Unexpected declaration $name');
+      }
+    }
+  }
+
   ExpectedResult _expectId(
       Element enclosingElement, SearchResultKind kind, String search,
       {int length, bool isResolved: true, bool isQualified: false}) {
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index 3faa53d..ba4fa96 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -459,7 +459,7 @@
   test_dependencyOnFactoryRedirectWithTypeParams() async {
     await _assertProperDependencies(r'''
 class A {
-  const factory A(var a) = B<int>;
+  const factory A(int a) = B<int>;
 }
 
 class B<T> implements A {
diff --git a/pkg/analyzer/test/src/task/strong/checker_test.dart b/pkg/analyzer/test/src/task/strong/checker_test.dart
index bb6b1d0..ec7c82a 100644
--- a/pkg/analyzer/test/src/task/strong/checker_test.dart
+++ b/pkg/analyzer/test/src/task/strong/checker_test.dart
@@ -768,7 +768,7 @@
 ''');
   }
 
-  test_fieldOverride_fuzzyArrows() async {
+  test_fieldOverride() async {
     await checkFile('''
 typedef void ToVoid<T>(T x);
 class F {
@@ -777,13 +777,13 @@
 }
 
 class G extends F {
-  final ToVoid<int> f = null;
-  /*error:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null;
+  /*error:INVALID_METHOD_OVERRIDE*/final ToVoid<int> f = null;
+  final ToVoid<dynamic> g = null;
 }
 
 class H implements F {
-  final ToVoid<int> f = null;
-  /*error:INVALID_METHOD_OVERRIDE*/final ToVoid<dynamic> g = null;
+  /*error:INVALID_METHOD_OVERRIDE*/final ToVoid<int> f = null;
+  final ToVoid<dynamic> g = null;
 }
  ''');
   }
@@ -1192,14 +1192,14 @@
   {
     BotA f;
     f = topA;
-    f = /*error:INVALID_ASSIGNMENT*/topTop;
+    f = /*error:INVALID_CAST_FUNCTION*/topTop;
     f = aa;
     f = /*error:INVALID_ASSIGNMENT*/aTop;
     f = botA;
     f = /*info:DOWN_CAST_COMPOSITE*/botTop;
     apply<BotA>(
         topA,
-        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/topTop,
+        /*error:INVALID_CAST_FUNCTION*/topTop,
         aa,
         /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/aTop,
         botA,
@@ -1217,14 +1217,14 @@
   {
     AA f;
     f = topA;
-    f = /*error:INVALID_ASSIGNMENT*/topTop;
+    f = /*error:INVALID_CAST_FUNCTION*/topTop;
     f = aa;
     f = /*error:INVALID_CAST_FUNCTION*/aTop; // known function
     f = /*info:DOWN_CAST_COMPOSITE*/botA;
     f = /*info:DOWN_CAST_COMPOSITE*/botTop;
     apply<AA>(
         topA,
-        /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/topTop,
+        /*error:INVALID_CAST_FUNCTION*/topTop,
         aa,
         /*error:INVALID_CAST_FUNCTION*/aTop, // known function
         /*info:DOWN_CAST_COMPOSITE*/botA,
@@ -1293,6 +1293,145 @@
 ''');
   }
 
+  @failingTest
+  test_fuzzyArrowLegacyAssignability_GlobalInference() async {
+    // Test for legacy fuzzy arrow support on assignability, pending
+    // cleanup.  https://github.com/dart-lang/sdk/issues/29630
+    // Tests impact of https://github.com/dart-lang/sdk/issues/32114
+    // on fuzzy arrow warnings
+    await checkFile('''
+    typedef T Fn<T>(T x);
+    typedef T FnB<T extends int>(T x);
+
+    class I2i {
+      int call(int x) => x;
+    }
+    class D2i {
+      int call(dynamic x) => x as int;
+    }
+    class I2d {
+      dynamic call(int x) => x;
+    }
+    class D2d {
+      dynamic call(dynamic x) => x;
+    }
+    Fn global0;
+    var inferred0 = global0;
+    FnB global1;
+    var inferred1 = global1;
+
+    void test0() {
+      int Function(int) i2i;
+      int Function(dynamic) d2i;
+      dynamic Function(int) i2d;
+      dynamic Function(dynamic) d2d;
+      I2i ci2i;
+      D2i cd2i;
+      I2d ci2d;
+      D2d cd2d;
+
+      { 
+        var f = inferred0;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/i2i;
+        f = d2i;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/i2d;
+        f = d2d;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/ci2i;
+        f = cd2i;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/ci2d;
+        f = cd2d;
+      }
+      {
+        var f = inferred1; 
+        f = i2i;
+        f = d2i;
+        f = /*info:DOWN_CAST_COMPOSITE*/i2d; // Real downcast
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM, info:DOWN_CAST_COMPOSITE*/d2d; // Fuzzy downcast
+        f = ci2i;
+        f = cd2i;
+        f = /*error:INVALID_ASSIGNMENT, info:DOWN_CAST_COMPOSITE*/ci2d;
+        f = /*error:INVALID_ASSIGNMENT*/cd2d;
+      }
+    }
+  ''');
+  }
+
+  test_fuzzyArrowLegacyAssignability() async {
+    // Test for legacy fuzzy arrow support on assignability, pending
+    // cleanup.  https://github.com/dart-lang/sdk/issues/29630
+    await checkFile('''
+
+    class I2i {
+      int call(int x) => x;
+    }
+    class D2i {
+      int call(dynamic x) => x as int;
+    }
+    class I2d {
+      dynamic call(int x) => x;
+    }
+    class D2d {
+      dynamic call(dynamic x) => x;
+    }
+
+    void test0() {
+      int Function(int) i2i;
+      int Function(dynamic) d2i;
+      dynamic Function(int) i2d;
+      dynamic Function(dynamic) d2d;
+      I2i ci2i;
+      D2i cd2i;
+      I2d ci2d;
+      D2d cd2d;
+
+      { 
+        int Function(int) f;
+        f = i2i;
+        f = d2i;
+        f = /*info:DOWN_CAST_COMPOSITE*/i2d;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM, info:DOWN_CAST_COMPOSITE*/d2d;
+        f = ci2i;
+        f = cd2i;
+        f = /*error:INVALID_ASSIGNMENT, info:DOWN_CAST_COMPOSITE*/ci2d;
+        f = /*error:INVALID_ASSIGNMENT*/cd2d;
+      }
+      { 
+        int Function(dynamic) f;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/i2i;
+        f = d2i;
+        f = /*info:DOWN_CAST_COMPOSITE*/i2d;
+        f = /*info:DOWN_CAST_COMPOSITE*/d2d;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/ci2i;
+        f = cd2i;
+        f = /*error:INVALID_ASSIGNMENT, info:DOWN_CAST_COMPOSITE*/ci2d;
+        f = /*error:INVALID_ASSIGNMENT, info:DOWN_CAST_COMPOSITE*/cd2d;
+      }
+      { 
+        dynamic Function(int) f;
+        f = i2i;
+        f = d2i;
+        f = i2d;
+        f = d2d;
+        f = ci2i;
+        f = cd2i;
+        f = ci2d;
+        f = cd2d;
+      }
+      { 
+        dynamic Function(dynamic) f;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/i2i;
+        f = d2i;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/i2d;
+        f = d2d;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/ci2i;
+        f = cd2i;
+        f = /*warning:USES_DYNAMIC_AS_BOTTOM*/ci2d;
+        f = cd2d;
+      }
+    }
+  ''');
+  }
+
   test_functionTypingAndSubtyping_dynamicFunctions_closuresAreNotFuzzy() async {
     // Regression test for definite function cases
     // https://github.com/dart-lang/sdk/issues/26118
@@ -2144,7 +2283,7 @@
 ''');
   }
 
-  test_getterOverride_fuzzyArrows() async {
+  test_getterOverride() async {
     await checkFile('''
 typedef void ToVoid<T>(T x);
 
@@ -2154,13 +2293,13 @@
 }
 
 class G extends F {
-  ToVoid<int> get f => null;
-  /*error:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null;
+  /*error:INVALID_METHOD_OVERRIDE*/ToVoid<int> get f => null;
+  ToVoid<dynamic> get g => null;
 }
 
 class H implements F {
-  ToVoid<int> get f => null;
-  /*error:INVALID_METHOD_OVERRIDE*/ToVoid<dynamic> get g => null;
+  /*error:INVALID_METHOD_OVERRIDE*/ToVoid<int> get f => null;
+  ToVoid<dynamic> get g => null;
 }
 ''');
   }
@@ -3004,7 +3143,7 @@
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is I2I;
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is D2I;
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is I2D;
-  b = foo is D2D;
+  b = /*info:NON_GROUND_TYPE_CHECK_INFO*/foo is D2D;
 
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is II2I;
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2I;
@@ -3013,7 +3152,7 @@
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DD2I;
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DI2D;
   b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is ID2D;
-  b = bar is DD2D;
+  b = /*info:NON_GROUND_TYPE_CHECK_INFO*/bar is DD2D;
 
   // For as, the validity of checks is deferred to runtime.
   Function f;
@@ -3092,7 +3231,7 @@
   TakesA<dynamic> g;
   TakesA<String> h;
   g = /*warning:USES_DYNAMIC_AS_BOTTOM*/h;
-  f = /*info:DOWN_CAST_COMPOSITE*/f ?? g;
+  f = f ?? g;
 }
 ''');
   }
@@ -3133,7 +3272,7 @@
 ''');
   }
 
-  test_methodOverride_fuzzyArrows() async {
+  test_methodOverride_contravariant() async {
     await checkFile('''
 abstract class A {
   bool operator ==(Object object);
@@ -3715,7 +3854,7 @@
 ''');
   }
 
-  test_setterOverride_fuzzyArrows() async {
+  test_setterOverride() async {
     await checkFile('''
 typedef void ToVoid<T>(T x);
 class F {
@@ -3726,15 +3865,15 @@
 }
 
 class G extends F {
-  /*error:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {}
-  void set g(ToVoid<dynamic> x) {}
+  void set f(ToVoid<int> x) {}
+  /*error:INVALID_METHOD_OVERRIDE*/void set g(ToVoid<dynamic> x) {}
   /*error:INVALID_METHOD_OVERRIDE*/void set h(int x) {}
   void set i(dynamic x) {}
 }
 
 class H implements F {
-  /*error:INVALID_METHOD_OVERRIDE*/void set f(ToVoid<int> x) {}
-  void set g(ToVoid<dynamic> x) {}
+  void set f(ToVoid<int> x) {}
+  /*error:INVALID_METHOD_OVERRIDE*/void set g(ToVoid<dynamic> x) {}
   /*error:INVALID_METHOD_OVERRIDE*/void set h(int x) {}
   void set i(dynamic x) {}
 }
@@ -4059,8 +4198,7 @@
     : _comparator = /*info:DOWN_CAST_COMPOSITE*/(compare == null) ? Comparable.compare : compare,
       _validKey = (isValidKey != null) ? isValidKey : (/*info:INFERRED_TYPE_CLOSURE*/(v) => true) {
 
-    // NOTE: this is a down cast because isValidKey has fuzzy arrow type.
-    _Predicate<Object> v = /*info:DOWN_CAST_COMPOSITE*/(isValidKey != null)
+    _Predicate<Object> v = (isValidKey != null)
         ? isValidKey : (/*info:INFERRED_TYPE_CLOSURE*/(_) => true);
 
     v = (isValidKey != null)
@@ -4464,4 +4602,9 @@
   test_covariantOverride_fields() async {
     await super.test_covariantOverride_fields();
   }
+
+  @override
+  test_fuzzyArrowLegacyAssignability_GlobalInference() async {
+    await super.test_fuzzyArrowLegacyAssignability_GlobalInference();
+  }
 }
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
index 2bca691..fe90449 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_core.dart
@@ -32,6 +32,12 @@
       <String, LinkedEditGroup>{};
 
   /**
+   * The range of the selection for the change being built, or `null` if there
+   * is no selection.
+   */
+  SourceRange _selectionRange;
+
+  /**
    * The set of [Position]s that belong to the current [EditBuilderImpl] and
    * should not be updated in result of inserting this builder.
    */
@@ -43,6 +49,9 @@
   ChangeBuilderImpl();
 
   @override
+  SourceRange get selectionRange => _selectionRange;
+
+  @override
   SourceChange get sourceChange {
     _linkedEditGroups.forEach((String name, LinkedEditGroup group) {
       _change.addLinkedEditGroup(group);
@@ -86,6 +95,10 @@
     _change.selection = position;
   }
 
+  void _setSelectionRange(SourceRange range) {
+    _selectionRange = range;
+  }
+
   /**
    * Update the offsets of any positions that occur at or after the given
    * [offset] such that the positions are offset by the given [delta]. Positions
@@ -131,10 +144,10 @@
   final int length;
 
   /**
-   * The offset of the selection for the change being built, or `-1` if the
+   * The range of the selection for the change being built, or `null` if the
    * selection is not inside the change being built.
    */
-  int _selectionOffset = -1;
+  SourceRange _selectionRange;
 
   /**
    * The end-of-line marker used in the file being edited, or `null` if the
@@ -203,8 +216,16 @@
   }
 
   @override
+  void selectAll(void writer()) {
+    int rangeOffset = _buffer.length;
+    writer();
+    int rangeLength = _buffer.length - rangeOffset;
+    _selectionRange = new SourceRange(offset + rangeOffset, rangeLength);
+  }
+
+  @override
   void selectHere() {
-    _selectionOffset = offset + _buffer.length;
+    _selectionRange = new SourceRange(offset + _buffer.length, 0);
   }
 
   @override
@@ -331,11 +352,12 @@
    * Capture the selection offset if one was set.
    */
   void _captureSelection(EditBuilderImpl builder, SourceEdit edit) {
-    int offset = builder._selectionOffset;
-    if (offset >= 0) {
+    SourceRange range = builder._selectionRange;
+    if (range != null) {
       Position position =
-          new Position(fileEdit.file, offset + _deltaToEdit(edit));
+          new Position(fileEdit.file, range.offset + _deltaToEdit(edit));
       changeBuilder.setSelection(position);
+      changeBuilder._setSelectionRange(range);
     }
   }
 
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index bddf997..bd58ad2 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -317,23 +317,28 @@
   @override
   void writeOverrideOfInheritedMember(ExecutableElement member,
       {StringBuffer displayTextBuffer, String returnTypeGroupName}) {
-    // prepare environment
     String prefix = getIndent(1);
-    // may be property
     String prefix2 = getIndent(2);
     ElementKind elementKind = member.kind;
+    // TODO(brianwilkerson) Look for a non-abstract inherited member farther up
+    // in the superclass chain that we could invoke.
+    bool isAbstract = member.isAbstract;
     bool isGetter = elementKind == ElementKind.GETTER;
     bool isSetter = elementKind == ElementKind.SETTER;
     bool isMethod = elementKind == ElementKind.METHOD;
     bool isOperator = isMethod && (member as MethodElement).isOperator;
+    String memberName = member.displayName;
     write(prefix);
+
+    // @override
+    writeln('@override');
+    write(prefix);
+
     if (isGetter) {
       writeln('// TODO: implement ${member.displayName}');
       write(prefix);
     }
-    // @override
-    writeln('@override');
-    write(prefix);
+
     // return type
     DartType returnType = member.type.returnType;
     bool typeWritten = writeType(returnType,
@@ -351,13 +356,27 @@
       write(Keyword.OPERATOR.lexeme);
       write(' ');
     }
+
     // name
-    write(member.displayName, displayTextBuffer: displayTextBuffer);
+    write(memberName, displayTextBuffer: displayTextBuffer);
 
     // parameters + body
     if (isGetter) {
-      writeln(' => null;');
-      displayTextBuffer?.write(' => ...');
+      if (isAbstract) {
+        write(' => ');
+        selectAll(() {
+          write('null');
+        });
+        writeln(';');
+      } else {
+        write(' => ');
+        selectAll(() {
+          write('super.');
+          write(memberName);
+        });
+        writeln(';');
+      }
+      displayTextBuffer?.write(' => …');
     } else {
       writeTypeParameters(member.typeParameters,
           methodBeingCopied: member, displayTextBuffer: displayTextBuffer);
@@ -365,18 +384,66 @@
       writeParameters(parameters,
           methodBeingCopied: member, displayTextBuffer: displayTextBuffer);
       writeln(' {');
-      displayTextBuffer?.write(' {');
+
       // TO-DO
       write(prefix2);
-      writeln('// TODO: implement ${member.displayName}');
-      if (typeWritten && !returnType.isVoid) {
+      writeln('// TODO: implement $memberName');
+
+      if (returnType.isVoid) {
+        if (!isAbstract) {
+          write(prefix2);
+          selectAll(() {
+            write('super.');
+            write(memberName);
+            write('(');
+            for (int i = 0; i < parameters.length; i++) {
+              if (i > 0) {
+                write(', ');
+              }
+              write(parameters[i].name);
+            }
+            write(');');
+          });
+          writeln();
+        }
+      } else if (isSetter) {
+        if (!isAbstract) {
+          write(prefix2);
+          selectAll(() {
+            write('super.');
+            write(memberName);
+            write(' = ');
+            write(parameters[0].name);
+            write(';');
+          });
+          writeln();
+        }
+      } else {
         write(prefix2);
-        writeln('return null;');
+        if (isAbstract) {
+          selectAll(() {
+            write('return null;');
+          });
+        } else {
+          selectAll(() {
+            write('return super.');
+            write(memberName);
+            write('(');
+            for (int i = 0; i < parameters.length; i++) {
+              if (i > 0) {
+                write(', ');
+              }
+              write(parameters[i].name);
+            }
+            write(');');
+          });
+        }
+        writeln();
       }
       // close method
       write(prefix);
       writeln('}');
-      displayTextBuffer?.write(' ... }');
+      displayTextBuffer?.write(' { … }');
     }
   }
 
diff --git a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
index 39e5b4f..dd37c1b 100644
--- a/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
+++ b/pkg/analyzer_plugin/lib/utilities/change_builder/change_builder_core.dart
@@ -20,6 +20,12 @@
   factory ChangeBuilder() = ChangeBuilderImpl;
 
   /**
+   * Return the range of the selection for the change being built, or `null` if
+   * there is no selection.
+   */
+  SourceRange get selectionRange;
+
+  /**
    * Return the source change that was built. The source change will not be
    * complete until all of the futures returned by [addFileEdit] have completed.
    */
@@ -66,7 +72,12 @@
       {LinkedEditSuggestionKind kind, List<String> suggestions});
 
   /**
-   * Set the selection to the given location within the edit being built.
+   * Set the selection to cover all of the code written by the given [writer].
+   */
+  void selectAll(void writer());
+
+  /**
+   * Set the selection to the current location within the edit being built.
    */
   void selectHere();
 
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index e255aa1..8bc6e51 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -887,7 +887,51 @@
     expect(group.positions, hasLength(1));
   }
 
-  test_writeOverrideOfInheritedMember_method() async {
+  test_writeOverrideOfInheritedMember_getter_abstract() async {
+    await _assertWriteOverrideOfInheritedAccessor('''
+abstract class A {
+  int get zero;
+}
+class B extends A {
+}
+''', '''
+  @override
+  // TODO: implement zero
+  int get zero => null;
+''', displayText: 'zero => …', selection: new SourceRange(113, 4));
+  }
+
+  test_writeOverrideOfInheritedMember_getter_concrete() async {
+    await _assertWriteOverrideOfInheritedAccessor('''
+class A {
+  int get zero => 0;
+}
+class B extends A {
+}
+''', '''
+  @override
+  // TODO: implement zero
+  int get zero => super.zero;
+''', displayText: 'zero => …', selection: new SourceRange(109, 10));
+  }
+
+  test_writeOverrideOfInheritedMember_method_abstract() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+abstract class A {
+  A add(A a);
+}
+class B extends A {
+}
+''', '''
+  @override
+  A add(A a) {
+    // TODO: implement add
+    return null;
+  }
+''', displayText: 'add(A a) { … }', selection: new SourceRange(113, 12));
+  }
+
+  test_writeOverrideOfInheritedMember_method_concrete() async {
     await _assertWriteOverrideOfInheritedMethod('''
 class A {
   A add(A a) => null;
@@ -895,15 +939,15 @@
 class B extends A {
 }
 ''', '''
-@override
-A add(A a) {
-  // TODO: implement add
-  return null;
-}
-''', displayText: 'add(A a) { ... }');
+  @override
+  A add(A a) {
+    // TODO: implement add
+    return super.add(a);
+  }
+''', displayText: 'add(A a) { … }', selection: new SourceRange(112, 20));
   }
 
-  test_writeOverrideOfInheritedMember_method_functionTypeAlias() async {
+  test_writeOverrideOfInheritedMember_method_functionTypeAlias_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 typedef int F(int left, int right);
 abstract class A {
@@ -912,30 +956,67 @@
 class B extends A {
 }
 ''', '''
-@override
-void perform(F f) {
-  // TODO: implement perform
-}
-''', displayText: 'perform(F f) { ... }');
+  @override
+  void perform(F f) {
+    // TODO: implement perform
+  }
+''', displayText: 'perform(F f) { … }', selection: null);
   }
 
-  test_writeOverrideOfInheritedMember_method_functionTypedParameter() async {
+  test_writeOverrideOfInheritedMember_method_functionTypeAlias_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+typedef int F(int left, int right);
+class A {
+  void perform(F f) {}
+}
+class B extends A {
+}
+''', '''
+  @override
+  void perform(F f) {
+    // TODO: implement perform
+    super.perform(f);
+  }
+''', displayText: 'perform(F f) { … }', selection: new SourceRange(160, 17));
+  }
+
+  test_writeOverrideOfInheritedMember_method_functionTypedParameter_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A {
   forEach(int f(double p1, String p2));
 }
-
 class B extends A {
 }
 ''', '''
-@override
-forEach(int Function(double p1, String p2) f) {
-  // TODO: implement forEach
-}
-''', displayText: 'forEach(int Function(double p1, String p2) f) { ... }');
+  @override
+  forEach(int Function(double p1, String p2) f) {
+    // TODO: implement forEach
+    return null;
+  }
+''',
+        displayText: 'forEach(int Function(double p1, String p2) f) { … }',
+        selection: new SourceRange(178, 12));
   }
 
-  test_writeOverrideOfInheritedMember_method_generic_noBounds() async {
+  test_writeOverrideOfInheritedMember_method_functionTypedParameter_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A {
+  forEach(int f(double p1, String p2)) {}
+}
+class B extends A {
+}
+''', '''
+  @override
+  forEach(int Function(double p1, String p2) f) {
+    // TODO: implement forEach
+    return super.forEach(f);
+  }
+''',
+        displayText: 'forEach(int Function(double p1, String p2) f) { … }',
+        selection: new SourceRange(171, 24));
+  }
+
+  test_writeOverrideOfInheritedMember_method_generic_noBounds_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A {
   List<T> get<T>(T key);
@@ -943,15 +1024,31 @@
 class B implements A {
 }
 ''', '''
-@override
-List<T> get<T>(T key) {
-  // TODO: implement get
-  return null;
-}
-''', displayText: 'get<T>(T key) { ... }');
+  @override
+  List<T> get<T>(T key) {
+    // TODO: implement get
+    return null;
+  }
+''', displayText: 'get<T>(T key) { … }', selection: new SourceRange(138, 12));
   }
 
-  test_writeOverrideOfInheritedMember_method_generic_withBounds() async {
+  test_writeOverrideOfInheritedMember_method_generic_noBounds_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A {
+  List<T> get<T>(T key) {}
+}
+class B implements A {
+}
+''', '''
+  @override
+  List<T> get<T>(T key) {
+    // TODO: implement get
+    return super.get(key);
+  }
+''', displayText: 'get<T>(T key) { … }', selection: new SourceRange(131, 22));
+  }
+
+  test_writeOverrideOfInheritedMember_method_generic_withBounds_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A<K1, V1> {
   List<T> get<T extends V1>(K1 key);
@@ -959,63 +1056,167 @@
 class B<K2, V2> implements A<K2, V2> {
 }
 ''', '''
-@override
-List<T> get<T extends V2>(K2 key) {
-  // TODO: implement get
-  return null;
-}
-''', displayText: 'get<T extends V2>(K2 key) { ... }');
+  @override
+  List<T> get<T extends V2>(K2 key) {
+    // TODO: implement get
+    return null;
+  }
+''',
+        displayText: 'get<T extends V2>(K2 key) { … }',
+        selection: new SourceRange(186, 12));
   }
 
-  test_writeOverrideOfInheritedMember_method_genericFunctionTypedParameter() async {
+  test_writeOverrideOfInheritedMember_method_generic_withBounds_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A<K1, V1> {
+  List<T> get<T extends V1>(K1 key) {
+    return null;
+  }
+}
+class B<K2, V2> implements A<K2, V2> {
+}
+''', '''
+  @override
+  List<T> get<T extends V2>(K2 key) {
+    // TODO: implement get
+    return super.get(key);
+  }
+''',
+        displayText: 'get<T extends V2>(K2 key) { … }',
+        selection: new SourceRange(199, 22));
+  }
+
+  test_writeOverrideOfInheritedMember_method_genericFunctionTypedParameter_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A {
   int foo(T Function<T>() fn);
 }
-
 class B extends A {
 }
 ''', '''
-@override
-int foo(T Function<T>() fn) {
-  // TODO: implement foo
-  return null;
-}
-''', displayText: 'foo(T Function<T>() fn) { ... }');
+  @override
+  int foo(T Function<T>() fn) {
+    // TODO: implement foo
+    return null;
+ }
+''',
+        displayText: 'foo(T Function<T>() fn) { … }',
+        selection: new SourceRange(147, 12));
   }
 
-  test_writeOverrideOfInheritedMember_method_nullAsTypeArgument() async {
+  test_writeOverrideOfInheritedMember_method_genericFunctionTypedParameter_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A {
+  int foo(T Function<T>() fn) => 0;
+}
+class B extends A {
+}
+''', '''
+  @override
+  int foo(T Function<T>() fn) {
+    // TODO: implement foo
+    return super.foo(fn);
+ }
+''',
+        displayText: 'foo(T Function<T>() fn) { … }',
+        selection: new SourceRange(143, 21));
+  }
+
+  test_writeOverrideOfInheritedMember_method_nullAsTypeArgument_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A {
   List<Null> foo();
 }
-
 class B extends A {
 }
 ''', '''
-@override
-List<Null> foo() {
-  // TODO: implement foo
-  return null;
-}
-''', displayText: 'foo() { ... }');
+  @override
+  List<Null> foo() {
+    // TODO: implement foo
+    return null;
+ }
+''', displayText: 'foo() { … }', selection: new SourceRange(125, 12));
   }
 
-  test_writeOverrideOfInheritedMember_method_voidAsTypeArgument() async {
+  test_writeOverrideOfInheritedMember_method_nullAsTypeArgument_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A {
+  List<Null> foo() => null
+}
+class B extends A {
+}
+''', '''
+  @override
+  List<Null> foo() {
+    // TODO: implement foo
+    return super.foo();
+ }
+''', displayText: 'foo() { … }', selection: new SourceRange(123, 19));
+  }
+
+  test_writeOverrideOfInheritedMember_method_voidAsTypeArgument_abstract() async {
     await _assertWriteOverrideOfInheritedMethod('''
 abstract class A {
   List<void> foo();
 }
-
 class B extends A {
 }
 ''', '''
-@override
-List<void> foo() {
-  // TODO: implement foo
-  return null;
+  @override
+  List<void> foo() {
+    // TODO: implement foo
+    return null;
+  }
+''', displayText: 'foo() { … }', selection: new SourceRange(125, 12));
+  }
+
+  test_writeOverrideOfInheritedMember_method_voidAsTypeArgument_concrete() async {
+    await _assertWriteOverrideOfInheritedMethod('''
+class A {
+  List<void> foo() => null;
 }
-''', displayText: 'foo() { ... }');
+class B extends A {
+}
+''', '''
+  @override
+  List<void> foo() {
+    // TODO: implement foo
+    return super.foo();
+  }
+''', displayText: 'foo() { … }', selection: new SourceRange(124, 19));
+  }
+
+  test_writeOverrideOfInheritedMember_setter_abstract() async {
+    await _assertWriteOverrideOfInheritedAccessor('''
+abstract class A {
+  set value(int value);
+}
+class B extends A {
+}
+''', '''
+  @override
+  set value(int value) {
+    // TODO: implement value
+  }
+''', displayText: 'value(int value) { … }', selection: null);
+  }
+
+  test_writeOverrideOfInheritedMember_setter_concrete() async {
+    await _assertWriteOverrideOfInheritedAccessor('''
+class A {
+  set value(int value) {}
+}
+class B extends A {
+}
+''', '''
+  @override
+  set value(int value) {
+    // TODO: implement value
+    super.value = value;
+  }
+''',
+        displayText: 'value(int value) { … }',
+        selection: new SourceRange(128, 20));
   }
 
   test_writeParameterMatchingArgument() async {
@@ -1436,12 +1637,48 @@
 
   /**
    * Assuming that the [content] being edited defines a class named 'A' whose
+   * first accessor is the member to be overridden and ends with a class to
+   * which an inherited method is to be added, assert that the text of the
+   * overridden member matches the [expected] text (modulo white space). Assert
+   * that the generated display text matches the given [displayText]. If a
+   * [selection] is provided, assert that the generated selection range matches
+   * it.
+   */
+  _assertWriteOverrideOfInheritedAccessor(String content, String expected,
+      {String displayText, SourceRange selection}) async {
+    String path = provider.convertPath('/test.dart');
+    addSource(path, content);
+    ClassElement classA = await _getClassElement(path, 'A');
+
+    StringBuffer displayBuffer =
+        displayText != null ? new StringBuffer() : null;
+
+    DartChangeBuilderImpl builder = new DartChangeBuilder(session);
+    await builder.addFileEdit(path, (FileEditBuilder builder) {
+      builder.addInsertion(content.length - 2, (EditBuilder builder) {
+        (builder as DartEditBuilder).writeOverrideOfInheritedMember(
+            classA.accessors[0],
+            displayTextBuffer: displayBuffer);
+      });
+    });
+    SourceEdit edit = getEdit(builder);
+    expect(edit.replacement, equalsIgnoringWhitespace(expected));
+    expect(displayBuffer?.toString(), displayText);
+    if (selection != null) {
+      expect(builder.selectionRange, selection);
+    }
+  }
+
+  /**
+   * Assuming that the [content] being edited defines a class named 'A' whose
    * first method is the member to be overridden and ends with a class to which
    * an inherited method is to be added, assert that the text of the overridden
-   * member matches the [expected] text (modulo white space).
+   * member matches the [expected] text (modulo white space). Assert that the
+   * generated display text matches the given [displayText]. If a [selection] is
+   * provided, assert that the generated selection range matches it.
    */
   _assertWriteOverrideOfInheritedMethod(String content, String expected,
-      {String displayText}) async {
+      {String displayText, SourceRange selection}) async {
     String path = provider.convertPath('/test.dart');
     addSource(path, content);
     ClassElement classA = await _getClassElement(path, 'A');
@@ -1460,6 +1697,9 @@
     SourceEdit edit = getEdit(builder);
     expect(edit.replacement, equalsIgnoringWhitespace(expected));
     expect(displayBuffer?.toString(), displayText);
+    if (selection != null) {
+      expect(builder.selectionRange, selection);
+    }
   }
 
   Future<ClassElement> _getClassElement(String path, String name) async {
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart
index e16a180..4e48c16 100644
--- a/pkg/compiler/lib/src/io/code_output.dart
+++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -45,7 +45,8 @@
   }
 
   @override
-  void forEachSourceLocation(void f(int targetOffset, var sourceLocation)) {
+  void forEachSourceLocation(
+      void f(int targetOffset, SourceLocation sourceLocation)) {
     markers.forEach((int targetOffset, List<SourceLocation> sourceLocations) {
       for (SourceLocation sourceLocation in sourceLocations) {
         f(targetOffset, sourceLocation);
diff --git a/pkg/compiler/lib/src/kernel/element_map_mixins.dart b/pkg/compiler/lib/src/kernel/element_map_mixins.dart
index 2041dd4..59ba7e9 100644
--- a/pkg/compiler/lib/src/kernel/element_map_mixins.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_mixins.dart
@@ -126,8 +126,8 @@
     bool mayLookupInMain() {
       var mainUri = elementEnvironment.mainLibrary.canonicalUri;
       // Tests permit lookup outside of dart: libraries.
-      return mainUri.path.contains('sdk/tests/compiler/dart2js_native') ||
-          mainUri.path.contains('sdk/tests/compiler/dart2js_extra');
+      return mainUri.path.contains('tests/compiler/dart2js_native') ||
+          mainUri.path.contains('tests/compiler/dart2js_extra');
     }
 
     DartType lookup(String typeName, {bool required}) {
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index e4f8248..742cadb 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -32,6 +32,7 @@
 import 'package:analyzer/src/summary/summary_sdk.dart';
 import 'package:analyzer/src/task/strong/ast_properties.dart';
 import 'package:path/path.dart' as path;
+import 'package:source_span/source_span.dart' show SourceLocation;
 
 import '../closure/closure_annotator.dart' show ClosureAnnotator;
 import '../compiler/js_metalet.dart' as JS;
@@ -41,12 +42,13 @@
 import '../compiler/type_utilities.dart';
 import '../js_ast/js_ast.dart' as JS;
 import '../js_ast/js_ast.dart' show js;
+import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan;
 import 'ast_builder.dart';
 import 'module_compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile;
 import 'element_helpers.dart';
 import 'extension_types.dart' show ExtensionTypeSet;
 import 'js_interop.dart';
-import 'js_typeref_codegen.dart' show JsTypeRefCodegen;
+import 'js_typeref_codegen.dart' show JSTypeRefCodegen;
 import 'js_typerep.dart' show JSTypeRep, JSType;
 import 'nullable_type_inference.dart' show NullableTypeInference;
 import 'property_model.dart';
@@ -69,7 +71,7 @@
 // expressions (which result in JS.Expression) and statements
 // (which result in (JS.Statement).
 class CodeGenerator extends Object
-    with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference
+    with ClosureAnnotator, JSTypeRefCodegen, NullableTypeInference
     implements AstVisitor<JS.Node> {
   final AnalysisContext context;
   final SummaryDataStore summaryData;
@@ -166,20 +168,23 @@
 
   Map<TypeDefiningElement, AstNode> _declarationNodes;
 
-  /// The stack of currently emitting elements, if generating top-level code
-  /// for them. This is not used when inside method bodies, because order does
-  /// not matter for those.
-  final _topLevelElements = <TypeDefiningElement>[];
+  /// The class that's currently emitting top-level (module-level) JS code.
+  ///
+  /// This is primarily used to forward declare classes so they are available
+  /// to JS class `extends`.
+  ///
+  /// This is not set when inside method bodies, because they are run after we
+  /// load modules, so they can freely access all classes.
+  TypeDefiningElement _topLevelClass;
 
   /// The current element being loaded.
   /// We can use this to determine if we're loading top-level code or not:
   ///
-  ///     _currentElements.last == _topLevelElements.last
-  //
-  // TODO(jmesserly): ideally we'd only track types here, in other words,
-  // TypeDefiningElement. However we still rely on this for [currentLibrary] so
-  // we need something to be pushed always.
-  final _currentElements = <Element>[];
+  ///     _currentElement == _topLevelClass
+  ///
+  /// This is also used to find the current compilation unit for emitting source
+  /// mappings.
+  Element _currentElement;
 
   final _deferredProperties = new HashMap<PropertyAccessorElement, JS.Method>();
 
@@ -246,9 +251,13 @@
     _typeRep = new JSTypeRep(rules, types);
   }
 
-  Element get currentElement => _currentElements.last;
+  LibraryElement get currentLibrary => _currentElement.library;
 
-  LibraryElement get currentLibrary => currentElement.library;
+  CompilationUnitElement get _currentCompilationUnit {
+    var e = _currentElement;
+    while (e is! CompilationUnitElement) e = e.enclosingElement;
+    return e;
+  }
 
   /// The main entry point to JavaScript code generation.
   ///
@@ -568,7 +577,8 @@
     var node = _declarationNodes.remove(e);
     if (node == null) return; // not from this module or already loaded.
 
-    _currentElements.add(e);
+    var savedElement = _currentElement;
+    _currentElement = e;
 
     // TODO(jmesserly): this is not really the right place for this.
     // Ideally we do this per function body.
@@ -579,23 +589,7 @@
 
     _moduleItems.add(node.accept<JS.Node>(this));
 
-    var last = _currentElements.removeLast();
-    assert(identical(e, last));
-  }
-
-  /// Start generating top-level code for the element [e].
-  ///
-  /// Subsequent [emitDeclaration] calls will cause those elements to be
-  /// generated before this one, until [finishTopLevel] is called.
-  void _startTopLevelCodeForClass(TypeDefiningElement e) {
-    assert(identical(e, currentElement));
-    _topLevelElements.add(e);
-  }
-
-  /// Finishes the top-level code for the element [e].
-  void _finishTopLevelCodeForClass(TypeDefiningElement e) {
-    var last = _topLevelElements.removeLast();
-    assert(identical(e, last));
+    _currentElement = savedElement;
   }
 
   /// To emit top-level module items, we sometimes need to reorder them.
@@ -611,8 +605,7 @@
   void _declareBeforeUse(TypeDefiningElement e) {
     if (e == null) return;
 
-    var topLevel = _topLevelElements;
-    if (topLevel.isNotEmpty && identical(currentElement, topLevel.last)) {
+    if (_topLevelClass != null && identical(_currentElement, _topLevelClass)) {
       // If the item is from our library, try to emit it now.
       _emitTypeDeclaration(e);
     }
@@ -625,7 +618,8 @@
     // this is only to catch things that haven't been emitted yet.
     //
     // See _emitTypeDeclaration.
-    _currentElements.add(unit.element);
+    var savedElement = _currentElement;
+    _currentElement = unit.element;
     var isInternalSdk = isSdkInternalRuntime(currentLibrary);
     List<VariableDeclaration> fields;
     for (var declaration in unit.declarations) {
@@ -663,15 +657,16 @@
 
     if (fields != null) _emitTopLevelFields(fields);
 
-    _currentElements.removeLast();
+    _currentElement = savedElement;
   }
 
   void _emitExportDirectives(CompilationUnit unit) {
+    var savedElement = _currentElement;
     for (var directive in unit.directives) {
-      _currentElements.add(directive.element);
+      _currentElement = directive.element;
       directive.accept(this);
-      _currentElements.removeLast();
     }
+    _currentElement = savedElement;
   }
 
   @override
@@ -782,9 +777,7 @@
       result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]);
     } else {
       // Always go through a runtime helper, because implicit interfaces.
-
       var castType = _emitType(type);
-
       result = js.call('#.is(#)', [castType, lhs]);
     }
 
@@ -913,7 +906,8 @@
       }
     }
 
-    var jsCtors = _defineConstructors(classElem, className, memberMap);
+    var jsCtors =
+        _defineConstructors(classElem, className, memberMap, classNode);
     var jsMethods = _emitClassMethods(classElem, members);
     _emitSuperclassCovarianceChecks(classNode, jsMethods);
 
@@ -1343,7 +1337,8 @@
       }
     }
 
-    _startTopLevelCodeForClass(classElem);
+    var savedTopLevel = _topLevelClass;
+    _topLevelClass = classElem;
 
     // Unroll mixins.
     var mixinLength = classElem.mixins.length;
@@ -1373,7 +1368,7 @@
       mixinBody.add(
           _callHelperStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
 
-      _finishTopLevelCodeForClass(classElem);
+      _topLevelClass = savedTopLevel;
 
       if (methods.isNotEmpty) {
         // However we may need to add some methods to this class that call
@@ -1424,7 +1419,7 @@
       baseClass = mixinId;
     }
 
-    _finishTopLevelCodeForClass(classElem);
+    _topLevelClass = savedTopLevel;
 
     body.add(_emitClassStatement(classElem, className, baseClass, methods));
 
@@ -1580,7 +1575,6 @@
           param = covariantParams.lookup(param) as ParameterElement;
 
           if (param == null) continue;
-          JS.Parameter jsParam;
           if (param.kind == ParameterKind.NAMED) {
             foundNamedParams = true;
 
@@ -1593,7 +1587,7 @@
               name
             ]));
           } else {
-            jsParam = _emitParameter(param);
+            var jsParam = _emitParameter(param);
             jsParams.add(jsParam);
 
             if (param.kind == ParameterKind.POSITIONAL) {
@@ -1643,15 +1637,14 @@
       // Pass along all arguments verbatim, and let the callee handle them.
       // TODO(jmesserly): we'll need something different once we have
       // rest/spread support, but this should work for now.
-      var params =
-          _emitFormalParameterList(node.parameters, destructure: false);
+      var params = _emitFormalParameters(node.parameters?.parameters);
 
       fun = new JS.Fun(
           params,
           new JS.Block([
             js.statement('return $newKeyword #(#)',
                 [visitConstructorName(redirect), params])
-              ..sourceInformation = redirect
+              ..sourceInformation = _nodeStart(redirect)
           ]),
           returnType: returnType);
     } else {
@@ -1661,14 +1654,17 @@
       if (init != null) body.add(init);
       body.add(_visitStatement(node.body));
 
-      var params = _emitFormalParameterList(node.parameters);
+      var params = _emitFormalParameters(node.parameters?.parameters);
       fun = new JS.Fun(params, new JS.Block(body), returnType: returnType);
     }
 
     _currentFunction = savedFunction;
 
     return closureAnnotate(
-        new JS.Method(name, fun, isStatic: true), element, node);
+        new JS.Method(name, fun, isStatic: true)
+          ..sourceInformation = _functionEnd(node),
+        element,
+        node);
   }
 
   /// Given a class C that implements method M from interface I, but does not
@@ -1768,7 +1764,8 @@
     var mocks = _classProperties.mockMembers;
     if (!mocks.containsKey(element.name)) {
       var getter = js.call('function() { return this[#]; }', [virtualField]);
-      result.add(new JS.Method(name, getter, isGetter: true));
+      result.add(new JS.Method(name, getter, isGetter: true)
+        ..sourceInformation = _functionSpan(field.name));
     }
 
     if (!mocks.containsKey(element.name + '=')) {
@@ -1788,7 +1785,8 @@
         jsCode = 'function(value) { #[#] = value; }';
       }
 
-      result.add(new JS.Method(name, js.call(jsCode, args), isSetter: true));
+      result.add(new JS.Method(name, js.call(jsCode, args), isSetter: true)
+        ..sourceInformation = _functionSpan(field.name));
     }
 
     return result;
@@ -1900,8 +1898,11 @@
   }
 
   /// Defines all constructors for this class as ES5 constructors.
-  List<JS.Statement> _defineConstructors(ClassElement classElem,
-      JS.Expression className, Map<Element, Declaration> memberMap) {
+  List<JS.Statement> _defineConstructors(
+      ClassElement classElem,
+      JS.Expression className,
+      Map<Element, Declaration> memberMap,
+      Declaration classNode) {
     var isCallable = isCallableClass(classElem);
 
     var body = <JS.Statement>[];
@@ -1942,8 +1943,10 @@
       var ctorBody = <JS.Statement>[_initializeFields(fields)];
       if (superCall != null) ctorBody.add(superCall);
 
-      addConstructor('',
-          _finishConstructorFunction([], new JS.Block(ctorBody), isCallable));
+      addConstructor(
+          '',
+          _finishConstructorFunction([], new JS.Block(ctorBody), isCallable)
+            ..sourceInformation = _functionEnd(classNode));
       return body;
     }
 
@@ -2279,7 +2282,7 @@
       List<VariableDeclaration> fields,
       bool isCallable,
       JS.Expression className) {
-    var params = _emitFormalParameterList(node.parameters);
+    var params = _emitFormalParameters(node.parameters?.parameters);
 
     var savedFunction = _currentFunction;
     _currentFunction = node.body;
@@ -2290,10 +2293,11 @@
     _superAllowed = savedSuperAllowed;
     _currentFunction = savedFunction;
 
-    return _finishConstructorFunction(params, body, isCallable);
+    return _finishConstructorFunction(params, body, isCallable)
+      ..sourceInformation = _functionEnd(node);
   }
 
-  JS.Expression _finishConstructorFunction(
+  JS.Fun _finishConstructorFunction(
       List<JS.Parameter> params, JS.Block body, isCallable) {
     // We consider a class callable if it inherits from anything with a `call`
     // method. As a result, we can know the callable JS function was created
@@ -2381,10 +2385,13 @@
         superCall != null ? _emitArgumentList(superCall.argumentList) : null;
     var jsSuper = _emitSuperConstructorCallIfNeeded(
         cls.element, className, superCall?.staticElement, superCallArgs);
-    if (jsSuper != null) body.add(jsSuper..sourceInformation = superCall);
+    if (jsSuper != null) {
+      if (superCall != null) jsSuper.sourceInformation = _nodeStart(superCall);
+      body.add(jsSuper);
+    }
 
     body.add(_emitFunctionScopedBody(node.body, node.element));
-    return new JS.Block(body)..sourceInformation = node;
+    return new JS.Block(body);
   }
 
   JS.Statement _emitRedirectingConstructor(
@@ -2468,8 +2475,8 @@
           _classProperties.virtualFields[f] ?? _declareMemberName(f.getter);
       var jsInit = _visitInitializer(initializer, f);
       body.add(jsInit
-          .toAssignExpression(
-              js.call('this.#', [access])..sourceInformation = hoverInfo)
+          .toAssignExpression(js.call('this.#', [access])
+            ..sourceInformation = _nodeSpan(hoverInfo))
           .toStatement());
     }
 
@@ -2518,10 +2525,10 @@
     _emitCovarianceBoundsCheck(
         element.typeParameters, _classProperties?.covariantParameters, body);
     for (var param in parameters.parameters) {
-      var jsParam = _emitSimpleIdentifier(param.identifier);
+      var jsParam = _emitParameter(param.identifier.staticElement)
+        ..sourceInformation = _nodeStart(param.identifier);
 
-      if (!options.destructureNamedParams &&
-          param.kind != ParameterKind.REQUIRED) {
+      if (param.kind != ParameterKind.REQUIRED) {
         if (param.kind == ParameterKind.NAMED) {
           // Parameters will be passed using their real names, not the (possibly
           // renamed) local variable.
@@ -2577,7 +2584,7 @@
       var defaultValue = param.defaultValue;
       return _isJSUndefined(defaultValue)
           ? null
-          : _visitAndMarkExpression(defaultValue);
+          : _visitExpression(defaultValue);
     } else {
       return new JS.LiteralNull();
     }
@@ -2599,8 +2606,7 @@
     if (node.isGetter) {
       return new JS.Fun([], js.statement('{ return this.#; }', [name]));
     } else if (node.isSetter) {
-      var params =
-          _emitFormalParameterList(node.parameters, destructure: false);
+      var params = _emitFormalParameters(node.parameters?.parameters);
       return new JS.Fun(
           params, js.statement('{ this.# = #; }', [name, params.last]));
     } else {
@@ -2629,7 +2635,8 @@
         new JS.Method(_declareMemberName(node.element), fn,
             isGetter: node.isGetter,
             isSetter: node.isSetter,
-            isStatic: node.isStatic),
+            isStatic: node.isStatic)
+          ..sourceInformation = _functionEnd(node),
         node.element,
         node);
   }
@@ -2673,6 +2680,7 @@
         _isInlineJSFunction(node.functionExpression)) {
       fn = JS.simplifyPassThroughArrowFunCallBody(fn);
     }
+    fn.sourceInformation = _functionEnd(node);
 
     var element = resolutionMap.elementDeclaredByFunctionDeclaration(node);
     var nameExpr = _emitTopLevelName(element);
@@ -2710,7 +2718,8 @@
     return closureAnnotate(
         new JS.Method(_propertyName(name),
             _emitFunctionExpression(node.functionExpression),
-            isGetter: node.isGetter, isSetter: node.isSetter),
+            isGetter: node.isGetter, isSetter: node.isSetter)
+          ..sourceInformation = _functionEnd(node),
         node.element,
         node);
   }
@@ -2762,11 +2771,7 @@
   }
 
   JS.ArrowFun _emitArrowFunction(FunctionExpression node) {
-    JS.Fun fn = _emitFunction(node.element, node.parameters, node.body);
-    return _toArrowFunction(fn);
-  }
-
-  JS.ArrowFun _toArrowFunction(JS.Fun f) {
+    var f = _emitFunction(node.element, node.parameters, node.body);
     JS.Node body = f.body;
 
     // Simplify `=> { return e; }` to `=> e`
@@ -2781,8 +2786,7 @@
     // Convert `function(...) { ... }` to `(...) => ...`
     // This is for readability, but it also ensures correct `this` binding.
     return new JS.ArrowFun(f.params, body,
-        typeParams: f.typeParams, returnType: f.returnType)
-      ..sourceInformation = f.sourceInformation;
+        typeParams: f.typeParams, returnType: f.returnType);
   }
 
   /// Emits a non-arrow FunctionExpression node.
@@ -2802,14 +2806,16 @@
 
     // normal function (sync), vs (sync*, async, async*)
     var isSync = !(element.isAsynchronous || element.isGenerator);
-    var formals = _emitFormalParameterList(parameters, destructure: isSync);
+    var formals = _emitFormalParameters(parameters?.parameters);
     var typeFormals = _emitTypeFormals(type.typeFormals);
     if (_reifyGeneric(element)) formals.insertAll(0, typeFormals);
 
     JS.Block code = isSync
         ? _emitFunctionBody(element, parameters, body)
-        : new JS.Block(
-            [_emitGeneratorFunction(element, parameters, body).toReturn()]);
+        : new JS.Block([
+            _emitGeneratorFunction(element, parameters, body).toReturn()
+              ..sourceInformation = _nodeStart(body)
+          ]);
     if (element.isOperator && formals.isNotEmpty) {
       if (element.name == '[]=') {
         // []= methods need to return the value. We could also address this at
@@ -2915,6 +2921,7 @@
         // itself, for recursive calls.
         gen = new JS.NamedFunction(new JS.TemporaryId(name), gen);
       }
+      gen.sourceInformation = _functionEnd(body);
       if (JS.This.foundIn(gen)) gen = js.call('#.bind(this)', gen);
 
       _superAllowed = savedSuperAllowed;
@@ -2945,8 +2952,7 @@
       var params = parameters?.parameters;
 
       var jsParams = _emitFormalParameters(
-          params?.where((p) => isPotentiallyMutated(body, p.element)),
-          destructure: false);
+          params?.where((p) => isPotentiallyMutated(body, p.element)));
 
       var gen = emitGeneratorFn(jsParams);
       if (jsParams.isNotEmpty) gen = js.call('() => #(#)', [gen, jsParams]);
@@ -2994,7 +3000,7 @@
 
     var fn = _emitFunctionExpression(func.functionExpression);
 
-    var name = new JS.Identifier(func.name.name)..sourceInformation = func.name;
+    var name = _emitVariableDef(func.name);
     JS.Statement declareFn;
     if (JS.This.foundIn(fn)) {
       declareFn = js.statement('const # = #.bind(this);', [name, fn]);
@@ -3006,7 +3012,7 @@
       declareFn = new JS.Block(
           [declareFn, _emitFunctionTagged(name, element.type).toStatement()]);
     }
-    return declareFn..sourceInformation = node;
+    return declareFn;
   }
 
   /// Emits a simple identifier, including handling an inferred generic
@@ -3014,7 +3020,8 @@
   @override
   JS.Expression visitSimpleIdentifier(SimpleIdentifier node) {
     var typeArgs = _getTypeArgs(node.staticElement, node.staticType);
-    var simpleId = _emitSimpleIdentifier(node)..sourceInformation = node;
+    var simpleId = _emitSimpleIdentifier(node)
+      ..sourceInformation = _nodeSpan(node);
     if (typeArgs == null) return simpleId;
     return _callHelper('gbind(#, #)', [simpleId, typeArgs]);
   }
@@ -3112,6 +3119,11 @@
     return new JS.Identifier(name);
   }
 
+  JS.Identifier _emitVariableDef(SimpleIdentifier id, {JS.TypeRef type}) {
+    return new JS.Identifier(id.name, type: type)
+      ..sourceInformation = _nodeStart(id);
+  }
+
   /// Returns `true` if the type name referred to by [node] is used in a
   /// position where it should evaluate as a type literal -- an object of type
   /// Type.
@@ -3537,7 +3549,7 @@
     } else {
       target = new JS.Identifier(element.name);
     }
-    target.sourceInformation = left;
+    target.sourceInformation = _nodeSpan(left);
     return _visitExpression(rhs).toAssignExpression(target);
   }
 
@@ -3555,10 +3567,10 @@
     var member = _emitMemberName(field.name,
         isStatic: isStatic, type: classElem.type, element: field.setter);
     jsTarget = isStatic
-        ? new JS.PropertyAccess(_emitStaticAccess(classElem), member)
-        : _emitTargetAccess(jsTarget, member, field.setter);
-    return _visitExpression(right)
-        .toAssignExpression(jsTarget..sourceInformation = id);
+        ? (new JS.PropertyAccess(_emitStaticAccess(classElem), member)
+          ..sourceInformation = _nodeSpan(id))
+        : _emitTargetAccess(jsTarget, member, field.setter, id);
+    return _visitExpression(right).toAssignExpression(jsTarget);
   }
 
   JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) {
@@ -3581,7 +3593,7 @@
 
   @override
   JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
-    return new JS.Block([_visitAndMarkExpression(node.expression).toReturn()]);
+    return new JS.Block([_visitExpression(node.expression).toReturn()]);
   }
 
   @override
@@ -3647,16 +3659,19 @@
   JS.Expression _emitTarget(Expression target, Element member, bool isStatic) {
     if (isStatic) {
       if (member is ConstructorElement) {
-        return _emitConstructorAccess(member.enclosingElement.type);
+        return _emitConstructorAccess(member.enclosingElement.type)
+          ..sourceInformation = _nodeSpan(target);
       }
       if (member is PropertyAccessorElement) {
         var field = member.variable;
         if (field is FieldElement) {
-          return _emitStaticAccess(field.enclosingElement);
+          return _emitStaticAccess(field.enclosingElement)
+            ..sourceInformation = _nodeSpan(target);
         }
       }
       if (member is MethodElement) {
-        return _emitStaticAccess(member.enclosingElement);
+        return _emitStaticAccess(member.enclosingElement)
+          ..sourceInformation = _nodeSpan(target);
       }
     }
     return _visitExpression(target);
@@ -3664,13 +3679,16 @@
 
   /// Emits the [JS.PropertyAccess] for accessors or method calls to
   /// [jsTarget].[jsName], replacing `super` if it is not allowed in scope.
-  JS.Expression _emitTargetAccess(
-      JS.Expression jsTarget, JS.Expression jsName, Element member) {
+  JS.Expression _emitTargetAccess(JS.Expression jsTarget, JS.Expression jsName,
+      Element member, AstNode node) {
+    JS.Expression result;
     if (!_superAllowed && jsTarget is JS.Super && member != null) {
-      return _getSuperHelper(member, jsName)
-        ..sourceInformation = jsTarget.sourceInformation;
+      result = _getSuperHelper(member, jsName);
+    } else {
+      result = new JS.PropertyAccess(jsTarget, jsName);
     }
-    return new JS.PropertyAccess(jsTarget, jsName);
+    if (node != null) result.sourceInformation = _nodeEnd(node);
+    return result;
   }
 
   JS.Expression _getSuperHelper(Element member, JS.Expression jsName) {
@@ -3720,7 +3738,8 @@
     JS.Expression jsTarget = _emitTarget(target, element, isStatic);
     if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) {
       if (jsTarget is JS.Super) {
-        jsTarget = _emitTargetAccess(jsTarget, jsName, element);
+        jsTarget =
+            _emitTargetAccess(jsTarget, jsName, element, node.methodName);
         return _emitDynamicInvoke(jsTarget, typeArgs, args);
       }
       if (typeArgs != null) {
@@ -3740,7 +3759,7 @@
       assert(typeArgs == null); // Object methods don't take type args.
       return _callHelper('#(#, #)', [name, jsTarget, args]);
     }
-    jsTarget = _emitTargetAccess(jsTarget, jsName, element);
+    jsTarget = _emitTargetAccess(jsTarget, jsName, element, node.methodName);
     var castTo = getImplicitOperationCast(node);
     if (castTo != null) {
       jsTarget = js.call('#._check(#)', [_emitType(castTo), jsTarget]);
@@ -4016,65 +4035,19 @@
     return jsParams;
   }
 
-  List<JS.Parameter> _emitFormalParameterList(FormalParameterList parameters,
-          {bool destructure: true}) =>
-      _emitFormalParameters(parameters?.parameters, destructure: destructure);
-
-  List<JS.Parameter> _emitFormalParameters(Iterable<FormalParameter> parameters,
-      {bool destructure: true}) {
+  List<JS.Parameter> _emitFormalParameters(
+      Iterable<FormalParameter> parameters) {
     if (parameters == null) return [];
 
-    destructure = destructure && options.destructureNamedParams;
-
     var result = <JS.Parameter>[];
-    var namedVars = <JS.DestructuredVariable>[];
-    var hasNamedArgsConflictingWithObjectProperties = false;
-    var needsOpts = false;
-
     for (var param in parameters) {
       if (param.kind == ParameterKind.NAMED) {
-        if (destructure) {
-          if (JS.objectProperties.contains(param.identifier.name)) {
-            hasNamedArgsConflictingWithObjectProperties = true;
-          }
-          var name = _visitExpression(param.identifier);
-          JS.SimpleBindingPattern structure;
-          var paramName = param.identifier.name;
-          if (JS.invalidVariableName(paramName)) {
-            structure = new JS.SimpleBindingPattern(name);
-            name = js.string(paramName);
-          }
-
-          var defaultValue = _defaultParamValue(param);
-          namedVars.add(new JS.DestructuredVariable(
-              name: name, structure: structure, defaultValue: defaultValue));
-        } else {
-          needsOpts = true;
-        }
-      } else {
-        var jsParam = _visitExpression(param);
-        var defaultValue = _defaultParamValue(param);
-        result.add(destructure && defaultValue != null
-            ? new JS.DestructuredVariable(
-                name: jsParam, defaultValue: defaultValue)
-            : jsParam);
+        result.add(namedArgumentTemp);
+        break;
       }
+      result.add(_emitFormalParameter(param));
     }
 
-    if (needsOpts) {
-      result.add(namedArgumentTemp);
-    } else if (namedVars.isNotEmpty) {
-      // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
-      // in case there are conflicting names we create an object without
-      // any prototype.
-      var defaultOpts = hasNamedArgsConflictingWithObjectProperties
-          ? js.call('Object.create(null)')
-          : js.call('{}');
-      result.add(new JS.DestructuredVariable(
-          structure: new JS.ObjectBindingPattern(namedVars),
-          type: emitNamedParamsArgType(parameters.map((p) => p.element)),
-          defaultValue: defaultOpts));
-    }
     return result;
   }
 
@@ -4134,8 +4107,12 @@
       //     if (stream.addStream(e)) return;
       //     yield;
       var helperName = star ? 'addStream' : 'add';
-      return js.statement('{ if(#.#(#)) return; #; }',
-          [_asyncStarController, helperName, jsExpr, new JS.Yield(null)]);
+      return js.statement('{ if(#.#(#)) return; #; }', [
+        _asyncStarController,
+        helperName,
+        jsExpr,
+        new JS.Yield(null)..sourceInformation = _nodeStart(node)
+      ]);
     }
     // A normal yield in a sync*
     return jsExpr.toYieldStatement(star: star);
@@ -4171,7 +4148,7 @@
       var variable = variables[0];
       var initializer = variable.initializer;
       if (initializer != null) {
-        var name = new JS.Identifier(variable.name.name);
+        var name = _emitVariableDef(variable.name);
         JS.Expression value;
         if (_annotatedNullCheck(variable.element)) {
           value = notNull(initializer);
@@ -4198,12 +4175,13 @@
         return value.toVariableDeclaration(name);
       }
     }
-    return _visitExpression(node.variables).toStatement();
+    return visitVariableDeclarationList(node.variables).toStatement();
   }
 
   @override
   JS.VariableDeclarationList visitVariableDeclarationList(
       VariableDeclarationList node) {
+    if (node == null) return null;
     return new JS.VariableDeclarationList(
         'let', node.variables?.map(visitVariableDeclaration)?.toList());
   }
@@ -4217,8 +4195,7 @@
     }
 
     var name =
-        new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type))
-          ..sourceInformation = node.name;
+        _emitVariableDef(node.name, type: emitTypeRef(node.element.type));
     return new JS.VariableInitialization(
         name, _visitInitializer(node.initializer, node.element));
   }
@@ -4262,9 +4239,9 @@
     // explicitly initialize to null, to avoid getting `undefined`.
     // TODO(jmesserly): do this only for vars that aren't definitely assigned.
     if (init == null) return new JS.LiteralNull();
-    var value =
-        _annotatedNullCheck(variable) ? notNull(init) : _visitExpression(init);
-    return value..sourceInformation = init;
+    return _annotatedNullCheck(variable)
+        ? notNull(init)
+        : _visitExpression(init);
   }
 
   JS.Statement _emitLazyFields(
@@ -4470,7 +4447,8 @@
     }
 
     return _emitInstanceCreationExpression(element, getType(constructor.type),
-        name, node.argumentList, node.isConst);
+        name, node.argumentList, node.isConst)
+      ..sourceInformation = _nodeStart(node.constructorName);
   }
 
   bool isPrimitiveType(DartType t) => _typeRep.isPrimitive(t);
@@ -4563,7 +4541,8 @@
     // conversion.
     if (op.type == TokenType.BAR_BAR ||
         op.type == TokenType.AMPERSAND_AMPERSAND) {
-      return _visitTest(node);
+      return _visitTest(node)
+        ..sourceInformation = _getLocation(node.operator.offset);
     }
 
     if (op.type.isEqualityOperator) return _emitEqualityOperator(node, op);
@@ -4587,6 +4566,11 @@
     var leftType = getStaticType(left);
     var rightType = getStaticType(right);
 
+    JS.Expression operatorCall() {
+      return _emitSend(left, op.lexeme, [right])
+        ..sourceInformation = _getLocation(node.operator.offset);
+    }
+
     if (_typeRep.binaryOperationIsPrimitive(leftType, rightType) ||
         leftType == types.stringType && op.type == TokenType.PLUS) {
       // special cases where we inline the operation
@@ -4594,7 +4578,8 @@
       // TODO(jmesserly): it would be nice to just inline the method from core,
       // instead of special cases here.
       JS.Expression binary(String code) {
-        return js.call(code, [notNull(left), notNull(right)]);
+        return js.call(code, [notNull(left), notNull(right)])
+          ..sourceInformation = _getLocation(node.operator.offset);
       }
 
       JS.Expression bitwise(String code) {
@@ -4606,12 +4591,13 @@
           // `a ~/ b` is equivalent to `(a / b).truncate()`
           var div = ast.binaryExpression(left, '/', right)
             ..staticType = node.staticType;
-          return _emitSend(div, 'truncate', []);
+          return _emitSend(div, 'truncate', [])
+            ..sourceInformation = _getLocation(node.operator.offset);
 
         case TokenType.PERCENT:
           // TODO(sra): We can generate `a % b + 0` if both are non-negative
           // (the `+ 0` is to coerce -0.0 to 0).
-          return _emitSend(left, op.lexeme, [right]);
+          return operatorCall();
 
         case TokenType.AMPERSAND:
           return bitwise('# & #');
@@ -4636,7 +4622,7 @@
               _parentMasksToWidth(node, 31 - shiftCount)) {
             return binary('# >> #');
           }
-          return _emitSend(left, op.lexeme, [right]);
+          return operatorCall();
 
         case TokenType.LT_LT:
           if (_is31BitUnsigned(node)) {
@@ -4647,7 +4633,7 @@
           if (_asIntInRange(right, 0, 31) != null) {
             return _coerceBitOperationResultToUnsigned(node, binary('# << #'));
           }
-          return _emitSend(left, op.lexeme, [right]);
+          return operatorCall();
 
         default:
           // TODO(vsm): When do Dart ops not map to JS?
@@ -4655,7 +4641,7 @@
       }
     }
 
-    return _emitSend(left, op.lexeme, [right]);
+    return operatorCall();
   }
 
   /// Bit operations are coerced to values on [0, 2^32). The coercion changes
@@ -5070,32 +5056,16 @@
       // The printer handles precedence so we don't need to.
       _visitExpression(node.expression);
 
-  @override
-  JS.Identifier visitDefaultFormalParameter(DefaultFormalParameter node) {
-    return _emitParameter(node.element, declaration: true);
-  }
-
-  JS.Parameter _emitNormalFormalParameter(NormalFormalParameter node) {
-    var id = _emitParameter(node.element, declaration: true);
-    var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null;
+  JS.Parameter _emitFormalParameter(FormalParameter node) {
+    var id = _emitParameter(node.element, declaration: true)
+      ..sourceInformation = _nodeSpan(node);
+    var isRestArg = node is! DefaultFormalParameter &&
+        findAnnotation(node.element, isJsRestAnnotation) != null;
     return isRestArg ? new JS.RestParameter(id) : id;
   }
 
   @override
-  visitSimpleFormalParameter(SimpleFormalParameter node) =>
-      _emitNormalFormalParameter(node);
-
-  @override
-  visitFieldFormalParameter(FieldFormalParameter node) =>
-      _emitNormalFormalParameter(node);
-
-  @override
-  visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) =>
-      _emitNormalFormalParameter(node);
-
-  @override
-  JS.This visitThisExpression(ThisExpression node) =>
-      new JS.This()..sourceInformation = node;
+  JS.This visitThisExpression(ThisExpression node) => new JS.This();
 
   @override
   JS.Expression visitSuperExpression(SuperExpression node) => new JS.Super();
@@ -5107,11 +5077,13 @@
       return _callHelper('loadLibrary');
     }
 
+    JS.Expression result;
     if (isLibraryPrefix(node.prefix)) {
-      return _visitExpression(node.identifier);
+      result = _visitExpression(node.identifier);
     } else {
-      return _emitAccess(node.prefix, node.identifier, node.staticType);
+      result = _emitAccess(node.prefix, node.identifier, node.staticType);
     }
+    return result;
   }
 
   @override
@@ -5197,7 +5169,7 @@
         !virtualFields.isVirtual(field)) {
       // If super.x is a sealed field, then x is an instance property since
       // subclasses cannot override x.
-      jsTarget = new JS.This()..sourceInformation = target;
+      jsTarget = new JS.This()..sourceInformation = jsTarget.sourceInformation;
     }
 
     JS.Expression result;
@@ -5213,12 +5185,12 @@
         result = _callHelper('tagStatic(#, #)', [jsTarget, jsName]);
       } else if (isSuper) {
         result = _callHelper('bind(this, #, #)',
-            [jsName, _emitTargetAccess(jsTarget, jsName, accessor)]);
+            [jsName, _emitTargetAccess(jsTarget, jsName, accessor, memberId)]);
       } else {
         result = _callHelper('bind(#, #)', [jsTarget, jsName]);
       }
     } else {
-      result = _emitTargetAccess(jsTarget, jsName, accessor);
+      result = _emitTargetAccess(jsTarget, jsName, accessor, memberId);
     }
     return typeArgs == null
         ? result
@@ -5253,7 +5225,7 @@
 
     // Generic dispatch to a statically known method.
     return js.call('#(#)', [
-      _emitTargetAccess(_visitExpression(target), memberName, element),
+      _emitTargetAccess(_visitExpression(target), memberName, element, null),
       _visitExpressionList(args)
     ]);
   }
@@ -5324,14 +5296,14 @@
 
   @override
   JS.For visitForStatement(ForStatement node) {
-    var init = _visitAndMarkExpression(node.initialization) ??
-        _visitExpression(node.variables);
+    var init = _visitExpression(node.initialization) ??
+        visitVariableDeclarationList(node.variables);
     var updaters = node.updaters;
     JS.Expression update;
     if (updaters != null && updaters.isNotEmpty) {
-      update = new JS.Expression.binary(
-              updaters.map(_visitAndMarkExpression).toList(), ',')
-          .toVoidExpression();
+      update =
+          new JS.Expression.binary(updaters.map(_visitExpression).toList(), ',')
+              .toVoidExpression();
     }
     var condition = _visitTest(node.condition);
     return new JS.For(init, condition, update, _visitScope(node.body));
@@ -5354,14 +5326,14 @@
     }
 
     var init = _visitExpression(node.identifier);
-    var iterable = _visitAndMarkExpression(node.iterable);
+    var iterable = _visitExpression(node.iterable);
     var body = _visitScope(node.body);
     if (init == null) {
-      var name = node.loopVariable.identifier.name;
-      init = js.call('let #', name);
+      var id = node.loopVariable.identifier;
+      init = js.call('let #', _emitVariableDef(id));
       if (_annotatedNullCheck(node.loopVariable.element)) {
-        body =
-            new JS.Block([_nullParameterCheck(new JS.Identifier(name)), body]);
+        body = new JS.Block(
+            [_nullParameterCheck(new JS.Identifier(id.name)), body]);
       }
     }
     return new JS.ForOf(init, iterable, body);
@@ -5392,13 +5364,11 @@
         null,
         ast.argumentList([node.iterable]),
         false);
-    var iter = _visitExpression(
-        _createTemporary('it', streamIterator, nullable: false));
-
+    var iter = new JS.TemporaryId('iter');
+    var variable = node.identifier ?? node.loopVariable.identifier;
     var init = _visitExpression(node.identifier);
     if (init == null) {
-      init = js
-          .call('let # = #.current', [node.loopVariable.identifier.name, iter]);
+      init = js.call('let # = #.current', [_emitVariableDef(variable), iter]);
     } else {
       init = js.call('# = #.current', [init, iter]);
     }
@@ -5412,10 +5382,12 @@
         [
           iter,
           createStreamIter,
-          new JS.Yield(js.call('#.moveNext()', iter)),
+          new JS.Yield(js.call('#.moveNext()', iter))
+            ..sourceInformation = _nodeStart(variable),
           init,
           _visitStatement(node.body),
           new JS.Yield(js.call('#.cancel()', iter))
+            ..sourceInformation = _nodeStart(variable)
         ]);
   }
 
@@ -5448,7 +5420,9 @@
     // This could incorrectly shadow a user's name.
     var savedCatch = _catchParameter;
 
-    if (clauses.length == 1 && clauses.single.exceptionParameter != null) {
+    var isSingleCatch =
+        clauses.length == 1 && clauses.single.exceptionParameter != null;
+    if (isSingleCatch) {
       // Special case for a single catch.
       _catchParameter = clauses.single.exceptionParameter;
     } else {
@@ -5462,6 +5436,9 @@
     }
 
     var catchVarDecl = _emitSimpleIdentifier(_catchParameter);
+    if (isSingleCatch) {
+      catchVarDecl..sourceInformation = _nodeStart(_catchParameter);
+    }
     _catchParameter = savedCatch;
     return new JS.Catch(catchVarDecl, new JS.Block([catchBody]));
   }
@@ -5479,7 +5456,8 @@
     return new JS.If(
         js.call('#.is(#)', [castType, _emitSimpleIdentifier(_catchParameter)]),
         then,
-        otherwise);
+        otherwise)
+      ..sourceInformation = _nodeStart(clause);
   }
 
   /// Visits the catch clause body. This skips the exception type guard, if any.
@@ -5496,17 +5474,15 @@
         vars.add(name.name);
       } else if (name != null) {
         vars.add(name.name);
-        body.add(js.statement('let # = #;', [
-          _emitSimpleIdentifier(name),
-          _emitSimpleIdentifier(_catchParameter)
-        ]));
+        body.add(js.statement('let # = #;',
+            [_emitVariableDef(name), _emitSimpleIdentifier(_catchParameter)]));
         _catchParameter = name;
       }
       var stackVar = node.stackTraceParameter;
       if (stackVar != null) {
         vars.add(stackVar.name);
         body.add(js.statement('let # = #.stackTrace(#);', [
-          _emitSimpleIdentifier(stackVar),
+          _emitVariableDef(stackVar),
           _runtimeModule,
           _emitSimpleIdentifier(name)
         ]));
@@ -5520,7 +5496,7 @@
 
   @override
   JS.Case visitSwitchCase(SwitchCase node) {
-    var expr = _visitAndMarkExpression(node.expression);
+    var expr = _visitExpression(node.expression);
     var body = _visitStatementList(node.statements);
     if (node.labels.isNotEmpty) {
       body.insert(0, js.comment('Unimplemented case labels: ${node.labels}'));
@@ -5549,7 +5525,7 @@
 
   @override
   JS.Switch visitSwitchStatement(SwitchStatement node) => new JS.Switch(
-      _visitAndMarkExpression(node.expression),
+      _visitExpression(node.expression),
       node.members?.map(_emitSwitchMember)?.toList());
 
   @override
@@ -5715,30 +5691,22 @@
   @override
   visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value);
 
-  /// Visit a Dart [node] that produces a JS expression, without attaching
-  /// a source location.
-  JS.Expression _visitExpression(AstNode node) {
-    return node?.accept<JS.Node>(this) as JS.Expression;
-  }
-
   /// Visit a Dart [node] that produces a JS expression, and attaches a source
   /// location.
-  ///
-  /// Expressions should generally *not* be marked when they are visited as part
-  /// of other expressions. They should only be marked when needed for control
-  /// flow. For example, the condition in an `if (cond) {}` or the 3 branches in
-  /// a conditional `cond ? x : y`. Field initializers are another example.
-  JS.Expression _visitAndMarkExpression(AstNode node) {
-    var e = node?.accept<JS.Node>(this) as JS.Expression;
-    if (e != null) e.sourceInformation = node;
+  // TODO(jmesserly): parameter type should be `Expression`
+  JS.Expression _visitExpression(AstNode node) {
+    if (node == null) return null;
+    var e = node.accept<JS.Node>(this) as JS.Expression;
+    e.sourceInformation ??= _nodeStart(node);
     return e;
   }
 
   /// Visit a Dart [node] that produces a JS statement, and marks its source
   /// location for debugging.
   JS.Statement _visitStatement(AstNode node) {
-    var s = node?.accept<JS.Node>(this) as JS.Statement;
-    if (s != null) s.sourceInformation = node;
+    if (node == null) return null;
+    var s = node.accept<JS.Node>(this) as JS.Statement;
+    if (s is! Block) s.sourceInformation = _nodeStart(node);
     return s;
   }
 
@@ -5752,6 +5720,76 @@
     return nodes?.map(_visitExpression)?.toList();
   }
 
+  /// Gets the start position of [node] for use in source mapping.
+  ///
+  /// This is the most common kind of marking, and is used for most expressions
+  /// and statements.
+  SourceLocation _nodeStart(AstNode node) => _getLocation(node.offset);
+
+  /// Gets the end position of [node] for use in source mapping.
+  ///
+  /// This is used to complete a hover span, when we know the start position has
+  /// already been emitted. For example, `foo.bar` we only need to mark the end
+  /// of `.bar` to ensure `foo.bar` has a hover tooltip.
+  NodeEnd _nodeEnd(AstNode node) {
+    var loc = _getLocation(node.end);
+    return loc != null ? new NodeEnd(loc) : null;
+  }
+
+  /// Gets the end of a function for source mapping.
+  ///
+  /// JS wants a marking before the closing brace/semicolon, rather than after,
+  /// so this adjusts appropriately (it uses [node.endToken.offset] rather than
+  /// [node.end]).
+  ///
+  /// Every JS function should have an end marking for stepping out of it.
+  /// Alternatively this can be supplied with [_functionSpan].
+  NodeEnd _functionEnd(AstNode node) {
+    var loc = _getLocation(node.endToken.offset);
+    return loc != null ? new NodeEnd(loc) : null;
+  }
+
+  /// Similar to [_functionEnd] but also marks the start of the function.
+  ///
+  /// This is used when we want to support hovering.
+  NodeSpan _functionSpan(AstNode node) {
+    var start = _getLocation(node.offset);
+    var end = _getLocation(node.endToken.offset);
+    return start != null && end != null ? new NodeSpan(start, end) : null;
+  }
+
+  /// Combines [_nodeStart] and [_nodeEnd], used when we want to support
+  /// hovering on the [node].
+  NodeSpan _nodeSpan(AstNode node) {
+    var start = _getLocation(node.offset);
+    var end = _getLocation(node.end);
+    return start != null && end != null ? new NodeSpan(start, end) : null;
+  }
+
+  SourceLocation _getLocation(int offset) {
+    if (offset == -1) return null;
+    var unit = _currentCompilationUnit;
+    Uri fileUri;
+    if (unit.source.isInSystemLibrary) {
+      fileUri = unit.source.uri;
+    } else {
+      // TODO(jmesserly): this needs serious cleanup.
+      // There does appear to be something strange going on with Analyzer
+      // URIs if we try and use them directly on Windows.
+      // See also compiler.dart placeSourceMap, which could use cleanup too.
+      var sourcePath = unit.source.fullName;
+      fileUri = sourcePath.startsWith('package:')
+          ? Uri.parse(sourcePath)
+          // TODO(jmesserly): shouldn't this be path.toUri?
+          : new Uri.file(sourcePath);
+    }
+    var loc = unit.lineInfo.getLocation(offset);
+    return new SourceLocation(offset,
+        sourceUrl: fileUri,
+        line: loc.lineNumber - 1,
+        column: loc.columnNumber - 1);
+  }
+
   /// Generates an expression for a boolean conversion context (if, while, &&,
   /// etc.), where conversions and null checks are implemented via `dart.test`
   /// to give a more helpful message.
@@ -6153,6 +6191,22 @@
   @override
   visitDottedName(node) => _unreachable(node);
 
+  /// Unused, handled by [_emitFormalParameter].
+  @override
+  visitDefaultFormalParameter(node) => _unreachable(node);
+
+  /// Unused, handled by [_emitFormalParameter].
+  @override
+  visitSimpleFormalParameter(node) => _unreachable(node);
+
+  /// Unused, handled by [_emitFormalParameter].
+  @override
+  visitFieldFormalParameter(node) => _unreachable(node);
+
+  /// Unused, handled by [_emitFormalParameter].
+  @override
+  visitFunctionTypedFormalParameter(node) => _unreachable(node);
+
   /// Unused, handled by [visitEnumDeclaration].
   @override
   visitEnumConstantDeclaration(node) => _unreachable(node); // see
@@ -6161,7 +6215,7 @@
   @override
   visitExtendsClause(node) => _unreachable(node);
 
-  /// Unused, see [_emitFormalParameterList].
+  /// Unused, see [_emitFormalParameters].
   @override
   visitFormalParameterList(node) => _unreachable(node);
 
diff --git a/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart b/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
index ad6f169..a97aa6d 100644
--- a/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
@@ -18,6 +18,7 @@
 import 'package:analyzer/dart/element/element.dart'
     show
         ClassElement,
+        CompilationUnitElement,
         Element,
         ExecutableElement,
         FunctionElement,
@@ -206,3 +207,18 @@
       ? callMethod.returnType is FunctionType
       : callMethod != null;
 }
+
+Uri uriForCompilationUnit(CompilationUnitElement unit) {
+  if (unit.source.isInSystemLibrary) {
+    return unit.source.uri;
+  }
+  // TODO(jmesserly): this needs serious cleanup.
+  // There does appear to be something strange going on with Analyzer
+  // URIs if we try and use them directly on Windows.
+  // See also compiler.dart placeSourceMap, which could use cleanup too.
+  var sourcePath = unit.source.fullName;
+  return sourcePath.startsWith('package:')
+      ? Uri.parse(sourcePath)
+      // TODO(jmesserly): shouldn't this be path.toUri?
+      : new Uri.file(sourcePath);
+}
diff --git a/pkg/dev_compiler/lib/src/analyzer/js_typeref_codegen.dart b/pkg/dev_compiler/lib/src/analyzer/js_typeref_codegen.dart
index 617ac85..74be52e 100644
--- a/pkg/dev_compiler/lib/src/analyzer/js_typeref_codegen.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/js_typeref_codegen.dart
@@ -12,7 +12,7 @@
 import 'js_interop.dart';
 
 /// Mixin with logic to generate [TypeRef]s out of [DartType]s.
-abstract class JsTypeRefCodegen {
+abstract class JSTypeRefCodegen {
   final _resolved = <DartType, JS.TypeRef>{};
 
   // Mixin dependencies:
diff --git a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
index 120a36b..90cae8e 100644
--- a/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/module_compiler.dart
@@ -35,11 +35,11 @@
     show transformModuleFormat, ModuleFormat;
 import '../js_ast/js_ast.dart' as JS;
 import '../js_ast/js_ast.dart' show js;
+import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
 import 'code_generator.dart' show CodeGenerator;
 import 'context.dart' show AnalyzerOptions, createSourceFactory;
 import 'error_helpers.dart' show errorSeverity, formatError, sortErrors;
 import 'extension_types.dart' show ExtensionTypeSet;
-import 'source_map_printer.dart' show SourceMapPrintingContext;
 
 /// Compiles a set of Dart files into a single JavaScript module.
 ///
@@ -268,22 +268,6 @@
   /// Whether to emit Closure Compiler-friendly code.
   final bool closure;
 
-  /// Enable ES6 destructuring of named parameters. Off by default.
-  ///
-  /// Older V8 versions do not accept default values with destructuring in
-  /// arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
-  /// with regular functions (e.g. `function({a} = {}) { return 1 }`).
-  ///
-  /// Supporting the syntax:
-  /// * Chrome Canary (51)
-  /// * Firefox
-  ///
-  /// Not yet supporting:
-  /// * Atom (1.5.4)
-  /// * Electron (0.36.3)
-  // TODO(ochafik): Simplify this code when our target platforms catch up.
-  final bool destructureNamedParams;
-
   /// Mapping from absolute file paths to bazel short path to substitute in
   /// source maps.
   final Map<String, String> bazelMapping;
@@ -302,7 +286,6 @@
       this.replCompile: false,
       this.emitMetadata: false,
       this.closure: false,
-      this.destructureNamedParams: false,
       this.bazelMapping: const {},
       this.summaryOutPath});
 
@@ -316,7 +299,6 @@
         replCompile = args['repl-compile'],
         emitMetadata = args['emit-metadata'],
         closure = args['closure-experimental'],
-        destructureNamedParams = args['destructure-named-params'],
         bazelMapping = _parseBazelMappings(args['bazel-mapping']),
         summaryOutPath = args['summary-out'];
 
@@ -341,8 +323,6 @@
       ..addFlag('closure-experimental',
           help: 'emit Closure Compiler-friendly code (experimental)',
           defaultsTo: false)
-      ..addFlag('destructure-named-params',
-          help: 'Destructure named parameters', defaultsTo: false, hide: hide)
       ..addFlag('unsafe-force-compile',
           help: 'Compile code even if it has errors. ಠ_ಠ\n'
               'This has undefined behavior!',
diff --git a/pkg/dev_compiler/lib/src/analyzer/source_map_printer.dart b/pkg/dev_compiler/lib/src/analyzer/source_map_printer.dart
deleted file mode 100644
index 2b01425..0000000
--- a/pkg/dev_compiler/lib/src/analyzer/source_map_printer.dart
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:collection';
-
-import 'package:analyzer/dart/ast/ast.dart' show AstNode, CompilationUnit;
-import 'package:analyzer/src/generated/source.dart' show LineInfo;
-import 'package:source_maps/source_maps.dart' hide Printer;
-import 'package:source_span/source_span.dart' show SourceLocation;
-
-import '../js_ast/js_ast.dart' as JS;
-
-class SourceMapPrintingContext extends JS.SimpleJavaScriptPrintingContext {
-  /// Current line in the buffer.
-  int _line = 0;
-
-  /// Current column in the buffer.
-  int _column = 0;
-
-  /// The source_maps builder we write JavaScript code to.
-  final sourceMap = new SourceMapBuilder();
-
-  /// The last marked line in the buffer.
-  int _previousLine = -1;
-
-  /// The last marked column in the buffer.
-  int _previousColumn = -1;
-
-  Uri _sourceUri;
-  LineInfo _lineInfo;
-  JS.Node _topLevelNode;
-
-  final _ends = new HashMap<JS.Node, int>.identity();
-
-  @override
-  void emit(String code) {
-    var chars = code.runes.toList();
-    var length = chars.length;
-    for (int i = 0; i < length; i++) {
-      var c = chars[i];
-      if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) {
-        // Return not followed by line-feed is treated as a new line.
-        _line++;
-        _column = 0;
-      } else {
-        _column++;
-      }
-    }
-    super.emit(code);
-  }
-
-  void enterNode(JS.Node jsNode) {
-    var srcInfo = jsNode.sourceInformation as AstNode;
-    if (srcInfo == null || srcInfo.isSynthetic) return;
-    int offset = srcInfo.offset;
-    if (offset == -1) return;
-
-    // Make sure source locations are associated with the correct source file.
-    //
-    // Consecutive top-level declarations may come from different compilation
-    // units due to ordering within the JS module, and due to parts. Libraries
-    // and parts are not necessarily emitted in contiguous blocks.
-    if (_topLevelNode == null) {
-      // This happens for synthetic nodes created by AstFactory.
-      // We don't need to mark positions for them because we'll have a position
-      // for the next thing down.
-
-      var unit =
-          (srcInfo.getAncestor((n) => n is CompilationUnit) as CompilationUnit)
-              ?.element;
-
-      if (unit == null) return;
-      if (unit.source.isInSystemLibrary) {
-        _sourceUri = unit.source.uri;
-      } else {
-        // TODO(jmesserly): this needs serious cleanup.
-        // There does appear to be something strange going on with Analyzer
-        // URIs if we try and use them directly on Windows.
-        // See also compiler.dart placeSourceMap, which could use cleanup too.
-        var sourcePath = unit.source.fullName;
-        _sourceUri = sourcePath.startsWith('package:')
-            ? Uri.parse(sourcePath)
-            // TODO(jmesserly): shouldn't this be path.toUri?
-            : new Uri.file(sourcePath);
-      }
-
-      _topLevelNode = jsNode;
-      _lineInfo = unit.lineInfo;
-    }
-
-    _mark(offset);
-    _ends[jsNode] = srcInfo.end;
-  }
-
-  void exitNode(JS.Node jsNode) {
-    if (_topLevelNode == null) return;
-
-    var end = _ends.remove(jsNode);
-    if (end != null) _mark(end);
-
-    if (identical(jsNode, _topLevelNode)) {
-      _sourceUri = null;
-      _lineInfo = null;
-      _topLevelNode = null;
-    }
-  }
-
-  void _mark(int offset) {
-    if (_previousColumn == _column && _previousLine == _line) return;
-
-    var loc = _lineInfo.getLocation(offset);
-    // Chrome Devtools wants a mapping for the beginning of
-    // a line, so bump locations at the end of a line to the beginning of
-    // the next line.
-    var next = _lineInfo.getLocation(offset + 1);
-    if (next.lineNumber == loc.lineNumber + 1) {
-      loc = next;
-    }
-    _previousLine = _line;
-    _previousColumn = _column;
-    sourceMap.addLocation(
-        new SourceLocation(offset,
-            sourceUrl: _sourceUri,
-            line: loc.lineNumber - 1,
-            column: loc.columnNumber - 1),
-        new SourceLocation(buffer.length, line: _line, column: _column),
-        null);
-  }
-}
-
-const int _LF = 10;
-const int _CR = 13;
diff --git a/pkg/dev_compiler/lib/src/js_ast/printer.dart b/pkg/dev_compiler/lib/src/js_ast/printer.dart
index c54ea1a..2e42e50 100644
--- a/pkg/dev_compiler/lib/src/js_ast/printer.dart
+++ b/pkg/dev_compiler/lib/src/js_ast/printer.dart
@@ -581,7 +581,10 @@
   visitFunctionDeclaration(FunctionDeclaration declaration) {
     indent();
     outClosureAnnotation(declaration);
-    functionOut(declaration.function, declaration.name);
+    var f = declaration.function;
+    context.enterNode(f);
+    functionOut(f, declaration.name);
+    context.exitNode(f);
     lineOut();
   }
 
@@ -938,7 +941,10 @@
   }
 
   visitNamedFunction(NamedFunction namedFunction) {
-    functionOut(namedFunction.function, namedFunction.name);
+    var f = namedFunction.function;
+    context.enterNode(f);
+    functionOut(f, namedFunction.name);
+    context.exitNode(f);
   }
 
   visitFun(Fun fun) {
diff --git a/pkg/dev_compiler/lib/src/js_ast/source_map_printer.dart b/pkg/dev_compiler/lib/src/js_ast/source_map_printer.dart
new file mode 100644
index 0000000..d33d6ad
--- /dev/null
+++ b/pkg/dev_compiler/lib/src/js_ast/source_map_printer.dart
@@ -0,0 +1,158 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:source_maps/source_maps.dart' hide Printer;
+import 'package:source_span/source_span.dart' show SourceLocation;
+import 'js_ast.dart';
+
+class NodeEnd {
+  final SourceLocation end;
+  NodeEnd(this.end);
+}
+
+class NodeSpan {
+  final SourceLocation start, end;
+  NodeSpan(this.start, this.end);
+}
+
+class SourceMapPrintingContext extends SimpleJavaScriptPrintingContext {
+  /// Current line in the buffer.
+  int _line = 0;
+
+  /// Current column in the buffer.
+  int _column = 0;
+
+  /// The source_maps builder we write JavaScript code to.
+  final sourceMap = new SourceMapBuilder();
+
+  /// The last marked line in the buffer.
+  int _previousDartOffset = -1;
+
+  SourceLocation _pendingDartOffset;
+  SourceLocation _pendingJSLocation;
+
+  @override
+  void emit(String code) {
+    var chars = code.runes.toList();
+    var length = chars.length;
+    for (int i = 0; i < length; i++) {
+      var c = chars[i];
+      if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) {
+        // Return not followed by line-feed is treated as a new line.
+        _line++;
+        _column = 0;
+      } else {
+        _column++;
+      }
+    }
+    super.emit(code);
+  }
+
+  @override
+  void enterNode(Node node) {
+    var srcInfo = node.sourceInformation;
+    if (srcInfo == null) return;
+
+    SourceLocation dartStart;
+    if (srcInfo is SourceLocation) {
+      dartStart = srcInfo;
+    } else if (srcInfo is NodeSpan) {
+      dartStart = srcInfo.start;
+    } else if (srcInfo is! NodeEnd) {
+      throw new StateError(
+          'wrong kind of source map data: `$srcInfo` <${srcInfo.runtimeType}>');
+    }
+
+    if (dartStart != null) {
+      if (node is Expression) {
+        _markExpressionStart(dartStart);
+      } else {
+        _mark(dartStart);
+      }
+    }
+  }
+
+  @override
+  void exitNode(Node node) {
+    if (node is! Expression) _flushPendingMarks();
+
+    var srcInfo = node.sourceInformation;
+    if (srcInfo == null) return;
+
+    SourceLocation dartEnd;
+    if (srcInfo is NodeSpan) {
+      dartEnd = srcInfo.end;
+    } else if (srcInfo is NodeEnd) {
+      dartEnd = srcInfo.end;
+    } else if (srcInfo is! SourceLocation) {
+      throw new StateError(
+          'wrong kind of source map data: `$srcInfo` <${srcInfo.runtimeType}>');
+    }
+
+    if (dartEnd != null) {
+      if (node is Fun || node is Method || node is NamedFunction) {
+        // Mark the exit point of a function. V8 steps to the end of a function
+        // at its exit, so this provides a mapping for that location.
+        int column = _column - 1;
+        if (column >= 0) {
+          // Adjust the colum, because any ending brace or semicolon is already in
+          // the output.
+          var jsEnd = new SourceLocation(buffer.length - 1,
+              line: _line, column: column);
+          _flushPendingMarks();
+          _mark(dartEnd, jsEnd);
+        }
+      } else {
+        _flushPendingMarks();
+        _mark(dartEnd);
+      }
+    }
+  }
+
+  /// Marks that we entered a Dart node at this offset.
+  ///
+  /// Multiple nested Dart expressions will appear to start at the same Dart
+  /// location, but they can have different JavaScript locations. For example:
+  ///
+  ///     get foo => _foo.bar && _baz;
+  ///     //         ^^^^^^^^^^^^^^^^ binary-op, JS code `dart.test(...) && ...`
+  ///     //         ^^^^^^^^         property access, JS code `this[_foo].bar`
+  ///     //         ^^^^             identifier, JS code `this[_foo]`
+  ///
+  /// To ensure hover works, we must not include `dart.test` at the start of
+  /// `_foo`. Also to ensure `_foo` and `_foo.bar` works, we must mark the end
+  /// of `_foo`.
+  void _markExpressionStart(SourceLocation dartOffset) {
+    if (_pendingDartOffset != dartOffset) _flushPendingMarks();
+    _pendingDartOffset = dartOffset;
+    _pendingJSLocation = _getJSLocation();
+  }
+
+  /// Mark any pending marks from [_markExpressionStart].
+  ///
+  /// This ensures we get the innermost JS offset for a given Dart offset.
+  void _flushPendingMarks() {
+    var pending = _pendingDartOffset;
+    if (pending != null) {
+      _mark(pending, _pendingJSLocation);
+      _pendingDartOffset = null;
+      _pendingJSLocation = null;
+    }
+  }
+
+  void _mark(SourceLocation dartLocation, [SourceLocation jsLocation]) {
+    jsLocation ??= _getJSLocation();
+
+    // Don't mark the same JS location to two different Dart locations.
+    if (_previousDartOffset == dartLocation.offset) return;
+    _previousDartOffset = dartLocation.offset;
+    sourceMap.addLocation(dartLocation, jsLocation, null);
+  }
+
+  SourceLocation _getJSLocation() =>
+      new SourceLocation(buffer.length, line: _line, column: _column);
+}
+
+const int _LF = 10;
+const int _CR = 13;
diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart
index e35b71a..bdc4519 100644
--- a/pkg/dev_compiler/lib/src/kernel/command.dart
+++ b/pkg/dev_compiler/lib/src/kernel/command.dart
@@ -18,8 +18,8 @@
 import '../compiler/js_names.dart' as JS;
 import '../compiler/module_builder.dart';
 import '../js_ast/js_ast.dart' as JS;
+import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
 import 'compiler.dart';
-import 'source_map_printer.dart';
 
 const _binaryName = 'dartdevk';
 
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index a0477ec..c8494bd 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -11,12 +11,14 @@
 import 'package:kernel/kernel.dart' hide ConstantVisitor;
 import 'package:kernel/type_algebra.dart';
 import 'package:kernel/type_environment.dart';
+import 'package:source_span/source_span.dart' show SourceLocation;
 
 import '../compiler/js_names.dart' as JS;
 import '../compiler/js_utils.dart' as JS;
 import '../compiler/module_builder.dart' show pathToJSIdentifier;
 import '../js_ast/js_ast.dart' as JS;
 import '../js_ast/js_ast.dart' show js;
+import '../js_ast/source_map_printer.dart' show NodeEnd, NodeSpan;
 import 'js_interop.dart';
 import 'js_typerep.dart';
 import 'kernel_helpers.dart';
@@ -87,6 +89,11 @@
   ///
   Class _currentClass;
 
+  /// The current source file URI for emitting in the source map.
+  Uri _currentUri;
+
+  Program _program;
+
   Library _currentLibrary;
 
   FunctionNode _currentFunction;
@@ -235,6 +242,8 @@
     if (_moduleItems.isNotEmpty) {
       throw new StateError('Can only call emitModule once.');
     }
+    _program = p;
+
     for (var i = 0; i < summaries.length; i++) {
       var summary = summaries[i];
       var summaryUri = summaryUris[i];
@@ -464,15 +473,18 @@
 
     var savedClass = _currentClass;
     var savedLibrary = _currentLibrary;
+    var savedUri = _currentUri;
     _currentClass = c;
     types.thisType = c.thisType;
     _currentLibrary = c.enclosingLibrary;
+    _currentUri = c.fileUri;
 
     _moduleItems.add(_emitClassDeclaration(c));
 
     _currentClass = savedClass;
     types.thisType = savedClass?.thisType;
     _currentLibrary = savedLibrary;
+    _currentUri = savedUri;
   }
 
   /// To emit top-level classes, we sometimes need to reorder them.
@@ -1311,8 +1323,9 @@
         () => _superDisallowed(
             () => _emitConstructorBody(node, fields, className)));
 
-    var block = new JS.Block(body)..sourceInformation = node;
-    return _finishConstructorFunction(params, block, isCallable);
+    return _finishConstructorFunction(params, new JS.Block(body), isCallable)
+      ..sourceInformation = _nodeEnd(node.fileEndOffset) ??
+          _nodeEnd(node.enclosingClass.fileEndOffset);
   }
 
   List<JS.Statement> _emitConstructorBody(
@@ -1350,7 +1363,9 @@
     var superCall = node.initializers.firstWhere((i) => i is SuperInitializer,
         orElse: () => null) as SuperInitializer;
     var jsSuper = _emitSuperConstructorCallIfNeeded(cls, className, superCall);
-    if (jsSuper != null) body.add(jsSuper..sourceInformation = superCall);
+    if (jsSuper != null) {
+      body.add(jsSuper..sourceInformation = _nodeStart(superCall));
+    }
 
     body.add(_emitFunctionScopedBody(fn));
     return body;
@@ -1424,9 +1439,7 @@
     // We consider a class callable if it inherits from anything with a `call`
     // method. As a result, we can know the callable JS function was created
     // at the first constructor that was hit.
-    if (!isCallable)
-      return new JS.Fun(params, body)
-        ..sourceInformation = body.sourceInformation;
+    if (!isCallable) return new JS.Fun(params, body);
     return js.call(r'''function callableClass(#) {
           if (typeof this !== "function") {
             function self(...args) {
@@ -1457,14 +1470,13 @@
     }
 
     var body = <JS.Statement>[];
-    emitFieldInit(Field f, Expression initializer, [TreeNode sourceInfo]) {
+    emitFieldInit(Field f, Expression initializer, TreeNode hoverInfo) {
       var access = _classProperties.virtualFields[f] ?? _declareMemberName(f);
       var jsInit = _visitInitializer(initializer, f.annotations);
       body.add(jsInit
           .toAssignExpression(js.call('this.#', [access])
-            ..sourceInformation = sourceInfo == null ? f : null)
-          .toStatement()
-            ..sourceInformation = sourceInfo);
+            ..sourceInformation = _nodeStart(hoverInfo))
+          .toStatement());
     }
 
     for (var f in fields) {
@@ -1475,7 +1487,7 @@
           (init == null || _constants.isConstant(init))) {
         continue;
       }
-      emitFieldInit(f, init);
+      emitFieldInit(f, init, f);
     }
 
     // Run constructor field initializers such as `: foo = bar.baz`
@@ -1499,15 +1511,14 @@
     // explicitly initialize to null, to avoid getting `undefined`.
     // TODO(jmesserly): do this only for vars that aren't definitely assigned.
     if (init == null) return new JS.LiteralNull();
-    var value = _annotatedNullCheck(annotations)
+    return _annotatedNullCheck(annotations)
         ? notNull(init)
-        : _visitAndMarkExpression(init);
-    return value..sourceInformation = init;
+        : _visitExpression(init);
   }
 
   JS.Expression notNull(Expression expr) {
     if (expr == null) return null;
-    var jsExpr = _visitAndMarkExpression(expr);
+    var jsExpr = _visitExpression(expr);
     if (!isNullable(expr)) return jsExpr;
     return _callHelper('notNull(#)', jsExpr);
   }
@@ -1636,7 +1647,7 @@
         isGetter: member.isGetter,
         isSetter: member.isSetter,
         isStatic: member.isStatic)
-      ..sourceInformation = member;
+      ..sourceInformation = _nodeEnd(member.fileEndOffset);
   }
 
   JS.Fun _emitNativeFunctionBody(Procedure node) {
@@ -1756,7 +1767,7 @@
         new JS.Fun(_emitFormalParameters(node.function),
             _emitFunctionBody(node.function)),
         isStatic: true)
-      ..sourceInformation = node;
+      ..sourceInformation = _nodeEnd(node.fileEndOffset);
   }
 
   /// Emits an expression that lets you access statics on a [type] from code.
@@ -1905,7 +1916,7 @@
     if (!mocks.containsKey(field.name.name)) {
       var getter = js.call('function() { return this[#]; }', [virtualField]);
       result.add(new JS.Method(name, getter, isGetter: true)
-        ..sourceInformation = field);
+        ..sourceInformation = _nodeStart(field));
     }
 
     if (!mocks.containsKey(field.name.name + '=')) {
@@ -1922,7 +1933,7 @@
       }
 
       result.add(new JS.Method(name, js.call(jsCode, args), isSetter: true)
-        ..sourceInformation = field);
+        ..sourceInformation = _nodeStart(field));
     }
 
     return result;
@@ -2069,6 +2080,8 @@
   }
 
   void _emitTypedef(Typedef t) {
+    var savedUri = _currentUri;
+    _currentUri = t.fileUri;
     var body = _callHelper(
         'typedef(#, () => #)', [js.string(t.name, "'"), _emitType(t.type)]);
 
@@ -2079,6 +2092,8 @@
     } else {
       result = js.statement('# = #;', [_emitTopLevelName(t), body]);
     }
+
+    _currentUri = savedUri;
     _moduleItems.add(result);
   }
 
@@ -2086,6 +2101,8 @@
   // TODO(jmesserly): it'd be nice to avoid this special case.
   JS.Statement _emitInternalSdkFields(Iterable<Field> fields) {
     var lazyFields = <Field>[];
+    var savedUri = _currentUri;
+
     for (var field in fields) {
       // Skip our magic undefined constant.
       if (field.name.name == 'undefined') continue;
@@ -2096,6 +2113,7 @@
           _isInlineJSCall(init) ||
           init is ConstructorInvocation &&
               isSdkInternalRuntime(init.target.enclosingLibrary)) {
+        _currentUri = field.fileUri;
         _moduleItems.add(js.statement('# = #;', [
           _emitTopLevelName(field),
           _visitInitializer(init, field.annotations)
@@ -2104,12 +2122,17 @@
         lazyFields.add(field);
       }
     }
+
+    _currentUri = savedUri;
     return _emitLazyFields(_currentLibrary, lazyFields);
   }
 
   JS.Statement _emitLazyFields(NamedNode target, Iterable<Field> fields) {
     var accessors = <JS.Method>[];
+    var savedUri = _currentUri;
+
     for (var field in fields) {
+      _currentUri = field.fileUri;
       var name = field.name.name;
       var access = _emitStaticMemberName(name);
       accessors.add(new JS.Method(access, _emitStaticFieldInitializer(field),
@@ -2121,10 +2144,12 @@
             isSetter: true));
       }
     }
+    _currentUri = _currentLibrary.fileUri;
 
     var objExpr =
         target is Class ? _emitTopLevelName(target) : emitLibraryName(target);
 
+    _currentUri = savedUri;
     return _callHelperStatement('defineLazy(#, { # });', [objExpr, accessors]);
   }
 
@@ -2434,16 +2459,26 @@
   }
 
   JS.Method _emitLibraryAccessor(Procedure node) {
+    var savedUri = _currentUri;
+    _currentUri = node.fileUri;
+
     var name = node.name.name;
-    return new JS.Method(
+    var result = new JS.Method(
         _propertyName(name), _emitFunction(node.function, node.name.name),
         isGetter: node.isGetter, isSetter: node.isSetter)
-      ..sourceInformation = node;
+      ..sourceInformation = _nodeEnd(node.fileEndOffset);
+
+    _currentUri = savedUri;
+    return result;
   }
 
   JS.Statement _emitLibraryFunction(Procedure p) {
+    var savedUri = _currentUri;
+    _currentUri = p.fileUri;
+
     var body = <JS.Statement>[];
-    var fn = _emitFunction(p.function, p.name.name)..sourceInformation = p;
+    var fn = _emitFunction(p.function, p.name.name)
+      ..sourceInformation = _nodeEnd(p.fileEndOffset);
 
     if (_currentLibrary.importUri.scheme == 'dart' &&
         _isInlineJSFunction(p.function.body)) {
@@ -2458,6 +2493,7 @@
               .toStatement());
     }
 
+    _currentUri = savedUri;
     return JS.Statement.from(body);
   }
 
@@ -2706,7 +2742,8 @@
     JS.Block code = isSync
         ? _emitFunctionBody(f)
         : new JS.Block([
-            _emitGeneratorFunction(f, name).toReturn()..sourceInformation = f
+            _emitGeneratorFunction(f, name).toReturn()
+              ..sourceInformation = _nodeStart(f)
           ]);
 
     if (name != null && formals.isNotEmpty) {
@@ -2734,11 +2771,8 @@
 
   // TODO(jmesserly): rename _emitParameters
   List<JS.Parameter> _emitFormalParameters(FunctionNode f) {
-    var result =
-        f.positionalParameters.map((p) => new JS.Identifier(p.name)).toList();
-    if (f.namedParameters.isNotEmpty) {
-      result.add(namedArgumentTemp);
-    }
+    var result = f.positionalParameters.map(_emitVariableDef).toList();
+    if (f.namedParameters.isNotEmpty) result.add(namedArgumentTemp);
     return result;
   }
 
@@ -2773,7 +2807,7 @@
         //
         // TODO(jmesserly): this will emit argument initializers (for default
         // values) inside the generator function body. Is that the best place?
-        var jsBody = _emitFunctionBody(function)..sourceInformation = function;
+        var jsBody = _emitFunctionBody(function);
         gen = new JS.Fun(getParameters(jsBody), jsBody, isGenerator: true);
 
         // Name the function if possible, to get better stack traces.
@@ -2781,6 +2815,8 @@
           name = JS.friendlyNameForDartOperator[name] ?? name;
           gen = new JS.NamedFunction(new JS.TemporaryId(name), gen);
         }
+
+        gen.sourceInformation = _nodeEnd(function.fileEndOffset);
         if (JS.This.foundIn(gen)) gen = js.call('#.bind(this)', gen);
       });
 
@@ -2806,7 +2842,6 @@
       //
       // In the future, we might be able to simplify this, see:
       // https://github.com/dart-lang/sdk/issues/28320
-
       var jsParams = _emitFormalParameters(function);
       var gen = emitGeneratorFn((fnBody) => jsParams =
           jsParams.where(JS.findMutatedVariables(fnBody).contains).toList());
@@ -2831,8 +2866,7 @@
       var gen = emitGeneratorFn((_) => [_asyncStarController]);
 
       var returnType = _getExpectedReturnType(function, coreTypes.streamClass);
-      return _callHelper('asyncStar(#, #)', [_emitType(returnType), gen])
-        ..sourceInformation = function;
+      return _callHelper('asyncStar(#, #)', [_emitType(returnType), gen]);
     }
 
     assert(function.asyncMarker == AsyncMarker.Async);
@@ -2851,8 +2885,7 @@
     // declared return type.
     var returnType = types.unfutureType(function.functionType.returnType);
     return js.call('#.async(#, #)',
-        [emitLibraryName(coreTypes.asyncLibrary), _emitType(returnType), gen])
-      ..sourceInformation = function;
+        [emitLibraryName(coreTypes.asyncLibrary), _emitType(returnType), gen]);
   }
 
   /// Gets the expected return type of a `sync*` or `async*` body.
@@ -2929,7 +2962,7 @@
     for (var p in f.namedParameters) {
       // Parameters will be passed using their real names, not the (possibly
       // renamed) local variable.
-      var jsParam = new JS.Identifier(p.name);
+      var jsParam = _emitVariableDef(p);
       var paramName = js.string(p.name, "'");
       var defaultValue = _defaultParamValue(p);
       if (defaultValue != null) {
@@ -2942,16 +2975,14 @@
           namedArgumentTemp,
           paramName,
           defaultValue,
-        ])
-          ..sourceInformation = p);
+        ]));
       } else {
         body.add(js.statement('let # = # && #.#;', [
           jsParam,
           namedArgumentTemp,
           namedArgumentTemp,
           paramName,
-        ])
-          ..sourceInformation = p);
+        ]));
       }
       initParameter(p, jsParam);
     }
@@ -3021,14 +3052,14 @@
   }
 
   JS.Statement _visitStatement(Statement s) {
-    var result = s?.accept(this);
-    if (result != null) {
-      result.sourceInformation = s;
+    if (s == null) return null;
+    var result = s.accept(this);
+    // TODO(jmesserly): is the `is! Block` still necessary?
+    if (s is! Block) result.sourceInformation = _nodeStart(s);
 
-      // The statement might be the target of a break or continue with a label.
-      var name = _labelNames[s];
-      if (name != null) result = new JS.LabeledStatement(name, result);
-    }
+    // The statement might be the target of a break or continue with a label.
+    var name = _labelNames[s];
+    if (name != null) result = new JS.LabeledStatement(name, result);
     return result;
   }
 
@@ -3053,7 +3084,7 @@
 
   /// Visits [nodes] with [_visitExpression].
   List<JS.Expression> _visitExpressionList(Iterable<Expression> nodes) {
-    return nodes?.map(_visitAndMarkExpression)?.toList();
+    return nodes?.map(_visitExpression)?.toList();
   }
 
   /// Generates an expression for a boolean conversion context (if, while, &&,
@@ -3064,20 +3095,14 @@
   JS.Expression _visitTest(Expression node) {
     if (node == null) return null;
 
-    JS.Expression finish(JS.Expression result) {
-      result?.sourceInformation = node;
-      return result;
-    }
-
     if (node is Not) {
       // TODO(leafp): consider a peephole opt for identical
       // and == here.
-      return finish(js.call('!#', _visitTest(node.operand)));
+      return js.call('!#', _visitTest(node.operand));
     }
     if (node is LogicalExpression) {
       JS.Expression shortCircuit(String code) {
-        return finish(
-            js.call(code, [_visitTest(node.left), _visitTest(node.right)]));
+        return js.call(code, [_visitTest(node.left), _visitTest(node.right)]);
       }
 
       var op = node.operator;
@@ -3085,25 +3110,60 @@
       if (op == '||') return shortCircuit('# || #');
     }
 
-    var result = _visitAndMarkExpression(node);
+    var result = _visitExpression(node);
     if (node.getStaticType(types) != coreTypes.boolClass.rawType) {
-      return finish(_callHelper('dtest(#)', result));
+      return _callHelper('dtest(#)', result);
     }
     if (isNullable(node)) result = _callHelper('test(#)', result);
-    return finish(result);
-  }
-
-  JS.Expression _visitExpression(e) {
-    JS.Expression result = e?.accept(this);
     return result;
   }
 
-  JS.Expression _visitAndMarkExpression(Expression e) {
-    JS.Expression result = e?.accept(this);
-    if (result != null) result.sourceInformation = e;
+  JS.Expression _visitExpression(Expression e) {
+    if (e == null) return null;
+    var result = e.accept(this) as JS.Expression;
+    result.sourceInformation ??= _nodeStart(e);
     return result;
   }
 
+  /// Gets the start position of [node] for use in source mapping.
+  ///
+  /// This is the most common kind of marking, and is used for most expressions
+  /// and statements.
+  SourceLocation _nodeStart(TreeNode node) => _getLocation(node.fileOffset);
+
+  /// Gets the end position of [node] for use in source mapping.
+  ///
+  /// This is mainly used for things that compile to JS functions. JS wants a
+  /// marking on the end of all functions for stepping purposes.
+  ///
+  /// This can be used to complete a hover span, when we know the start position
+  /// has already been emitted. For example, `foo.bar` we only need to mark the
+  /// end of `.bar` to ensure `foo.bar` has a hover tooltip.
+  NodeEnd _nodeEnd(int endOffset) {
+    var loc = _getLocation(endOffset);
+    return loc != null ? new NodeEnd(loc) : null;
+  }
+
+  /// Combines [_nodeStart] with the variable name length to produce a hoverable
+  /// span for the varaible.
+  //
+  // TODO(jmesserly): we need a lot more nodes to support hover.
+  NodeSpan _variableSpan(int offset, int nameLength) {
+    var start = _getLocation(offset);
+    var end = _getLocation(offset + nameLength);
+    return start != null && end != null ? new NodeSpan(start, end) : null;
+  }
+
+  SourceLocation _getLocation(int offset) {
+    if (offset == -1) return null;
+    var fileUri = _currentUri;
+    if (fileUri == null) return null;
+    var loc = _program.getLocation(fileUri, offset);
+    if (loc == null) return null;
+    return new SourceLocation(offset,
+        sourceUrl: fileUri, line: loc.line - 1, column: loc.column - 1);
+  }
+
   @override
   defaultStatement(Statement node) => _emitInvalidNode(node).toStatement();
 
@@ -3114,7 +3174,7 @@
       var inlineJS = _emitInlineJSCode(expr);
       return inlineJS is JS.Expression ? inlineJS.toStatement() : inlineJS;
     }
-    return _visitAndMarkExpression(expr).toStatement();
+    return _visitExpression(expr).toStatement();
   }
 
   @override
@@ -3283,8 +3343,7 @@
   JS.For visitForStatement(ForStatement node) {
     return translateLoop(node, () {
       emitForInitializer(VariableDeclaration v) =>
-          new JS.VariableInitialization(
-              _emitVariableRef(v)..sourceInformation = v,
+          new JS.VariableInitialization(_emitVariableDef(v),
               _visitInitializer(v.initializer, v.annotations));
 
       var init = node.variables.map(emitForInitializer).toList();
@@ -3294,7 +3353,7 @@
       JS.Expression update;
       if (updates.isNotEmpty) {
         update = new JS.Expression.binary(
-                updates.map(_visitAndMarkExpression).toList(), ',')
+                updates.map(_visitExpression).toList(), ',')
             .toVoidExpression();
       }
       var condition = _visitTest(node.condition);
@@ -3311,13 +3370,13 @@
         return _emitAwaitFor(node);
       }
 
-      var iterable = _visitAndMarkExpression(node.iterable);
+      var iterable = _visitExpression(node.iterable);
       var body = _visitScope(effectiveBodyOf(node, node.body));
 
-      var v = _emitVariableRef(node.variable);
-      var init = js.call('let #', v)..sourceInformation = node.variable;
+      var init = js.call('let #', _emitVariableDef(node.variable));
       if (_annotatedNullCheck(node.variable.annotations)) {
-        body = new JS.Block([_nullParameterCheck(v), body]);
+        body = new JS.Block(
+            [_nullParameterCheck(_emitVariableRef(node.variable)), body]);
       }
 
       return new JS.ForOf(init, iterable, body);
@@ -3348,28 +3407,26 @@
             streamIterator,
             _asyncStreamIteratorClass.procedures
                 .firstWhere((p) => p.isFactory && p.name.name == '')),
-        [_visitExpression(node.iterable)])
-      ..sourceInformation = node.iterable;
+        [_visitExpression(node.iterable)]);
 
     var iter = new JS.TemporaryId('iter');
-    var init =
-        js.call('let # = #.current', [_emitVariableRef(node.variable), iter]);
     return js.statement(
         '{'
         '  let # = #;'
         '  try {'
-        '    while (#) { #; #; }'
+        '    while (#) { let # = #.current; #; }'
         '  } finally { #; }'
         '}',
         [
           iter,
           createStreamIter,
           new JS.Yield(js.call('#.moveNext()', iter))
-            ..sourceInformation = node.variable,
-          init,
+            ..sourceInformation = _nodeStart(node.variable),
+          _emitVariableDef(node.variable),
+          iter,
           _visitStatement(node.body),
           new JS.Yield(js.call('#.cancel()', iter))
-            ..sourceInformation = node.variable
+            ..sourceInformation = _nodeStart(node.variable)
         ]);
   }
 
@@ -3384,13 +3441,13 @@
       var last =
           expressions.isNotEmpty && !c.isDefault ? expressions.last : null;
       for (var e in expressions) {
-        var jsExpr = _visitAndMarkExpression(e);
+        var jsExpr = _visitExpression(e);
         cases.add(new JS.Case(jsExpr, e == last ? body : emptyBlock));
       }
       if (c.isDefault) cases.add(new JS.Default(body));
     }
 
-    return new JS.Switch(_visitAndMarkExpression(node.expression), cases);
+    return new JS.Switch(_visitExpression(node.expression), cases);
   }
 
   @override
@@ -3443,7 +3500,7 @@
   JS.Statement visitReturnStatement(ReturnStatement node) {
     var e = node.expression;
     if (e == null) return new JS.Return();
-    return _visitAndMarkExpression(e).toReturn();
+    return _visitExpression(e).toReturn();
   }
 
   @override
@@ -3487,19 +3544,17 @@
       } else if (name != null) {
         vars.add(name.name);
         body.add(js.statement('let # = #;',
-            [_emitVariableRef(name), _emitVariableRef(_catchParameter)])
-          ..sourceInformation = name);
+            [_emitVariableDef(name), _emitVariableRef(_catchParameter)]));
         _catchParameter = name;
       }
       var stackTrace = node.stackTrace;
       if (stackTrace != null) {
         vars.add(stackTrace.name);
         body.add(js.statement('let # = #.stackTrace(#);', [
-          _emitVariableRef(stackTrace),
+          _emitVariableDef(stackTrace),
           _runtimeModule,
           _emitVariableRef(name)
-        ])
-          ..sourceInformation = stackTrace);
+        ]));
       }
     }
 
@@ -3516,7 +3571,7 @@
             [_emitType(node.guard), _emitVariableRef(_catchParameter)]),
         then,
         otherwise)
-      ..sourceInformation = node;
+      ..sourceInformation = _nodeStart(node);
   }
 
   @override
@@ -3536,7 +3591,7 @@
 
   @override
   visitYieldStatement(YieldStatement node) {
-    var jsExpr = _visitAndMarkExpression(node.expression);
+    var jsExpr = _visitExpression(node.expression);
     var star = node.isYieldStar;
     if (_asyncStarController != null) {
       // async* yields are generated differently from sync* yields. `yield e`
@@ -3554,7 +3609,7 @@
         _asyncStarController,
         helperName,
         jsExpr,
-        new JS.Yield(null)..sourceInformation = node
+        new JS.Yield(null)..sourceInformation = _nodeStart(node)
       ]);
     }
     // A normal yield in a sync*
@@ -3565,7 +3620,7 @@
   visitVariableDeclaration(VariableDeclaration node) {
     // TODO(jmesserly): casts are sometimes required here.
     // Kernel does not represent these explicitly.
-    var v = _emitVariableRef(node)..sourceInformation = node;
+    var v = _emitVariableDef(node);
     return js.statement('let # = #;',
         [v, _visitInitializer(node.initializer, node.annotations)]);
   }
@@ -3575,8 +3630,7 @@
     var func = node.function;
     var fn = _emitFunction(func, node.variable.name);
 
-    var name = new JS.Identifier(node.variable.name)
-      ..sourceInformation = node.variable;
+    var name = _emitVariableDef(node.variable);
     JS.Statement declareFn;
     if (JS.This.foundIn(fn)) {
       declareFn = js.statement('const # = #.bind(this);', [name, fn]);
@@ -3586,10 +3640,11 @@
     if (_reifyFunctionType(func)) {
       declareFn = new JS.Block([
         declareFn,
-        _emitFunctionTagged(name, func.functionType).toStatement()
+        _emitFunctionTagged(_emitVariableRef(node.variable), func.functionType)
+            .toStatement()
       ]);
     }
-    return declareFn..sourceInformation = node;
+    return declareFn;
   }
 
   @override
@@ -3607,7 +3662,14 @@
   visitConstantExpression(ConstantExpression node) => defaultExpression(node);
 
   @override
-  visitVariableGet(VariableGet node) => _emitVariableRef(node.variable);
+  visitVariableGet(VariableGet node) {
+    var v = node.variable;
+    var id = _emitVariableRef(v);
+    if (id.name == v.name) {
+      id.sourceInformation = _variableSpan(node.fileOffset, v.name.length);
+    }
+    return id;
+  }
 
   JS.Identifier _emitVariableRef(VariableDeclaration v) {
     var name = v.name;
@@ -3618,6 +3680,14 @@
     return new JS.Identifier(name);
   }
 
+  /// Emits the declaration of a variable.
+  ///
+  /// This is similar to [_emitVariableRef] but it also attaches source
+  /// location information, so hover will work as expected.
+  JS.Identifier _emitVariableDef(VariableDeclaration v) {
+    return _emitVariableRef(v)..sourceInformation = _nodeStart(v);
+  }
+
   JS.Statement _initLetVariables() {
     if (_letVariables.isEmpty) return null;
     var result = new JS.VariableDeclarationList(
@@ -3632,7 +3702,7 @@
 
   // TODO(jmesserly): resugar operators for kernel, such as ++x, x++, x+=.
   @override
-  visitVariableSet(VariableSet node) => _visitAndMarkExpression(node.value)
+  visitVariableSet(VariableSet node) => _visitExpression(node.value)
       .toAssignExpression(_emitVariableRef(node.variable));
 
   @override
@@ -3644,14 +3714,12 @@
   @override
   visitPropertySet(PropertySet node) {
     return _emitPropertySet(
-        node.receiver, node.interfaceTarget, node.value, node.name.name)
-      ..sourceInformation = node;
+        node.receiver, node.interfaceTarget, node.value, node.name.name);
   }
 
   @override
   visitDirectPropertyGet(DirectPropertyGet node) {
-    return _emitPropertyGet(node.receiver, node.target)
-      ..sourceInformation = node;
+    return _emitPropertyGet(node.receiver, node.target);
   }
 
   @override
@@ -3665,6 +3733,9 @@
         type: receiver.getStaticType(types), member: member);
     var jsReceiver = _visitExpression(receiver);
 
+    // TODO(jmesserly): we need to mark an end span for property accessors so
+    // they can be hovered. Unfortunately this is not possible as Kernel does
+    // not store this data.
     if (member == null) {
       return _callHelper(
           '#(#, #)', [_emitDynamicOperationName('dload'), jsReceiver, jsName]);
@@ -3692,7 +3763,7 @@
         type: receiver.getStaticType(types), member: member);
 
     var jsReceiver = _visitExpression(receiver);
-    var jsValue = _visitAndMarkExpression(value);
+    var jsValue = _visitExpression(value);
 
     if (member == null) {
       return _callHelper('#(#, #, #)',
@@ -3722,12 +3793,12 @@
 
   @override
   visitStaticGet(StaticGet node) {
-    return _emitStaticTarget(node.target)..sourceInformation = node;
+    return _emitStaticTarget(node.target);
   }
 
   @override
   visitStaticSet(StaticSet node) {
-    return _visitAndMarkExpression(node.value)
+    return _visitExpression(node.value)
         .toAssignExpression(_emitStaticTarget(node.target));
   }
 
@@ -3756,7 +3827,7 @@
       }
     }
 
-    var jsReceiver = _visitAndMarkExpression(receiver);
+    var jsReceiver = _visitExpression(receiver);
     var args = _emitArgumentList(arguments);
     var receiverType = receiver.getStaticType(types);
     var typeArgs = arguments.types;
@@ -4087,8 +4158,8 @@
     // a measurable performance effect (possibly the helper is simple enough to
     // be inlined).
     if (isNullable(left)) {
-      return _callHelper('equals(#, #)',
-          [_visitAndMarkExpression(left), _visitAndMarkExpression(right)]);
+      return _callHelper(
+          'equals(#, #)', [_visitExpression(left), _visitExpression(right)]);
     }
 
     // Otherwise we emit a call to the == method.
@@ -4118,7 +4189,7 @@
             [_visitExpression(receiver), _visitExpressionList(args)]);
       } else {
         return _callHelper('dsend(#, #, #)', [
-          _visitAndMarkExpression(receiver),
+          _visitExpression(receiver),
           memberName,
           _visitExpressionList(args)
         ]);
@@ -4230,13 +4301,13 @@
         args.add(new JS.RestParameter(
             _visitExpression(arg.arguments.positional[0])));
       } else {
-        args.add(_visitAndMarkExpression(arg));
+        args.add(_visitExpression(arg));
       }
     }
     var named = <JS.Property>[];
     for (var arg in node.named) {
       named.add(new JS.Property(
-          _propertyName(arg.name), _visitAndMarkExpression(arg.value)));
+          _propertyName(arg.name), _visitExpression(arg.value)));
     }
     if (named.isNotEmpty) {
       args.add(new JS.ObjectInitializer(named));
@@ -4384,10 +4455,7 @@
     }
     var left = args[0];
     var right = args[1];
-    var jsArgs = [
-      _visitAndMarkExpression(left),
-      _visitAndMarkExpression(right)
-    ];
+    var jsArgs = [_visitExpression(left), _visitExpression(right)];
     if (_tripleEqIsIdentity(left, right)) {
       return _emitJSTripleEq(jsArgs, negated: negated);
     }
@@ -4544,8 +4612,8 @@
   visitConditionalExpression(ConditionalExpression node) {
     return js.call('# ? # : #', [
       _visitTest(node.condition),
-      _visitAndMarkExpression(node.then),
-      _visitAndMarkExpression(node.otherwise)
+      _visitExpression(node.then),
+      _visitExpression(node.otherwise)
     ]);
   }
 
@@ -4575,7 +4643,7 @@
         expectString = false;
       } else {
         if (expectString) strings.add('');
-        interpolations.add(_visitAndMarkExpression(e));
+        interpolations.add(_visitExpression(e));
         expectString = true;
       }
     }
@@ -4589,7 +4657,7 @@
     // Generate `is` as `dart.is` or `typeof` depending on the RHS type.
     JS.Expression result;
     var type = node.type;
-    var lhs = _visitAndMarkExpression(node.operand);
+    var lhs = _visitExpression(node.operand);
     var typeofName = _jsTypeofName(type);
     // Inline primitives other than int (which requires a Math.floor check).
     if (typeofName != null && type != coreTypes.intClass.rawType) {
@@ -4614,7 +4682,7 @@
   visitAsExpression(AsExpression node) {
     Expression fromExpr = node.operand;
     var to = node.type;
-    var jsFrom = _visitAndMarkExpression(fromExpr);
+    var jsFrom = _visitExpression(fromExpr);
     var from = fromExpr.getStaticType(types);
 
     // If the check was put here by static analysis to ensure soundness, we
@@ -4718,7 +4786,7 @@
 
   @override
   visitThrow(Throw node) =>
-      _callHelper('throw(#)', _visitAndMarkExpression(node.expression));
+      _callHelper('throw(#)', _visitExpression(node.expression));
 
   @override
   visitListLiteral(ListLiteral node) {
@@ -4755,8 +4823,8 @@
     emitEntries() {
       var entries = <JS.Expression>[];
       for (var e in node.entries) {
-        entries.add(_visitAndMarkExpression(e.key));
-        entries.add(_visitAndMarkExpression(e.value));
+        entries.add(_visitExpression(e.key));
+        entries.add(_visitExpression(e.value));
       }
       return new JS.ArrayInitializer(entries);
     }
@@ -4784,11 +4852,7 @@
   }
 
   JS.ArrowFun _emitArrowFunction(FunctionExpression node) {
-    JS.Fun fn = _emitFunction(node.function, null)..sourceInformation = node;
-    return _toArrowFunction(fn);
-  }
-
-  JS.ArrowFun _toArrowFunction(JS.Fun f) {
+    JS.Fun f = _emitFunction(node.function, null);
     JS.Node body = f.body;
 
     // Simplify `=> { return e; }` to `=> e`
@@ -4803,8 +4867,7 @@
     // Convert `function(...) { ... }` to `(...) => ...`
     // This is for readability, but it also ensures correct `this` binding.
     return new JS.ArrowFun(f.params, body,
-        typeParams: f.typeParams, returnType: f.returnType)
-      ..sourceInformation = f.sourceInformation;
+        typeParams: f.typeParams, returnType: f.returnType);
   }
 
   @override
diff --git a/pkg/dev_compiler/lib/src/kernel/source_map_printer.dart b/pkg/dev_compiler/lib/src/kernel/source_map_printer.dart
deleted file mode 100644
index 6e47bd9..0000000
--- a/pkg/dev_compiler/lib/src/kernel/source_map_printer.dart
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:source_maps/source_maps.dart' hide Printer;
-import 'package:source_span/source_span.dart' show SourceLocation;
-import 'package:kernel/kernel.dart';
-
-import '../js_ast/js_ast.dart' as JS;
-
-class SourceMapPrintingContext extends JS.SimpleJavaScriptPrintingContext {
-  /// Current line in the buffer.
-  int _line = 0;
-
-  /// Current column in the buffer.
-  int _column = 0;
-
-  /// The source_maps builder we write JavaScript code to.
-  final sourceMap = new SourceMapBuilder();
-
-  /// The last marked line in the buffer.
-  int _previousLine = -1;
-
-  /// The last marked column in the buffer.
-  int _previousColumn = -1;
-
-  List<FileUriNode> parentsStack = [];
-
-  @override
-  void emit(String code) {
-    var chars = code.runes.toList();
-    var length = chars.length;
-    for (int i = 0; i < length; i++) {
-      var c = chars[i];
-      if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) {
-        // Return not followed by line-feed is treated as a new line.
-        _line++;
-        _column = 0;
-      } else {
-        _column++;
-      }
-    }
-    super.emit(code);
-  }
-
-  void enterNode(JS.Node jsNode) {
-    var srcInfo = jsNode.sourceInformation;
-    if (srcInfo == null) return;
-
-    int offset;
-    bool mark = true;
-    if (srcInfo is TreeNode) {
-      offset = srcInfo.fileOffset;
-
-      if (srcInfo is FileUriNode) {
-        parentsStack.add(srcInfo);
-        if (srcInfo is Procedure ||
-            srcInfo is Class ||
-            srcInfo is Constructor) {
-          mark = false;
-        }
-      }
-      if (mark && srcInfo is Block) mark = false;
-    } else {
-      throw "Unexpected source information: ${srcInfo.runtimeType}";
-    }
-
-    if (offset == -1 || !mark) return;
-
-    _mark(offset, false);
-  }
-
-  void exitNode(JS.Node jsNode) {
-    var srcInfo = jsNode.sourceInformation;
-    if (srcInfo == null) return;
-
-    int offset = -1;
-    if (srcInfo is Member) {
-      offset = srcInfo.fileEndOffset;
-    } else if (srcInfo is FunctionNode) {
-      offset = srcInfo.fileEndOffset;
-    } else if (srcInfo is Class) {
-      offset = srcInfo.fileEndOffset;
-    }
-    if (offset == -1 && srcInfo is Constructor) {
-      // Probably default constructor that the user didn't write. Point to the
-      // end brace on the class instead.
-      Class parent = srcInfo.parent;
-      offset = parent.fileEndOffset;
-    }
-
-    // Any ending brace or semicolon is already in the output.
-    // Adjust column accordingly.
-    if (offset != -1) _mark(offset, true);
-
-    if (srcInfo is FileUriNode) {
-      parentsStack.removeLast();
-    }
-  }
-
-  void _mark(int offset, bool adjustColumn) {
-    int adjustedColumn = _column - (adjustColumn ? 1 : 0);
-    if (adjustedColumn < 0) return;
-    if ((_previousColumn == _column || _previousColumn == adjustedColumn) &&
-        _previousLine == _line) return;
-
-    if (parentsStack.isEmpty) {
-      // TODO(jensj)
-      return;
-    }
-
-    FileUriNode fileParent = parentsStack.last;
-    Program p = fileParent.enclosingProgram;
-    Uri fileUri = fileParent.fileUri;
-    while (fileUri == null && fileParent.parent is FileUriNode) {
-      fileParent = fileParent.parent;
-      fileUri = fileParent.fileUri;
-    }
-    if (fileUri == null) return;
-
-    var loc = p.getLocation(fileUri, offset);
-    if (loc == null) return;
-    _previousLine = _line;
-    _previousColumn = adjustedColumn;
-    sourceMap.addLocation(
-        new SourceLocation(offset,
-            sourceUrl: fileUri, line: loc.line - 1, column: loc.column - 1),
-        new SourceLocation(buffer.length - (adjustColumn ? 1 : 0),
-            line: _line, column: adjustedColumn),
-        null);
-  }
-}
-
-const int _LF = 10;
-const int _CR = 13;
diff --git a/pkg/dev_compiler/test/sourcemap/ddc_common.dart b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
index c609b5e..03bf73c 100644
--- a/pkg/dev_compiler/test/sourcemap/ddc_common.dart
+++ b/pkg/dev_compiler/test/sourcemap/ddc_common.dart
@@ -65,7 +65,8 @@
         jsPreambles: _getPreambles,
         useJsMethodNamesOnAbsence: true,
         jsNameConverter: _convertName,
-        forcedTmpDir: data.outDir);
+        forcedTmpDir: data.outDir,
+        verbose: true);
     return pass(data);
   }
 
diff --git a/pkg/dev_compiler/test/sourcemap/sourcemaps_ddc.status b/pkg/dev_compiler/test/sourcemap/sourcemaps_ddc.status
index 3744224..3642988 100644
--- a/pkg/dev_compiler/test/sourcemap/sourcemaps_ddc.status
+++ b/pkg/dev_compiler/test/sourcemap/sourcemaps_ddc.status
@@ -1,27 +1,3 @@
 # Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE.md file.
-
-/hello_sane_column_on_print_return_value: Crash
-/printing_class_fields_step_into: Crash
-/stops_at_ending_brace: Crash
-/no_mapping_on_class_constructor_line: Crash
-/no_mapping_on_class_named_constructor_line: Crash
-/hello_class_call: Crash
-/method_call_with_named_parameters: Crash
-/method_call_with_named_parameters_no_given: Crash
-/call_on_field_in_class: Crash
-/next_through_assign_int_test: Crash
-/next_through_catch_test: Crash
-/next_through_for_each_loop_test: Crash
-/next_through_operator_bracket_on_this_test: Crash
-/next_through_operator_bracket_test: Crash
-/hello_async: Crash
-/next_through_is_and_as_test: Crash
-/next_through_multi_catch_test: Crash
-/step_through_async_star_yield: Crash
-/step_through_await_for: Crash
-/step_through_conditional_expression: Crash
-/step_through_if_and_identical: Crash
-/step_through_property_get_test: Crash
-/step_through_sync_star: Crash
diff --git a/pkg/dev_compiler/test/sourcemap/stacktrace_ddc.status b/pkg/dev_compiler/test/sourcemap/stacktrace_ddc.status
index daa873c..3c3311b 100644
--- a/pkg/dev_compiler/test/sourcemap/stacktrace_ddc.status
+++ b/pkg/dev_compiler/test/sourcemap/stacktrace_ddc.status
@@ -3,9 +3,9 @@
 # BSD-style license that can be found in the LICENSE.md file.
 
 /null_interceptor_field: Crash
-/throw_in_constructor: Crash
 /throw_in_constructor_from_async: Crash
 /throw_in_instance_method: Crash
+/throw_in_static_method: Crash # Test works, but DDC's hover support means the expected column in the test is wrong
 
 /throw_in_async: Crash # dartbug.com/31451
 /throw_in_awaited_async: Crash # dartbug.com/31451
diff --git a/pkg/dev_compiler/test/sourcemap/stacktrace_ddk.status b/pkg/dev_compiler/test/sourcemap/stacktrace_ddk.status
index c16467e..c817e53 100644
--- a/pkg/dev_compiler/test/sourcemap/stacktrace_ddk.status
+++ b/pkg/dev_compiler/test/sourcemap/stacktrace_ddk.status
@@ -1,8 +1,9 @@
 # Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE.md file.
-
 /throw_in_async: Crash # dartbug.com/31451
 /throw_in_awaited_async: Crash # dartbug.com/31451
 /throw_in_constructor_from_async: Crash # dartbug.com/31451
+/throw_in_instance_method: Crash # Test works, but DDC's hover support means the expected column in the test is wrong
 /throw_in_top_level_method_from_async: Crash # dartbug.com/31451
+
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/call_on_field_in_class.dart b/pkg/dev_compiler/test/sourcemap/testfiles/call_on_field_in_class.dart
index 9ca2539..a6b1f9e 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/call_on_field_in_class.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/call_on_field_in_class.dart
@@ -4,15 +4,15 @@
 
 main() {
   /* bl */ var foo = new /*s:1*/ Foo();
-  foo.foo = foo. /*s:2*/ fooMethod;
-  foo. /*s:3*/ fooMethod();
+  foo.foo = foo. /*sl:2*/ fooMethod;
+  foo /*sl:3*/ .fooMethod();
   // Stepping into this doesn't really work because what it does is something
   // like this:
   // main -> dart.dsend -> dart.callMethod -> get foo ->
   // (back in dart.callMethod) -> dart._checkAndCall -> fooMethod
   // which seems unlikely to be something the user is going to step through.
   // As a "fix" here a breakpoint has been set on the line in fooMethod.
-  foo. /*s:5*/ foo();
+  foo. /*sl:5*/ foo();
 }
 
 class Foo {
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/hello_world.dart b/pkg/dev_compiler/test/sourcemap/testfiles/hello_world.dart
index 10648d7..59746a1 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/hello_world.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/hello_world.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 main() {
-  /*bl*/
-  /*s:1*/ print("Hello, World!");
+  /*nb*/ // no break on empty line
+  /*bl*/ print("Hello, World!");
+/*s:1*/
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_is_and_as_test.dart b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_is_and_as_test.dart
index 6dfaf13..d3b49d1 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_is_and_as_test.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_is_and_as_test.dart
@@ -8,26 +8,26 @@
   /*bl*/
   /*sl:1*/ var i = 42.42;
   /*sl:2*/ var hex = 0x42;
-  if (/*bc:3*/ foo() /*bc:4*/ is int) {
+  /*bc:3*/ if (/*bc:4*/ foo() is int) {
     /*bc:5*/ print("foo is int");
   }
-  if (i /*bc:6*/ is int) {
+  /*bc:6*/ if (i is int) {
     print("i is int");
   }
-  if (i /*bc:7*/ is! int) {
+  /*bc:7*/ if (i is! int) {
     /*bc:8*/ print("i is not int");
   }
-  if (hex /*bc:9*/ is int) {
+  /*bc:9*/ if (hex is int) {
     /*bc:10*/ print("hex is int");
     // ignore: unnecessary_cast
-    int x = hex /*bc:11*/ as int;
+    int x = /*bc:11*/ hex as int;
     /*bc:12*/ if (x.isEven) {
       /*bc:13*/ print("it's even even!");
     } else {
       print("but it's not even even!");
     }
   }
-  if (hex /*bc:14*/ is! int) {
+  /*bc:14*/ if (hex is! int) {
     print("hex is not int");
   }
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_on_this_test.dart b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_on_this_test.dart
index 83db815..4dc4f138 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_on_this_test.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_on_this_test.dart
@@ -8,8 +8,8 @@
   operator [](index) => index;
 
   code() {
-    /*bl*/ this /*s:1*/ [42];
-    return this /*sl:2*/ [42];
+    /*bl*/ /*sl:1*/ this[42]; // DDK fails to hover on `this`
+    return /*sl:2*/ this[42];
   }
 }
 
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_test.dart b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_test.dart
index 3f96857..5bf49df 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_test.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/next_through_operator_bracket_test.dart
@@ -15,6 +15,6 @@
 
 main() {
   /*bl*/ /*sl:1*/ Class2 c = new Class2();
-  c /*s:2*/ [42];
-  c. /*s:3*/ code();
+  c /*sl:2*/ [42];
+  c /*sl:3*/ .code();
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/printing_class_fields_step_into.dart b/pkg/dev_compiler/test/sourcemap/testfiles/printing_class_fields_step_into.dart
index 0fd7de3..f450855 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/printing_class_fields_step_into.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/printing_class_fields_step_into.dart
@@ -5,18 +5,19 @@
 /*nb*/
 main() {
   /*bl*/
-  Foo foo = new /*bc:1*/ Foo(1, 2);
-  /*bc:5*/ print(foo.x);
-  /*bc:6*/ print(foo.y);
-  /*bc:7*/ print(foo.z);
+  Foo foo = new /*s:1*/ Foo(1, 2);
+  /*s:5*/ print(foo.x);
+  /*s:6*/ print(foo.y);
+  /*s:7*/ print(foo.z);
 }
 
 class Foo {
   var x, y, z;
 
   Foo(a, b)
-      : this.x /*bc:2*/ = a,
-        this.y /*bc:3*/ = b {
-    z = a /*bc:4*/ + b;
+      : this. /*sl:2*/ x = a, // `s:2` fails, DDK is missing hover info
+        this. /*sl:3*/ y = b {
+    // `s:3` fails, DDK is missing hover info
+    z = a /*sl:4*/ + b;
   }
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_await_for.dart b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_await_for.dart
index 25bb182..e2021cf 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_await_for.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_await_for.dart
@@ -5,7 +5,7 @@
 main() async {
   /* bl */
   /*sl:1 */ print("About to loop!");
-  await for (var /*s:3*/ /*s:5*/ i in /*s:2*/ foobar()) {
+  await for (var /*s:3*/ /*s:5*/ i in foobar /*sl:2*/ ()) {
     /*s:4*/ /*s:6*/ /*nbb:6:7*/ print(i);
   }
   /*s:7*/ /*nbb:7:8*/ print("Done!");
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_if_and_identical.dart b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_if_and_identical.dart
index 5a04d5a..0b81376 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_if_and_identical.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_if_and_identical.dart
@@ -5,10 +5,10 @@
 /*Debugger:stepOver*/
 
 main() {
-  if (/*bc:1*/ foo() /*bc:3*/ == /*bc:2*/ bar()) {
+  if (/*bc:1*/ foo() == /*bc:2*/ bar()) {
     print("wat?!?");
   }
-  if (identical(/*bc:4*/ foo(), /*bc:5*/ bar())) {
+  if (identical(/*bc:3*/ foo(), /*bc:4*/ bar())) {
     print("wat?!?");
   }
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_property_get_test.dart b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_property_get_test.dart
index f24c928..20ad5ea 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/step_through_property_get_test.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/step_through_property_get_test.dart
@@ -29,12 +29,12 @@
   void doStuff() {
     /* bl */
     /*s:1*/ print(data2);
-    /*s:3*/ print(data2 /*s:5*/ [1]);
+    /*s:3*/ print(data2 /*sl:5*/ [1]);
 
     /*s:6*/ print(data1);
-    /*s:8*/ print(data1 /*s:10*/ [1]);
+    /*s:8*/ print(data1 /*sl:10*/ [1]);
 
     /*s:11*/ print(super.data1);
-    /*s:13*/ print(super.data1 /*s:15*/ [1]);
+    /*s:13*/ print(super.data1 /*sl:15*/ [1]);
   }
 }
diff --git a/pkg/dev_compiler/test/sourcemap/testfiles/stops_at_ending_brace.dart b/pkg/dev_compiler/test/sourcemap/testfiles/stops_at_ending_brace.dart
index 54e7c16..da061d2 100644
--- a/pkg/dev_compiler/test/sourcemap/testfiles/stops_at_ending_brace.dart
+++ b/pkg/dev_compiler/test/sourcemap/testfiles/stops_at_ending_brace.dart
@@ -5,13 +5,13 @@
 main() {
   new Foo();
   // Comment to push the ending brace back a bit.
-/*s:3*/
+/*s:2*/
 }
 
 class Foo {
   Foo() {
-    /*bl*/ /*s:1*/ print('hi');
+    /*bl*/ print('hi');
     // Comment to push the ending brace back a bit.
-    /*s:2*/
+    /*s:1*/
   }
 }
diff --git a/pkg/dev_compiler/tool/input_sdk/patch/core_patch.dart b/pkg/dev_compiler/tool/input_sdk/patch/core_patch.dart
index 17423eb..1b3aa2a 100644
--- a/pkg/dev_compiler/tool/input_sdk/patch/core_patch.dart
+++ b/pkg/dev_compiler/tool/input_sdk/patch/core_patch.dart
@@ -86,7 +86,7 @@
       positionalArguments = new List.from(positionalArguments)..add(map);
     }
     return JS(
-        '', 'dart.dcall.apply(null, [#].concat(#))', f, positionalArguments);
+        '', '#.apply(null, [#].concat(#))', dart.dcall, f, positionalArguments);
   }
 
   static Map<String, dynamic> _toMangledNames(
diff --git a/pkg/dev_compiler/tool/input_sdk/private/js_number.dart b/pkg/dev_compiler/tool/input_sdk/private/js_number.dart
index fa8a5d8..eb86702 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/js_number.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/js_number.dart
@@ -384,11 +384,18 @@
   @notNull
   int get bitLength {
     int nonneg = this < 0 ? -this - 1 : this;
-    if (nonneg >= 0x100000000) {
+    int wordBits = 32;
+    while (nonneg >= 0x100000000) {
       nonneg = nonneg ~/ 0x100000000;
-      return _bitCount(_spread(nonneg)) + 32;
+      wordBits += 32;
     }
-    return _bitCount(_spread(nonneg));
+    return wordBits - _clz32(nonneg);
+  }
+
+  @notNull
+  static int _clz32(@notNull int uint32) {
+    // TODO(sra): Use `Math.clz32(uint32)` (not available on IE11).
+    return 32 - _bitCount(_spread(uint32));
   }
 
   // Returns pow(this, e) % m.
diff --git a/pkg/front_end/lib/src/fasta/deprecated_problems.dart b/pkg/front_end/lib/src/fasta/deprecated_problems.dart
index 385c911..9a85000 100644
--- a/pkg/front_end/lib/src/fasta/deprecated_problems.dart
+++ b/pkg/front_end/lib/src/fasta/deprecated_problems.dart
@@ -118,14 +118,14 @@
   String json = JSON.encode(data);
   HttpClient client = new HttpClient();
   try {
-    Uri uri = Uri.parse(defaultServerAddress);
+    Uri serverUri = Uri.parse(defaultServerAddress);
     HttpClientRequest request;
     try {
-      request = await client.postUrl(uri);
+      request = await client.postUrl(serverUri);
     } on SocketException {
       // Assume the crash logger isn't running.
       await client.close(force: true);
-      return new Future.error(error, trace);
+      return new Future.error(new Crash(uri, charOffset, error, trace), trace);
     }
     if (request != null) {
       await note("\nSending crash report data");
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 8dade32..2a04c1e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -377,11 +377,12 @@
                     name = "#T${index}";
                   }
                 } else if (argument.arguments == null) {
-                  name = unresolved[argument.name] ??= "#U${unresolvedCount++}";
+                  name = unresolved["${argument.name}"] ??=
+                      "#U${unresolvedCount++}";
                 }
               }
               name ??= "#U${unresolvedCount++}";
-              unresolvedReversed[name] = argument.name;
+              unresolvedReversed[name] = "${argument.name}";
               freeTypes[name] = argument;
               part.add(name);
               type.arguments[i] = new KernelNamedTypeBuilder(name, null);
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
index eb6157c..7d61f46 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
@@ -149,7 +149,7 @@
     var expectedType = inferrer.coreTypes.boolClass.rawType;
     var actualType =
         inferrer.inferExpression(condition, expectedType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         expectedType, actualType, condition, condition.fileOffset);
     if (message != null) {
       inferrer.inferExpression(message, null, false);
@@ -435,7 +435,7 @@
         // temporary variable first.
         assert(identical(combiner.arguments.positional[0], rhs));
         var expectedType = getPositionalParameterType(combinerType, 0);
-        inferrer.checkAssignability(
+        inferrer.ensureAssignable(
             expectedType, rhsType, rhs, combiner.fileOffset);
       }
       if (isOverloadedArithmeticOperator) {
@@ -454,7 +454,7 @@
           combinedType,
           combinerType,
           combiner.fileOffset);
-      var replacedCombiner2 = inferrer.checkAssignability(
+      var replacedCombiner2 = inferrer.ensureAssignable(
           writeContext, combinedType, replacedCombiner, writeOffset);
       if (replacedCombiner2 != null) {
         replacedCombiner = replacedCombiner2;
@@ -463,7 +463,7 @@
     } else {
       var rhsType = inferrer.inferExpression(rhs, writeContext, true);
       var replacedRhs =
-          inferrer.checkAssignability(writeContext, rhsType, rhs, writeOffset);
+          inferrer.ensureAssignable(writeContext, rhsType, rhs, writeOffset);
       _storeLetType(inferrer, replacedRhs ?? rhs, rhsType);
       if (nullAwareCombiner != null) {
         MethodInvocation equalsInvocation = nullAwareCombiner.condition;
@@ -541,7 +541,7 @@
     var expectedType = inferrer.coreTypes.boolClass.rawType;
     var conditionType =
         inferrer.inferExpression(condition, expectedType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         expectedType, conditionType, condition, condition.fileOffset);
     DartType thenType = inferrer.inferExpression(then, typeContext, true);
     bool useLub = _forceLub || typeContext == null;
@@ -691,7 +691,7 @@
     var boolType = inferrer.coreTypes.boolClass.rawType;
     var actualType =
         inferrer.inferExpression(condition, boolType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         boolType, actualType, condition, condition.fileOffset);
     inferrer.listener.doStatementExit(this);
   }
@@ -846,7 +846,7 @@
   void _inferInitializer(ShadowTypeInferrer inferrer) {
     inferrer.listener.fieldInitializerEnter(this);
     var initializerType = inferrer.inferExpression(value, field.type, true);
-    inferrer.checkAssignability(field.type, initializerType, value, fileOffset);
+    inferrer.ensureAssignable(field.type, initializerType, value, fileOffset);
     inferrer.listener.fieldInitializerExit(this);
   }
 }
@@ -893,7 +893,7 @@
     inferrer.listener.forInStatementEnter(this, variable, syntheticWrite);
     var inferredExpressionType = inferrer.resolveTypeParameter(inferrer
         .inferExpression(iterable, context, typeNeeded || typeChecksNeeded));
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         inferrer.wrapType(const DynamicType(), iterableClass),
         inferredExpressionType,
         iterable,
@@ -923,7 +923,7 @@
           new VariableDeclaration(null, type: inferredType, isFinal: true);
       var variableGet = new VariableGet(tempVar)
         ..fileOffset = this.variable.fileOffset;
-      var implicitDowncast = inferrer.checkAssignability(
+      var implicitDowncast = inferrer.ensureAssignable(
           variable.type, inferredType, variableGet, fileOffset);
       if (implicitDowncast != null) {
         this.variable = tempVar..parent = this;
@@ -932,7 +932,7 @@
       }
     } else if (syntheticAssignment is ShadowSyntheticExpression) {
       if (syntheticAssignment is ShadowComplexAssignment) {
-        inferrer.checkAssignability(
+        inferrer.ensureAssignable(
             greatestClosure(inferrer.coreTypes, syntheticWriteType),
             this.variable.type,
             syntheticAssignment.rhs,
@@ -962,7 +962,7 @@
       var expectedType = inferrer.coreTypes.boolClass.rawType;
       var conditionType = inferrer.inferExpression(
           condition, expectedType, !inferrer.isTopLevel);
-      inferrer.checkAssignability(
+      inferrer.ensureAssignable(
           expectedType, conditionType, condition, condition.fileOffset);
     }
     for (var update in updates) {
@@ -1076,7 +1076,7 @@
     var expectedType = inferrer.coreTypes.boolClass.rawType;
     var conditionType =
         inferrer.inferExpression(condition, expectedType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         expectedType, conditionType, condition, condition.fileOffset);
     inferrer.inferStatement(then);
     if (otherwise != null) inferrer.inferStatement(otherwise);
@@ -1158,7 +1158,7 @@
     var indexType = inferrer.inferExpression(index, indexContext, true);
     _storeLetType(inferrer, index, indexType);
     if (writeContext != null) {
-      inferrer.checkAssignability(
+      inferrer.ensureAssignable(
           expectedIndexTypeForWrite,
           indexType,
           _getInvocationArguments(inferrer, write).positional[0],
@@ -1171,7 +1171,7 @@
           inferrer.findMethodInvocationMember(receiverType, read, silent: true);
       var calleeFunctionType =
           inferrer.getCalleeFunctionType(readMember, receiverType, false);
-      inferrer.checkAssignability(
+      inferrer.ensureAssignable(
           getPositionalParameterType(calleeFunctionType, 0),
           indexType,
           _getInvocationArguments(inferrer, read).positional[0],
@@ -1337,8 +1337,8 @@
     }
     if (typeChecksNeeded) {
       for (int i = 0; i < expressions.length; i++) {
-        inferrer.checkAssignability(typeArgument, actualTypes[i],
-            expressions[i], expressions[i].fileOffset);
+        inferrer.ensureAssignable(typeArgument, actualTypes[i], expressions[i],
+            expressions[i].fileOffset);
       }
     }
     var inferredType = new InterfaceType(listClass, [inferredTypeArgument]);
@@ -1362,8 +1362,8 @@
     inferrer.listener.logicalExpressionBeforeRhs(this);
     var rightType =
         inferrer.inferExpression(right, boolType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(boolType, leftType, left, left.fileOffset);
-    inferrer.checkAssignability(boolType, rightType, right, right.fileOffset);
+    inferrer.ensureAssignable(boolType, leftType, left, left.fileOffset);
+    inferrer.ensureAssignable(boolType, rightType, right, right.fileOffset);
     var inferredType = boolType;
     inferrer.listener.logicalExpressionExit(this, inferredType);
     return inferredType;
@@ -1461,10 +1461,10 @@
       for (int i = 0; i < entries.length; i++) {
         var entry = entries[i];
         var key = entry.key;
-        inferrer.checkAssignability(
+        inferrer.ensureAssignable(
             keyType, actualTypes[2 * i], key, key.fileOffset);
         var value = entry.value;
-        inferrer.checkAssignability(
+        inferrer.ensureAssignable(
             valueType, actualTypes[2 * i + 1], value, value.fileOffset);
       }
     }
@@ -1551,7 +1551,7 @@
     var boolType = inferrer.coreTypes.boolClass.rawType;
     var actualType =
         inferrer.inferExpression(operand, boolType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(boolType, actualType, operand, fileOffset);
+    inferrer.ensureAssignable(boolType, actualType, operand, fileOffset);
     DartType inferredType = boolType;
     inferrer.listener.notExit(this, inferredType);
     return inferredType;
@@ -2476,7 +2476,7 @@
       type = inferredType;
     }
     if (initializer != null) {
-      var replacedInitializer = inferrer.checkAssignability(
+      var replacedInitializer = inferrer.ensureAssignable(
           type, initializerType, initializer, fileOffset);
       if (replacedInitializer != null) {
         initializer = replacedInitializer;
@@ -2546,7 +2546,7 @@
     var expectedType = inferrer.coreTypes.boolClass.rawType;
     var actualType =
         inferrer.inferExpression(condition, expectedType, !inferrer.isTopLevel);
-    inferrer.checkAssignability(
+    inferrer.ensureAssignable(
         expectedType, actualType, condition, condition.fileOffset);
     inferrer.inferStatement(body);
     inferrer.listener.whileStatementExit(this);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 55cfd14..6951f55 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -230,8 +230,16 @@
           : returnOrYieldContext;
       if (expectedType != null) {
         expectedType = greatestClosure(inferrer.coreTypes, expectedType);
-        if (inferrer.checkAssignability(
-                expectedType, type, expression, fileOffset) !=
+        DartType expectedTypeToCheck = expectedType;
+        if (!inferrer.isAssignable(expectedType, type) && isAsync) {
+          DartType unfuturedExpectedType =
+              inferrer.typeSchemaEnvironment.unfutureType(expectedType);
+          if (inferrer.isAssignable(unfuturedExpectedType, type)) {
+            expectedTypeToCheck = unfuturedExpectedType;
+          }
+        }
+        if (inferrer.ensureAssignable(
+                expectedTypeToCheck, type, expression, fileOffset) !=
             null) {
           type = expectedType;
         }
@@ -435,9 +443,14 @@
   /// inference.
   TypePromoter get typePromoter;
 
+  bool isAssignable(DartType expectedType, DartType actualType) {
+    return typeSchemaEnvironment.isSubtypeOf(expectedType, actualType) ||
+        typeSchemaEnvironment.isSubtypeOf(actualType, expectedType);
+  }
+
   /// Checks whether [actualType] can be assigned to [expectedType], and inserts
   /// an implicit downcast if appropriate.
-  Expression checkAssignability(DartType expectedType, DartType actualType,
+  Expression ensureAssignable(DartType expectedType, DartType actualType,
       Expression expression, int fileOffset) {
     assert(expectedType == null || isKnown(expectedType));
     // We don't need to insert assignability checks when doing top level type
@@ -929,7 +942,7 @@
     var actualType =
         inferExpression(initializer, declaredType, declaredType != null);
     if (declaredType != null) {
-      checkAssignability(
+      ensureAssignable(
           declaredType, actualType, initializer, initializer.fileOffset);
     }
     this.helper = null;
@@ -1066,7 +1079,7 @@
         var expression = i < numPositionalArgs
             ? arguments.positional[i]
             : arguments.named[i - numPositionalArgs].value;
-        checkAssignability(
+        ensureAssignable(
             expectedType, actualType, expression, expression.fileOffset);
       }
     }
@@ -1299,7 +1312,7 @@
     this.helper = helper;
     assert(declaredType != null);
     var actualType = inferExpression(initializer, declaredType, true);
-    checkAssignability(
+    ensureAssignable(
         declaredType, actualType, initializer, initializer.fileOffset);
     this.helper = null;
   }
diff --git a/pkg/front_end/testcases/regress/issue_32182.dart b/pkg/front_end/testcases/regress/issue_32182.dart
new file mode 100644
index 0000000..7e42146
--- /dev/null
+++ b/pkg/front_end/testcases/regress/issue_32182.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "issue_32182.dart" as self;
+
+class A<T> {}
+
+class M {
+  m() => 42;
+}
+
+class C extends A<self.A> with M {}
+
+main() {
+  new C().m() + 1;
+}
diff --git a/pkg/front_end/testcases/regress/issue_32182.dart.direct.expect b/pkg/front_end/testcases/regress/issue_32182.dart.direct.expect
new file mode 100644
index 0000000..4a0cedc
--- /dev/null
+++ b/pkg/front_end/testcases/regress/issue_32182.dart.direct.expect
@@ -0,0 +1,26 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A<T extends core::Object> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class M extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method m() → dynamic
+    return 42;
+}
+abstract class _A&M^#U0^<#U0 extends core::Object> = self::A<self::_A&M^#U0^::#U0> with self::M {
+}
+class C extends self::_A&M^#U0^<self::A<dynamic>> {
+  synthetic constructor •() → void
+    : super self::A::•()
+    ;
+}
+static method main() → dynamic {
+  new self::C::•().m().+(1);
+}
diff --git a/pkg/front_end/testcases/regress/issue_32182.dart.outline.expect b/pkg/front_end/testcases/regress/issue_32182.dart.outline.expect
new file mode 100644
index 0000000..b04b47b
--- /dev/null
+++ b/pkg/front_end/testcases/regress/issue_32182.dart.outline.expect
@@ -0,0 +1,22 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A<T extends core::Object> extends core::Object {
+  synthetic constructor •() → void
+    ;
+}
+class M extends core::Object {
+  synthetic constructor •() → void
+    ;
+  method m() → dynamic
+    ;
+}
+abstract class _A&M^#U0^<#U0 extends core::Object> = self::A<self::_A&M^#U0^::#U0> with self::M {
+}
+class C extends self::_A&M^#U0^<self::A<dynamic>> {
+  synthetic constructor •() → void
+    ;
+}
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/regress/issue_32182.dart.strong.expect b/pkg/front_end/testcases/regress/issue_32182.dart.strong.expect
new file mode 100644
index 0000000..9bf3c1c
--- /dev/null
+++ b/pkg/front_end/testcases/regress/issue_32182.dart.strong.expect
@@ -0,0 +1,26 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class A<T extends core::Object> extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
+class M extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method m() → dynamic
+    return 42;
+}
+abstract class _A&M^#U0^<#U0 extends core::Object> = self::A<self::_A&M^#U0^::#U0> with self::M {
+}
+class C extends self::_A&M^#U0^<self::A<dynamic>> {
+  synthetic constructor •() → void
+    : super self::A::•()
+    ;
+}
+static method main() → dynamic {
+  new self::C::•().{self::M::m}().+(1);
+}
diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart
index 0df34ed..55cd916 100644
--- a/pkg/kernel/lib/class_hierarchy.dart
+++ b/pkg/kernel/lib/class_hierarchy.dart
@@ -10,8 +10,6 @@
 import 'src/heap.dart';
 import 'type_algebra.dart';
 
-import 'src/incremental_class_hierarchy.dart' show IncrementalClassHierarchy;
-
 typedef HandleAmbiguousSupertypes = void Function(Class, Supertype, Supertype);
 
 abstract class MixinInferrer {
@@ -40,12 +38,6 @@
       .._initialize(mixinInferrer);
   }
 
-  /// Use [ClassHierarchy] factory instead.
-  @deprecated
-  factory ClassHierarchy.deprecated_incremental([Program program]) {
-    return new IncrementalClassHierarchy.deprecated();
-  }
-
   /// Given the [unordered] classes, return them in such order that classes
   /// occur after their superclasses.  If some superclasses are not in
   /// [unordered], they are not included.
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 6ae2bb9..78a59c6 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -14,7 +14,7 @@
 ///
 /// It is safe to clone members, but cloning a class or library is not
 /// supported.
-class CloneVisitor extends TreeVisitor {
+class CloneVisitor implements TreeVisitor {
   final Map<VariableDeclaration, VariableDeclaration> variables =
       <VariableDeclaration, VariableDeclaration>{};
   final Map<LabeledStatement, LabeledStatement> labels =
@@ -474,4 +474,89 @@
   visitNamedExpression(NamedExpression node) {
     return new NamedExpression(node.name, clone(node.value));
   }
+
+  defaultBasicLiteral(BasicLiteral node) {
+    return defaultExpression(node);
+  }
+
+  defaultExpression(Expression node) {
+    throw 'Unimplemented clone for Kernel expression: $node';
+  }
+
+  defaultInitializer(Initializer node) {
+    throw 'Unimplemented clone for Kernel initializer: $node';
+  }
+
+  defaultMember(Member node) {
+    throw 'Unimplemented clone for Kernel member: $node';
+  }
+
+  defaultStatement(Statement node) {
+    throw 'Unimplemented clone for Kernel statement: $node';
+  }
+
+  defaultTreeNode(TreeNode node) {
+    throw 'Cloning Kernel non-members is not supported.  '
+        'Tried cloning $node';
+  }
+
+  visitAssertInitializer(AssertInitializer node) {
+    return new AssertInitializer(clone(node.statement));
+  }
+
+  visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) {
+    return new CheckLibraryIsLoaded(node.import);
+  }
+
+  visitCombinator(Combinator node) {
+    return defaultTreeNode(node);
+  }
+
+  visitFieldInitializer(FieldInitializer node) {
+    return new FieldInitializer.byReference(
+        node.fieldReference, clone(node.value));
+  }
+
+  visitInstantiation(Instantiation node) {
+    return new Instantiation(
+        clone(node.expression), node.typeArguments.map(visitType).toList());
+  }
+
+  visitInvalidInitializer(InvalidInitializer node) {
+    return new InvalidInitializer();
+  }
+
+  visitLibraryDependency(LibraryDependency node) {
+    return defaultTreeNode(node);
+  }
+
+  visitLibraryPart(LibraryPart node) {
+    return defaultTreeNode(node);
+  }
+
+  visitLoadLibrary(LoadLibrary node) {
+    return new LoadLibrary(node.import);
+  }
+
+  visitLocalInitializer(LocalInitializer node) {
+    return new LocalInitializer(clone(node.variable));
+  }
+
+  visitProgram(Program node) {
+    return defaultTreeNode(node);
+  }
+
+  visitRedirectingInitializer(RedirectingInitializer node) {
+    return new RedirectingInitializer.byReference(
+        node.targetReference, clone(node.arguments));
+  }
+
+  visitSuperInitializer(SuperInitializer node) {
+    return new SuperInitializer.byReference(
+        node.targetReference, clone(node.arguments));
+  }
+
+  visitTypedef(Typedef node) {
+    return defaultTreeNode(node);
+  }
 }
diff --git a/pkg/kernel/lib/src/incremental_class_hierarchy.dart b/pkg/kernel/lib/src/incremental_class_hierarchy.dart
deleted file mode 100644
index ba22bce..0000000
--- a/pkg/kernel/lib/src/incremental_class_hierarchy.dart
+++ /dev/null
@@ -1,746 +0,0 @@
-// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-library kernel.incremental_class_hierarchy;
-
-import 'dart:collection';
-import 'dart:math';
-
-import 'package:kernel/ast.dart';
-import 'package:kernel/class_hierarchy.dart';
-import 'package:kernel/src/heap.dart';
-import 'package:kernel/type_algebra.dart';
-
-/// Use [ClassHierarchy] instead.
-@deprecated
-class IncrementalClassHierarchy implements ClassHierarchy {
-  /// Use [ClassHierarchy] instead.
-  @deprecated
-  IncrementalClassHierarchy.deprecated();
-
-  /// The next unique identifier for [_ClassInfo]s.
-  int _nextId = 0;
-
-  /// The mapping from [Class]es to the corresponding [_ClassInfo]s.
-  /// The map is ordered in such a way that classes are after superclasses.
-  /// It is filled lazily as the client requests information about classes.
-  final Map<Class, _ClassInfo> _info = new LinkedHashMap<Class, _ClassInfo>();
-
-  @override
-  ClassHierarchy applyChanges(Iterable<Class> classes) {
-    if (classes.isEmpty) return this;
-    return new IncrementalClassHierarchy.deprecated();
-  }
-
-  @override
-  void forEachOverridePair(Class node,
-      callback(Member declaredMember, Member interfaceMember, bool isSetter)) {
-    _ClassInfo info = _getInfo(node);
-    for (var supertype in node.supers) {
-      var superNode = supertype.classNode;
-      var superInfo = _getInfo(superNode);
-
-      var superGetters = superInfo.interfaceGettersAndCalls;
-      var superSetters = superInfo.interfaceSetters;
-
-      _reportOverrides(info.implementedGettersAndCalls, superGetters, callback);
-      _reportOverrides(info.declaredGettersAndCalls, superGetters, callback,
-          onlyAbstract: true);
-
-      _reportOverrides(info.implementedSetters, superSetters, callback,
-          isSetter: true);
-      _reportOverrides(info.declaredSetters, superSetters, callback,
-          isSetter: true, onlyAbstract: true);
-    }
-    if (!node.isAbstract) {
-      // If a non-abstract class declares an abstract method M whose
-      // implementation M' is inherited from the superclass, then the inherited
-      // method M' overrides the declared method M.
-      // This flies in the face of conventional override logic, but is necessary
-      // because an instance of the class will contain the method M' which can
-      // be invoked through the interface of M.
-      // Note that [_reportOverrides] does not report self-overrides, so in
-      // most cases these calls will just scan both lists and report nothing.
-      _reportOverrides(info.implementedGettersAndCalls,
-          info.declaredGettersAndCalls, callback);
-      _reportOverrides(info.implementedSetters, info.declaredSetters, callback,
-          isSetter: true);
-    }
-  }
-
-  @override
-  Supertype getClassAsInstanceOf(Class node, Class superclass) {
-    if (identical(node, superclass)) return node.asThisSupertype;
-    _ClassInfo info = _getInfo(node);
-    _ClassInfo superInfo = _getInfo(superclass);
-    if (!info.isSubtypeOf(superInfo)) return null;
-    if (superclass.typeParameters.isEmpty) return superclass.asRawSupertype;
-    return info.genericSuperTypes[superclass];
-  }
-
-  @override
-  int getClassDepth(Class node) {
-    return _getInfo(node).depth;
-  }
-
-  @override
-  InterfaceType getClassicLeastUpperBound(
-      InterfaceType type1, InterfaceType type2) {
-    // The algorithm is: first we compute a list of superclasses for both types,
-    // ordered from greatest to least depth, and ordered by topological sort
-    // index within each depth.  Due to the sort order, we can find the
-    // intersection of these lists by a simple walk.
-    //
-    // Then, for each class in the intersection, determine the exact type that
-    // is implemented by type1 and type2.  If the types match, that type is a
-    // candidate (it's a member of S_n).  As soon as we find a candidate which
-    // is unique for its depth, we return it.
-    //
-    // As an optimization, if the class for I is a subtype of the class for J,
-    // then we know that the list of superclasses of J is a subset of the list
-    // of superclasses for I; therefore it is sufficient to compute just the
-    // list of superclasses for J.  To avoid complicating the code below (which
-    // intersects the two lists), we set both lists equal to the list of
-    // superclasses for J.  And vice versa with the role of I and J swapped.
-
-    // Compute the list of superclasses for both types, with the above
-    // optimization.
-    _ClassInfo info1 = _getInfo(type1.classNode);
-    _ClassInfo info2 = _getInfo(type2.classNode);
-    List<_ClassInfo> classes1;
-    List<_ClassInfo> classes2;
-    if (identical(info1, info2) || info1.isSubtypeOf(info2)) {
-      classes1 = classes2 = _getRankedSuperclassList(info2);
-    } else if (info2.isSubtypeOf(info1)) {
-      classes1 = classes2 = _getRankedSuperclassList(info1);
-    } else {
-      classes1 = _getRankedSuperclassList(info1);
-      classes2 = _getRankedSuperclassList(info2);
-    }
-
-    // Walk the lists finding their intersection, looking for a depth that has a
-    // single candidate.
-    int i1 = 0;
-    int i2 = 0;
-    InterfaceType candidate = null;
-    int currentDepth = -1;
-    int numCandidatesAtThisDepth = 0;
-    while (true) {
-      _ClassInfo next = classes1[i1];
-      _ClassInfo next2 = classes2[i2];
-      if (!identical(next, next2)) {
-        if (_LubHeap.sortsBeforeStatic(next, next2)) {
-          ++i1;
-        } else {
-          ++i2;
-        }
-        continue;
-      }
-      ++i2;
-      ++i1;
-      if (next.depth != currentDepth) {
-        if (numCandidatesAtThisDepth == 1) return candidate;
-        currentDepth = next.depth;
-        numCandidatesAtThisDepth = 0;
-        candidate = null;
-      } else if (numCandidatesAtThisDepth > 1) {
-        continue;
-      }
-
-      // For each class in the intersection, find the exact type that is
-      // implemented by type1 and type2.  If they match, it's a candidate.
-      //
-      // Two additional optimizations:
-      //
-      // - If this class lacks type parameters, we know there is a match without
-      //   needing to substitute.
-      //
-      // - If the depth is 0, we have reached Object, so we can return it
-      //   immediately.  Since all interface types are subtypes of Object, this
-      //   ensures the loop terminates.
-      if (next.node.typeParameters.isEmpty) {
-        candidate = next.node.rawType;
-        if (currentDepth == 0) return candidate;
-        ++numCandidatesAtThisDepth;
-      } else {
-        var superType1 = identical(info1, next)
-            ? type1
-            : Substitution.fromInterfaceType(type1).substituteType(
-                info1.genericSuperTypes[next.node].asInterfaceType);
-        var superType2 = identical(info2, next)
-            ? type2
-            : Substitution.fromInterfaceType(type2).substituteType(
-                info2.genericSuperTypes[next.node].asInterfaceType);
-        if (superType1 == superType2) {
-          candidate = superType1;
-          ++numCandidatesAtThisDepth;
-        }
-      }
-    }
-  }
-
-  @override
-  int getClassIndex(Class node) {
-    return _getInfo(node).id;
-  }
-
-  @override
-  Member getDispatchTarget(Class node, Name name, {bool setter: false}) {
-    _ClassInfo info = _getInfo(node);
-    List<Member> targets =
-        setter ? info.implementedSetters : info.implementedGettersAndCalls;
-    return ClassHierarchy.findMemberByName(targets, name);
-  }
-
-  @override
-  Member getInterfaceMember(Class node, Name name, {bool setter: false}) {
-    _ClassInfo info = _getInfo(node);
-    List<Member> members =
-        setter ? info.interfaceSetters : info.interfaceGettersAndCalls;
-    return ClassHierarchy.findMemberByName(members, name);
-  }
-
-  @override
-  List<Member> getInterfaceMembers(Class class_, {bool setters: false}) {
-    var info = _getInfo(class_);
-    return setters ? info.interfaceSetters : info.interfaceGettersAndCalls;
-  }
-
-  @override
-  List<Member> getDeclaredMembers(Class class_, {bool setters: false}) {
-    var info = _getInfo(class_);
-    return setters ? info.declaredSetters : info.declaredGettersAndCalls;
-  }
-
-  @override
-  Iterable<Class> getOrderedClasses(Iterable<Class> unordered) {
-    unordered.forEach(_getInfo);
-    var unorderedSet = unordered.toSet();
-    return _info.keys.where(unorderedSet.contains);
-  }
-
-  @override
-  List<Class> getRankedSuperclasses(Class node) {
-    var info = _getInfo(node);
-    return _getRankedSuperclassList(info).map((info) => info.node).toList();
-  }
-
-  @override
-  Supertype asInstantiationOf(Supertype type, Class superclass) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass) {
-    Supertype castedType = getClassAsInstanceOf(type.classNode, superclass);
-    if (castedType == null) return null;
-    return Substitution
-        .fromInterfaceType(type)
-        .substituteType(castedType.asInterfaceType);
-  }
-
-  @override
-  bool hasProperSubtypes(Class class_) {
-    // TODO(scheglov): implement hasProperSubtypes
-    throw new UnimplementedError();
-  }
-
-  /// Fill the given [info] with declared instance methods and setters.
-  void _buildDeclaredMembers(_ClassInfo info) {
-    Class node = info.node;
-    if (node.mixedInType != null) {
-      _ClassInfo mixedInfo = _getInfo(node.mixedInType.classNode);
-      info.declaredGettersAndCalls = mixedInfo.declaredGettersAndCalls;
-      info.declaredSetters = mixedInfo.declaredSetters;
-    } else {
-      var members = info.declaredGettersAndCalls = <Member>[];
-      var setters = info.declaredSetters = <Member>[];
-      for (Procedure procedure in node.procedures) {
-        if (procedure.isStatic) continue;
-        if (procedure.kind == ProcedureKind.Setter) {
-          setters.add(procedure);
-        } else {
-          members.add(procedure);
-        }
-      }
-      for (Field field in node.fields) {
-        if (field.isStatic) continue;
-        if (field.hasImplicitGetter) {
-          members.add(field);
-        }
-        if (field.hasImplicitSetter) {
-          setters.add(field);
-        }
-      }
-      members.sort(ClassHierarchy.compareMembers);
-      setters.sort(ClassHierarchy.compareMembers);
-    }
-  }
-
-  /// Fill the given [info] with implemented not abstract members and setters.
-  void _buildImplementedMembers(_ClassInfo info) {
-    List<Member> inheritedMembers;
-    List<Member> inheritedSetters;
-    Supertype supertype = info.node.supertype;
-    if (supertype == null) {
-      inheritedMembers = inheritedSetters = const <Member>[];
-    } else {
-      _ClassInfo superInfo = _getInfo(supertype.classNode);
-      inheritedMembers = superInfo.implementedGettersAndCalls;
-      inheritedSetters = superInfo.implementedSetters;
-    }
-    info.implementedGettersAndCalls = _inheritMembers(
-        info.declaredGettersAndCalls, inheritedMembers,
-        skipAbstractMembers: true);
-    info.implementedSetters = _inheritMembers(
-        info.declaredSetters, inheritedSetters,
-        skipAbstractMembers: true);
-  }
-
-  /// Build interface methods or setters for the class described by [info].
-  void _buildInterfaceMembers(_ClassInfo info) {
-    List<Member> declaredGetters = info.declaredGettersAndCalls;
-    List<Member> declaredSetters = info.declaredSetters;
-    List<Member> allInheritedGetters = <Member>[];
-    List<Member> allInheritedSetters = <Member>[];
-
-    void inheritFrom(Supertype type) {
-      if (type == null) return;
-      var info = _getInfo(type.classNode);
-      // TODO(scheglov): Can we optimize this with yield?
-
-      var inheritedGetters = _getUnshadowedInheritedMembers(
-          declaredGetters, info.interfaceGettersAndCalls);
-      allInheritedGetters = _merge(allInheritedGetters, inheritedGetters);
-
-      var inheritedSetters = _getUnshadowedInheritedMembers(
-          declaredSetters, info.interfaceSetters);
-      allInheritedSetters = _merge(allInheritedSetters, inheritedSetters);
-    }
-
-    Class node = info.node;
-    inheritFrom(node.supertype);
-    inheritFrom(node.mixedInType);
-    node.implementedTypes.forEach(inheritFrom);
-
-    info.interfaceGettersAndCalls =
-        _inheritMembers(declaredGetters, allInheritedGetters);
-    info.interfaceSetters =
-        _inheritMembers(declaredSetters, allInheritedSetters);
-  }
-
-  /// Return the [_ClassInfo] for the [node].
-  _ClassInfo _getInfo(Class node) {
-    var info = _info[node];
-    if (info == null) {
-      info = new _ClassInfo(_nextId++, node);
-
-      void addSupertypeIdentifiers(_ClassInfo superInfo) {
-        info.supertypeIdSet.add(superInfo.id);
-        info.supertypeIdSet.addAll(superInfo.supertypeIdSet);
-      }
-
-      int superDepth = -1;
-      if (node.supertype != null) {
-        var superInfo = _getInfo(node.supertype.classNode);
-        superDepth = max(superDepth, superInfo.depth);
-        addSupertypeIdentifiers(superInfo);
-        _recordSuperTypes(info, node.supertype, superInfo);
-      }
-      if (node.mixedInType != null) {
-        var mixedInfo = _getInfo(node.mixedInType.classNode);
-        superDepth = max(superDepth, mixedInfo.depth);
-        addSupertypeIdentifiers(mixedInfo);
-        _recordSuperTypes(info, node.mixedInType, mixedInfo);
-      }
-      for (var implementedType in node.implementedTypes) {
-        var implementedInfo = _getInfo(implementedType.classNode);
-        superDepth = max(superDepth, implementedInfo.depth);
-        addSupertypeIdentifiers(implementedInfo);
-        _recordSuperTypes(info, implementedType, implementedInfo);
-      }
-
-      info.depth = superDepth + 1;
-      _info[node] = info;
-
-      _buildDeclaredMembers(info);
-      _buildImplementedMembers(info);
-      _buildInterfaceMembers(info);
-    }
-    return info;
-  }
-
-  List<_ClassInfo> _getRankedSuperclassList(_ClassInfo info) {
-    if (info.rankedSuperclassList != null) {
-      return info.rankedSuperclassList;
-    }
-
-    var heap = new _LubHeap()..add(info);
-    var chain = <_ClassInfo>[];
-    info.rankedSuperclassList = chain;
-
-    _ClassInfo lastInfo = null;
-    while (heap.isNotEmpty) {
-      var nextInfo = heap.remove();
-      if (identical(nextInfo, lastInfo)) continue;
-      lastInfo = nextInfo;
-
-      chain.add(nextInfo);
-
-      void addToHeap(Supertype supertype) {
-        var superInfo = _getInfo(supertype.classNode);
-        heap.add(superInfo);
-      }
-
-      var classNode = nextInfo.node;
-      if (classNode.supertype != null) addToHeap(classNode.supertype);
-      if (classNode.mixedInType != null) addToHeap(classNode.mixedInType);
-      classNode.implementedTypes.forEach(addToHeap);
-    }
-    return chain;
-  }
-
-  void _recordSuperTypes(
-      _ClassInfo subInfo, Supertype supertype, _ClassInfo superInfo) {
-    if (supertype.typeArguments.isEmpty) {
-      // The supertype is not generic, and if it does not have generic
-      // supertypes itself, then subclass also does not have generic supertypes.
-      if (superInfo.genericSuperTypes == null) return;
-      // Since the immediate super type is not generic, all entries in its
-      // super type map are also valid entries for this class.
-      if (subInfo.genericSuperTypes == null &&
-          superInfo.ownsGenericSuperTypeMap) {
-        // Instead of copying the map, take ownership of the map object.
-        // This may result in more entries being added to the map later. Those
-        // are not valid for the super type, but it works out because all
-        // lookups in the map are guarded by a subtype check, so the super type
-        // will not be bothered by the extra entries.
-        subInfo.genericSuperTypes = superInfo.genericSuperTypes;
-        superInfo.ownsGenericSuperTypeMap = false;
-      } else {
-        // Copy over the super type entries.
-        subInfo.genericSuperTypes ??= <Class, Supertype>{};
-        subInfo.genericSuperTypes.addAll(superInfo.genericSuperTypes);
-      }
-    } else {
-      // Copy over all transitive generic super types, and substitute the
-      // free variables with those provided in [supertype].
-      Class superclass = supertype.classNode;
-      var substitution = Substitution.fromPairs(
-          superclass.typeParameters, supertype.typeArguments);
-      subInfo.genericSuperTypes ??= <Class, Supertype>{};
-      superInfo.genericSuperTypes?.forEach((Class key, Supertype type) {
-        subInfo.genericSuperTypes[key] = substitution.substituteSupertype(type);
-      });
-      subInfo.genericSuperTypes[superclass] = supertype;
-    }
-  }
-
-  /// Returns the subset of members in [inherited] for which a member with the
-  /// same name does not occur in [declared].
-  ///
-  /// The input lists must be sorted, and the returned list is sorted.
-  static List<Member> _getUnshadowedInheritedMembers(
-      List<Member> declared, List<Member> inherited) {
-    List<Member> result =
-        new List<Member>.filled(inherited.length, null, growable: true);
-    int storeIndex = 0;
-    int i = 0, j = 0;
-    while (i < declared.length && j < inherited.length) {
-      Member declaredMember = declared[i];
-      Member inheritedMember = inherited[j];
-      int comparison =
-          ClassHierarchy.compareMembers(declaredMember, inheritedMember);
-      if (comparison < 0) {
-        ++i;
-      } else if (comparison > 0) {
-        result[storeIndex++] = inheritedMember;
-        ++j;
-      } else {
-        // Move past the shadowed member, but retain the declared member, as
-        // it may shadow multiple members.
-        ++j;
-      }
-    }
-    // If the list of declared members is exhausted, copy over the remains of
-    // the inherited members.
-    while (j < inherited.length) {
-      result[storeIndex++] = inherited[j++];
-    }
-    result.length = storeIndex;
-    return result;
-  }
-
-  /// Computes the list of implemented members, based on the declared instance
-  /// members and inherited instance members.
-  ///
-  /// Both lists must be sorted by name beforehand.
-  static List<Member> _inheritMembers(
-      List<Member> declared, List<Member> inherited,
-      {bool skipAbstractMembers: false}) {
-    List<Member> result = <Member>[]..length =
-        declared.length + inherited.length;
-    // Since both lists are sorted, we can fuse them like in merge sort.
-    int storeIndex = 0;
-    int i = 0, j = 0;
-    while (i < declared.length && j < inherited.length) {
-      Member declaredMember = declared[i];
-      Member inheritedMember = inherited[j];
-      if (skipAbstractMembers && declaredMember.isAbstract) {
-        ++i;
-        continue;
-      }
-      if (skipAbstractMembers && inheritedMember.isAbstract) {
-        ++j;
-        continue;
-      }
-      int comparison =
-          ClassHierarchy.compareMembers(declaredMember, inheritedMember);
-      if (comparison < 0) {
-        result[storeIndex++] = declaredMember;
-        ++i;
-      } else if (comparison > 0) {
-        result[storeIndex++] = inheritedMember;
-        ++j;
-      } else {
-        result[storeIndex++] = declaredMember;
-        ++i;
-        ++j; // Move past overridden member.
-      }
-    }
-    // One of the two lists is now exhausted, copy over the remains.
-    while (i < declared.length) {
-      Member declaredMember = declared[i++];
-      if (skipAbstractMembers && declaredMember.isAbstract) continue;
-      result[storeIndex++] = declaredMember;
-    }
-    while (j < inherited.length) {
-      Member inheritedMember = inherited[j++];
-      if (skipAbstractMembers && inheritedMember.isAbstract) continue;
-      result[storeIndex++] = inheritedMember;
-    }
-    result.length = storeIndex;
-    return result;
-  }
-
-  /// Merges two sorted lists.
-  ///
-  /// If a given member occurs in both lists, the merge will attempt to exclude
-  /// the duplicate member, but is not strictly guaranteed to do so.
-  static List<Member> _merge(List<Member> first, List<Member> second) {
-    if (first.isEmpty) return second;
-    if (second.isEmpty) return first;
-    List<Member> result = new List<Member>.filled(
-        first.length + second.length, null,
-        growable: true);
-    int storeIndex = 0;
-    int i = 0, j = 0;
-    while (i < first.length && j < second.length) {
-      Member firstMember = first[i];
-      Member secondMember = second[j];
-      int compare = ClassHierarchy.compareMembers(firstMember, secondMember);
-      if (compare <= 0) {
-        result[storeIndex++] = firstMember;
-        ++i;
-        // If the same member occurs in both lists, skip the duplicate.
-        if (identical(firstMember, secondMember)) {
-          ++j;
-        }
-      } else {
-        result[storeIndex++] = secondMember;
-        ++j;
-      }
-    }
-    while (i < first.length) {
-      result[storeIndex++] = first[i++];
-    }
-    while (j < second.length) {
-      result[storeIndex++] = second[j++];
-    }
-    result.length = storeIndex;
-    return result;
-  }
-
-  static void _reportOverrides(
-      List<Member> declaredList,
-      List<Member> inheritedList,
-      callback(Member declaredMember, Member interfaceMember, bool isSetter),
-      {bool isSetter: false,
-      bool onlyAbstract: false}) {
-    int i = 0, j = 0;
-    while (i < declaredList.length && j < inheritedList.length) {
-      Member declared = declaredList[i];
-      if (onlyAbstract && !declared.isAbstract) {
-        ++i;
-        continue;
-      }
-      Member inherited = inheritedList[j];
-      int comparison = ClassHierarchy.compareMembers(declared, inherited);
-      if (comparison < 0) {
-        ++i;
-      } else if (comparison > 0) {
-        ++j;
-      } else {
-        if (!identical(declared, inherited)) {
-          callback(declared, inherited, isSetter);
-        }
-        // A given declared member may override multiple interface members,
-        // so only move past the interface member.
-        ++j;
-      }
-    }
-  }
-
-  @override
-  List<Member> getDispatchTargets(Class class_, {bool setters: false}) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isUsedAsSuperInterface(Class class_) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isUsedAsSuperClass(Class class_) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isUsedAsMixin(Class class_) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isSubtypeOf(Class subtype, Class superclass) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isSubmixtureOf(Class submixture, Class superclass) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  bool isSubclassOf(Class subclass, Class superclass) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  ClassSet getSubtypesOf(Class class_) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  ClassSet getSubclassesOf(Class class_) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  Member getSingleTargetForInterfaceInvocation(Member interfaceTarget,
-      {bool setter: false}) {
-    throw new UnimplementedError();
-  }
-}
-
-/// Information about a [Class].
-class _ClassInfo {
-  /// The unique identifier of the [_ClassInfo].
-  final int id;
-
-  /// The [Class] node described by this [_ClassInfo].
-  final Class node;
-
-  /// The number of steps in the longest inheritance path from the class
-  /// to [Object], or `-1` if the depth has not been computed yet.
-  int depth = -1;
-
-  /// The list of superclasses sorted by depth (descending order) and
-  /// unique identifiers (ascending order), or `null` if the list has not
-  /// been computed yet.
-  List<_ClassInfo> rankedSuperclassList;
-
-  /// The set of [id]s for supertypes.
-  /// TODO(scheglov): Maybe optimize.
-  final Set<int> supertypeIdSet = new HashSet<int>();
-
-  /// Maps generic supertype classes to the instantiation implemented by this
-  /// class, or `null` if the class does not have generic supertypes.
-  ///
-  /// E.g. `List` maps to `List<String>` for a class that directly of indirectly
-  /// implements `List<String>`.
-  ///
-  /// However, the map may contain additional entries for classes that are not
-  /// supertypes of this class, so that a single map object can be shared
-  /// between different classes.  Lookups into the map should therefore be
-  /// guarded by a subtype check.
-  ///
-  /// For example:
-  ///
-  ///     class Q<T>
-  ///     class A<T>
-  ///
-  ///     class B extends A<String>
-  ///     class C extends B implements Q<int>
-  ///
-  /// In this case, a single map object `{A: A<String>, Q: Q<int>}` may be
-  /// shared by the classes `B` and `C`.
-  Map<Class, Supertype> genericSuperTypes;
-
-  /// If true, this is the current "owner" of [genericSuperTypes], meaning
-  /// we may add additional entries to the map or transfer ownership to another
-  /// class.
-  bool ownsGenericSuperTypeMap = true;
-
-  /// Instance fields, getters, methods, and operators declared in this class
-  /// or its mixed-in class, sorted according to [_compareMembers].
-  List<Member> declaredGettersAndCalls;
-
-  /// Non-final instance fields and setters declared in this class or its
-  /// mixed-in class, sorted according to [_compareMembers].
-  List<Member> declaredSetters;
-
-  /// Instance fields, getters, methods, and operators implemented by this class
-  /// (declared or inherited).
-  List<Member> implementedGettersAndCalls;
-
-  /// Non-final instance fields and setters implemented by this class
-  /// (declared or inherited).
-  List<Member> implementedSetters;
-
-  /// Instance fields, getters, methods, and operators declared in this class,
-  /// or its supertype, or interfaces, sorted according to [_compareMembers],
-  /// or `null` if it has not been computed yet.
-  List<Member> interfaceGettersAndCalls;
-
-  /// Non-final instance fields and setters declared in this class, or its
-  /// supertype, or interfaces, sorted according to [_compareMembers], or
-  /// `null` if it has not been computed yet.
-  List<Member> interfaceSetters;
-
-  _ClassInfo(this.id, this.node);
-
-  /// Return `true` if the [superInfo] corresponds to a supertype of this class.
-  bool isSubtypeOf(_ClassInfo superInfo) {
-    return supertypeIdSet.contains(superInfo.id);
-  }
-
-  @override
-  String toString() => node.toString();
-}
-
-/// Heap for use in computing least upper bounds.
-///
-/// The heap is sorted such that classes that are deepest in the hierarchy
-/// are removed first; in the case of ties, classes with lower unique
-/// identifiers removed first.
-class _LubHeap extends Heap<_ClassInfo> {
-  @override
-  bool sortsBefore(_ClassInfo a, _ClassInfo b) => sortsBeforeStatic(a, b);
-
-  static bool sortsBeforeStatic(_ClassInfo a, _ClassInfo b) {
-    if (a.depth > b.depth) return true;
-    if (a.depth < b.depth) return false;
-    return a.id < b.id;
-  }
-}
diff --git a/pkg/sourcemap_testing/lib/src/stacktrace_helper.dart b/pkg/sourcemap_testing/lib/src/stacktrace_helper.dart
index 4a989c2..e37478c 100644
--- a/pkg/sourcemap_testing/lib/src/stacktrace_helper.dart
+++ b/pkg/sourcemap_testing/lib/src/stacktrace_helper.dart
@@ -223,25 +223,25 @@
   if (verbose) {
     print('JavaScript stacktrace:');
     print(jsStackTrace.join('\n'));
-    print('Dart stacktrace:');
+    print('\nDart stacktrace:');
     print(dartStackTrace.join('\n'));
   }
   Expect.equals(
       expectedIndex,
       expectations.expectedLines.length,
       "Missing stack trace lines for test:\n${test.code}\n"
-      "Actual:\n${dartStackTrace.join('\n')}\n"
+      "Actual:\n${dartStackTrace.join('\n')}\n\n"
       "Expected:\n${expectations.expectedLines.join('\n')}\n");
   Expect.isTrue(
       unexpectedLines.isEmpty,
       "Unexpected stack trace lines for test:\n${test.code}\n"
-      "Actual:\n${dartStackTrace.join('\n')}\n"
+      "Actual:\n${dartStackTrace.join('\n')}\n\n"
       "Unexpected:\n${expectations.unexpectedLines.join('\n')}\n");
   Expect.isTrue(
       unexpectedBeforeLines.isEmpty && unexpectedAfterLines.isEmpty,
       "Unexpected stack trace lines:\n${test.code}\n"
-      "Actual:\n${dartStackTrace.join('\n')}\n"
-      "Unexpected before:\n${unexpectedBeforeLines.join('\n')}\n"
+      "Actual:\n${dartStackTrace.join('\n')}\n\n"
+      "Unexpected before:\n${unexpectedBeforeLines.join('\n')}\n\n"
       "Unexpected after:\n${unexpectedAfterLines.join('\n')}\n");
 
   if (forcedTmpDir == null) {
diff --git a/pkg/sourcemap_testing/lib/src/stepping_helper.dart b/pkg/sourcemap_testing/lib/src/stepping_helper.dart
index fad7ad9..7cd8a6b 100644
--- a/pkg/sourcemap_testing/lib/src/stepping_helper.dart
+++ b/pkg/sourcemap_testing/lib/src/stepping_helper.dart
@@ -123,13 +123,15 @@
     noBreaksEnd[stopNum2].add("test.dart:${annotation.lineNo}:");
   }
 
-  _checkRecordedStops(recordStops, expectedStops, noBreaksStart, noBreaksEnd);
+  _checkRecordedStops(
+      recordStops, expectedStops, noBreaksStart, noBreaksEnd, debug);
 
   for (Annotation annotation in code.annotations
       .where((annotation) => annotation.text.trim() == "nb")) {
     // Check that we didn't break where we're not allowed to.
     if (recordStopLines.contains(annotation.lineNo)) {
-      fail("Was not allowed to stop on line ${annotation.lineNo}, but did!");
+      fail("Was not allowed to stop on line ${annotation.lineNo}, but did!"
+          "  Actual line stops: $recordStopLines${_debugHint(debug)}");
     }
   }
   for (Annotation annotation in code.annotations
@@ -138,7 +140,8 @@
     if (recordStopLineColumns
         .contains("${annotation.lineNo}:${annotation.columnNo}")) {
       fail("Was not allowed to stop on line ${annotation.lineNo} "
-          "column ${annotation.columnNo}, but did!");
+          "column ${annotation.columnNo}, but did!"
+          "  Actual line stops: $recordStopLineColumns${_debugHint(debug)}");
     }
   }
 
@@ -147,8 +150,12 @@
   }
 }
 
-void _checkRecordedStops(List<String> recordStops, List<String> expectedStops,
-    List<List<String>> noBreaksStart, List<List<String>> noBreaksEnd) {
+void _checkRecordedStops(
+    List<String> recordStops,
+    List<String> expectedStops,
+    List<List<String>> noBreaksStart,
+    List<List<String>> noBreaksEnd,
+    bool debug) {
   // We want to find all expected lines in recorded lines in order, but allow
   // more in between in the recorded lines.
   // noBreaksStart and noBreaksStart gives instructions on what's *NOT* allowed
@@ -173,18 +180,48 @@
           noBreaksEnd[expectedIndex] != null) {
         aliveNoBreaks.removeAll(noBreaksEnd[expectedIndex]);
       }
-    } else if (aliveNoBreaks
-        .contains("${(recorded.split(":")..removeLast()).join(":")}:")) {
-      fail("Break '$recorded' was found when it wasn't allowed "
-          "(js step $stopNumber, after stop $expectedIndex)");
+    } else {
+      if (debug) {
+        // One of the most helpful debugging tools is to see stops that weren't
+        // matched. The most common failure is we didn't match one particular
+        // stop location (e.g. because of the column). This gets reported
+        // as an aliveNoBreaks failure (if the test is using no-breaks like
+        // `nbb`) or it's reported as "stops don't match" message.
+        //
+        // Both failures are difficult to debug without seeing the stops that
+        // didn't match. No breaks failures are misleading (the problem isn't
+        // an incorrect break, but we missed a stop, so the aliveNoBreaks is
+        // wrong), and the normal failure list dumps the enitre stop list,
+        // making it difficult to see where the mismatch was.
+        //
+        // Also we add 1 to expectedIndex, because the stop annotations are
+        // 1-based in the source files (e.g. `/*s:1*/` is expectedIndex 0)
+        print("Skipping stop `$recorded` that didn't match expected stop "
+            "${expectedIndex + 1} `${expectedStops[expectedIndex]}`");
+      }
+      if (aliveNoBreaks
+          .contains("${(recorded.split(":")..removeLast()).join(":")}:")) {
+        fail("Break '$recorded' was found when it wasn't allowed "
+            "(js step $stopNumber, after stop ${expectedIndex + 1}). "
+            "This can happen when an expected stop was not matched"
+            "${_debugHint(debug)}.");
+      }
     }
   }
   if (expectedIndex != expectedStops.length) {
     // Didn't find everything.
-    fail("Expected to find $expectedStops but found $recordStops");
+    fail("Expected to find $expectedStops but found $recordStops"
+        "${_debugHint(debug)}");
   }
 }
 
+/// If we're not in debug mode, this returns a message string with information
+/// about how to enable debug mode in the test runner.
+String _debugHint(bool debug) {
+  if (debug) return ''; // already in debug mode
+  return ' (pass -Ddebug=1 to the test runner to see debug information)';
+}
+
 void _debugPrint(List<_DartStackTraceDataEntry> trace, String outputPath) {
   StringBuffer sb = new StringBuffer();
   var jsFile =
diff --git a/pkg/vm/bin/dump_kernel.dart b/pkg/vm/bin/dump_kernel.dart
index 46f6680..fb2ad81 100644
--- a/pkg/vm/bin/dump_kernel.dart
+++ b/pkg/vm/bin/dump_kernel.dart
@@ -38,5 +38,6 @@
   final List<int> bytes = new File(input).readAsBytesSync();
   new BinaryBuilderWithMetadata(bytes).readProgram(program);
 
-  writeProgramToText(program, path: output, showMetadata: true);
+  writeProgramToText(program,
+      path: output, showExternal: true, showMetadata: true);
 }
diff --git a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
index 27d9a4b..95b11ce 100644
--- a/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
+++ b/pkg/vm/lib/transformations/no_dynamic_invocations_annotator.dart
@@ -24,9 +24,9 @@
 
   Selector(this.action, this.target);
 
-  Selector.invoke(Name target) : this(Action.invoke, target);
-  Selector.get(Name target) : this(Action.get, target);
-  Selector.set(Name target) : this(Action.set, target);
+  Selector.doInvoke(Name target) : this(Action.invoke, target);
+  Selector.doGet(Name target) : this(Action.get, target);
+  Selector.doSet(Name target) : this(Action.set, target);
 
   bool operator ==(other) {
     return other is Selector &&
@@ -83,7 +83,7 @@
       return;
     }
 
-    if (!_dynamicSelectors.contains(new Selector.set(node.name))) {
+    if (!_dynamicSelectors.contains(new Selector.doSet(node.name))) {
       _metadata.mapping[node] =
           const ProcedureAttributesMetadata.noDynamicInvocations();
     }
@@ -96,9 +96,9 @@
 
     Selector selector;
     if (node.kind == ProcedureKind.Method) {
-      selector = new Selector.invoke(node.name);
+      selector = new Selector.doInvoke(node.name);
     } else if (node.kind == ProcedureKind.Setter) {
-      selector = new Selector.set(node.name);
+      selector = new Selector.doSet(node.name);
     } else {
       return;
     }
@@ -124,7 +124,7 @@
     super.visitMethodInvocation(node);
 
     if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
-      dynamicSelectors.add(new Selector.invoke(node.name));
+      dynamicSelectors.add(new Selector.doInvoke(node.name));
     }
   }
 
@@ -133,7 +133,7 @@
     super.visitPropertyGet(node);
 
     if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
-      dynamicSelectors.add(new Selector.get(node.name));
+      dynamicSelectors.add(new Selector.doGet(node.name));
     }
   }
 
@@ -142,7 +142,7 @@
     super.visitPropertySet(node);
 
     if (node.dispatchCategory == DispatchCategory.dynamicDispatch) {
-      dynamicSelectors.add(new Selector.set(node.name));
+      dynamicSelectors.add(new Selector.doSet(node.name));
     }
   }
 }
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index d6dbafa..823a47e 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -340,6 +340,12 @@
     rebase_path("vm/version_in.cc", root_build_dir),
   ]
   if (!dart_version_git_info) {
-    args += [ "--ignore_svn_revision" ]
+    args += [ "--no_git_hash" ]
+  }
+  if (dart_custom_version_for_pub != "") {
+    args += [
+      "--custom_for_pub",
+      dart_custom_version_for_pub,
+    ]
   }
 }
diff --git a/runtime/runtime_args.gni b/runtime/runtime_args.gni
index 23a8c5e2..13751eb 100644
--- a/runtime/runtime_args.gni
+++ b/runtime/runtime_args.gni
@@ -56,4 +56,17 @@
   # Whether the Dart binary version string should include the git hash and
   # git commit time.
   dart_version_git_info = true
+
+  # When this argument is a non-empty string, the version repoted by the
+  # Dart VM will be one that is compatible with pub's interpretation of
+  # semantic version strings. The version string will also include the values
+  # of the argument. In particular the version string will read:
+  #
+  #     "M.m.p-dev.x.x-$(dart_custom_version_for_pub)-$(short_git_hash)"
+  #
+  # Where 'M', 'm', and 'p' are the major, minor and patch version numbers,
+  # and 'dev.x.x' is the dev version tag most recently preceeding the current
+  # revision. The short git hash can be omitted by setting
+  # dart_version_git_info=false
+  dart_custom_version_for_pub = ""
 }
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 53f44ca..1682003 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -373,6 +373,7 @@
       return i;
     }
   }
+  RELEASE_ASSERT(!function.IsNull());
   inlined_functions_.Add(function, Heap::kOld);
   return inlined_functions_.Length() - 1;
 }
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 474f6a8..5ccc59e 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -176,12 +176,13 @@
 // Object._haveSameRuntimeType(a, b).
 // Note: this optimization is not speculative.
 bool AotCallSpecializer::TryReplaceWithHaveSameRuntimeType(
-    InstanceCallInstr* call) {
-  const ICData& ic_data = *call->ic_data();
-  ASSERT(ic_data.NumArgsTested() == 2);
-
+    TemplateDartCall<0>* call) {
+  ASSERT((call->IsInstanceCall() &&
+          (call->AsInstanceCall()->ic_data()->NumArgsTested() == 2)) ||
+         call->IsStaticCall());
   ASSERT(call->type_args_len() == 0);
   ASSERT(call->ArgumentCount() == 2);
+
   Definition* left = call->ArgumentAt(0);
   Definition* right = call->ArgumentAt(1);
 
@@ -203,7 +204,6 @@
     InsertBefore(call, arg, NULL, FlowGraph::kEffect);
     args->Add(arg);
     const intptr_t kTypeArgsLen = 0;
-    ASSERT(call->type_args_len() == kTypeArgsLen);
     StaticCallInstr* static_call = new (Z) StaticCallInstr(
         call->token_pos(), have_same_runtime_type, kTypeArgsLen,
         Object::null_array(),  // argument_names
@@ -339,6 +339,16 @@
   return input;
 }
 
+// After replacing a call with a specialized instruction, make sure to
+// update types at all uses, as specialized instruction can provide a more
+// specific type.
+static void RefineUseTypes(Definition* instr) {
+  CompileType* new_type = instr->Type();
+  for (Value::Iterator it(instr->input_use_list()); !it.Done(); it.Advance()) {
+    it.Current()->RefineReachingType(new_type);
+  }
+}
+
 bool AotCallSpecializer::TryOptimizeInstanceCallUsingStaticTypes(
     InstanceCallInstr* instr) {
   ASSERT(I->strong() && FLAG_use_strong_mode_types);
@@ -481,6 +491,7 @@
                 instr->ToCString(), replacement->ToCString());
     }
     ReplaceCall(instr, replacement);
+    RefineUseTypes(replacement);
     return true;
   }
 
@@ -492,14 +503,19 @@
   ASSERT(I->strong() && FLAG_use_strong_mode_types);
   Definition* replacement = NULL;
 
+  const String& name = String::Handle(Z, instr->function().name());
+  const Token::Kind op_kind = MethodTokenRecognizer::RecognizeTokenKind(name);
+
+  if ((op_kind == Token::kEQ) && TryReplaceWithHaveSameRuntimeType(instr)) {
+    return true;
+  }
+
   const Class& owner = Class::Handle(Z, instr->function().Owner());
   if ((owner.id() != kIntegerCid) && (owner.id() != kDoubleCid)) {
     return false;
   }
 
   const intptr_t receiver_index = instr->FirstArgIndex();
-  const String& name = String::Handle(Z, instr->function().name());
-  const Token::Kind op_kind = MethodTokenRecognizer::RecognizeTokenKind(name);
 
   // Recognize double and int operators here as devirtualization can convert
   // instance calls of these operators into static calls.
@@ -668,6 +684,7 @@
                 instr->ToCString(), replacement->ToCString());
     }
     ReplaceCall(instr, replacement);
+    RefineUseTypes(replacement);
     return true;
   }
 
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.h b/runtime/vm/compiler/aot/aot_call_specializer.h
index 214d345..34a089a 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.h
+++ b/runtime/vm/compiler/aot/aot_call_specializer.h
@@ -42,7 +42,7 @@
   bool TryCreateICDataForUniqueTarget(InstanceCallInstr* call);
 
   bool RecognizeRuntimeTypeGetter(InstanceCallInstr* call);
-  bool TryReplaceWithHaveSameRuntimeType(InstanceCallInstr* call);
+  bool TryReplaceWithHaveSameRuntimeType(TemplateDartCall<0>* call);
 
   bool TryInlineFieldAccess(InstanceCallInstr* call);
   bool TryInlineFieldAccess(StaticCallInstr* call);
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 2ed402a..2362012 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -3926,7 +3926,13 @@
 
       case kUnboxedInt64: {
         ASSERT(FLAG_limit_ints_to_64_bits);
-        EmitLoadInt64FromBoxOrSmi(compiler);
+        if (value()->Type()->ToCid() == kSmiCid) {
+          // Smi -> int64 conversion is more efficient than
+          // handling arbitrary smi/mint.
+          EmitSmiConversion(compiler);
+        } else {
+          EmitLoadInt64FromBoxOrSmi(compiler);
+        }
         break;
       }
 
diff --git a/runtime/vm/compiler/backend/inliner.cc b/runtime/vm/compiler/backend/inliner.cc
index b888721..e3153f3 100644
--- a/runtime/vm/compiler/backend/inliner.cc
+++ b/runtime/vm/compiler/backend/inliner.cc
@@ -2235,6 +2235,7 @@
   // TODO(johnmccutchan): Do not allow IsNoSource once all nodes have proper
   // source positions.
   ASSERT(tp.IsReal() || tp.IsSynthetic() || tp.IsNoSource());
+  RELEASE_ASSERT(!function.IsNull());
   inline_id_to_function_->Add(&function);
   inline_id_to_token_pos_->Add(tp);
   caller_inline_id_->Add(parent_id);
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index daa2a9c..8100582 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -2752,6 +2752,9 @@
       case kTypeLiteral:
         EvaluateTypeLiteral();
         break;
+      case kAsExpression:
+        EvaluateAsExpression();
+        break;
       case kListLiteral:
       case kConstListLiteral:
         EvaluateListLiteralInternal();
@@ -3202,6 +3205,40 @@
   }
 }
 
+void StreamingConstantEvaluator::EvaluateAsExpression() {
+  builder_->ReadPosition();
+  const uint8_t flags = builder_->ReadFlags();
+  const bool is_type_error = (flags & (1 << 0)) != 0;
+
+  // Check that this AsExpression was inserted by the front-end.
+  if (!is_type_error) {
+    H.ReportError(
+        script_, TokenPosition::kNoSource,
+        "explicit as operator is not permitted in constant expression");
+  }
+
+  EvaluateExpression(builder_->ReaderOffset(), false);
+
+  const AbstractType& type = T.BuildType();
+  if (!type.IsInstantiated() || type.IsMalformed()) {
+    H.ReportError(
+        script_, TokenPosition::kNoSource,
+        "Not a constant expression: right hand side of an implicit "
+        "as-expression is expected to be an instantiated type, got %s",
+        type.ToCString());
+  }
+
+  const TypeArguments& instantiator_type_arguments = TypeArguments::Handle();
+  const TypeArguments& function_type_arguments = TypeArguments::Handle();
+  Error& error = Error::Handle();
+  if (!result_.IsInstanceOf(type, instantiator_type_arguments,
+                            function_type_arguments, &error)) {
+    H.ReportError(script_, TokenPosition::kNoSource,
+                  "Not a constant expression: %s is not an instance of %s",
+                  result_.ToCString(), type.ToCString());
+  }
+}
+
 void StreamingConstantEvaluator::EvaluateConditionalExpression() {
   bool condition = EvaluateBooleanExpressionHere();
   if (condition) {
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index 04c2b31..e51a939 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -850,6 +850,7 @@
 
  private:
   bool IsAllowedToEvaluate();
+  void EvaluateAsExpression();
   void EvaluateVariableGet();
   void EvaluateVariableGet(uint8_t payload);
   void EvaluatePropertyGet();
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index 4270fdc..21f69c0 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -29,8 +29,8 @@
         children_head(NULL),
         children_tail(NULL),
         children_next(NULL) {
-    ASSERT(!function.IsNull());
-    ASSERT(function.IsNotTemporaryScopedHandle());
+    RELEASE_ASSERT(!function.IsNull());
+    RELEASE_ASSERT(function.IsNotTemporaryScopedHandle());
   }
 
   void AppendChild(InliningNode* child) {
@@ -63,7 +63,7 @@
       temp_(0) {}
 
 intptr_t Dwarf::AddCode(const Code& code) {
-  ASSERT(!code.IsNull());
+  RELEASE_ASSERT(!code.IsNull());
   CodeIndexPair* pair = code_to_index_.Lookup(&code);
   if (pair != NULL) {
     return pair->index_;
@@ -104,7 +104,7 @@
 }
 
 intptr_t Dwarf::AddScript(const Script& script) {
-  ASSERT(!script.IsNull());
+  RELEASE_ASSERT(!script.IsNull());
   ScriptIndexPair* pair = script_to_index_.Lookup(&script);
   if (pair != NULL) {
     return pair->index_;
@@ -118,7 +118,7 @@
 }
 
 intptr_t Dwarf::LookupFunction(const Function& function) {
-  ASSERT(!function.IsNull());
+  RELEASE_ASSERT(!function.IsNull());
   FunctionIndexPair* pair = function_to_index_.Lookup(&function);
   if (pair == NULL) {
     FATAL1("Function detected too late during DWARF generation: %s",
@@ -128,7 +128,7 @@
 }
 
 intptr_t Dwarf::LookupScript(const Script& script) {
-  ASSERT(!script.IsNull());
+  RELEASE_ASSERT(!script.IsNull());
   ScriptIndexPair* pair = script_to_index_.Lookup(&script);
   if (pair == NULL) {
     FATAL1("Script detected too late during DWARF generation: %s",
@@ -308,6 +308,7 @@
   Script& script = Script::Handle(zone_);
   for (intptr_t i = 0; i < codes_.length(); i++) {
     const Code& code = *(codes_[i]);
+    RELEASE_ASSERT(!code.IsNull());
     if (!code.IsFunctionCode()) {
       continue;
     }
@@ -353,6 +354,9 @@
   }
   const Array& functions = Array::Handle(zone_, code.inlined_id_to_function());
   const Function& root_function = Function::ZoneHandle(zone_, code.function());
+  if (root_function.IsNull()) {
+    FATAL1("Wherefore art thou functionless code, %s?\n", code.ToCString());
+  }
 
   GrowableArray<InliningNode*> node_stack(zone_, 4);
   GrowableArray<TokenPosition> token_positions(zone_, 4);
diff --git a/runtime/vm/os_thread.cc b/runtime/vm/os_thread.cc
index bcc7115..e2ca88b 100644
--- a/runtime/vm/os_thread.cc
+++ b/runtime/vm/os_thread.cc
@@ -62,6 +62,12 @@
 }
 
 OSThread::~OSThread() {
+  if (!is_os_thread()) {
+    // If the embedder enters an isolate on this thread and does not exit the
+    // isolate, the thread local at thread_key_, which we are destructing here,
+    // will contain a dart::Thread instead of a dart::OSThread.
+    FATAL("Thread exited without calling Dart_ExitIsolate");
+  }
   RemoveThreadFromList(this);
   delete log_;
   log_ = NULL;
diff --git a/sdk/lib/_internal/js_runtime/lib/js_number.dart b/sdk/lib/_internal/js_runtime/lib/js_number.dart
index 113eab5..47ce41d 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_number.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_number.dart
@@ -433,11 +433,17 @@
 
   int get bitLength {
     int nonneg = this < 0 ? -this - 1 : this;
-    if (nonneg >= 0x100000000) {
+    int wordBits = 32;
+    while (nonneg >= 0x100000000) {
       nonneg = nonneg ~/ 0x100000000;
-      return _bitCount(_spread(nonneg)) + 32;
+      wordBits += 32;
     }
-    return _bitCount(_spread(nonneg));
+    return wordBits - _clz32(nonneg);
+  }
+
+  static int _clz32(int uint32) {
+    // TODO(sra): Use `Math.clz32(uint32)` (not available on IE11).
+    return 32 - _bitCount(_spread(uint32));
   }
 
   // Returns pow(this, e) % m.
@@ -592,9 +598,9 @@
     return (i & 0x0000003F);
   }
 
-  static _shru(int value, int shift) => JS('int', '# >>> #', value, shift);
-  static _shrs(int value, int shift) => JS('int', '# >> #', value, shift);
-  static _ors(int a, int b) => JS('int', '# | #', a, b);
+  static int _shru(int value, int shift) => JS('int', '# >>> #', value, shift);
+  static int _shrs(int value, int shift) => JS('int', '# >> #', value, shift);
+  static int _ors(int a, int b) => JS('int', '# | #', a, b);
 
   // Assumes i is <= 32-bit
   static int _spread(int i) {
diff --git a/tests/compiler/dart2js/memory_compiler.dart b/tests/compiler/dart2js/memory_compiler.dart
index 25e5600..98c0651 100644
--- a/tests/compiler/dart2js/memory_compiler.dart
+++ b/tests/compiler/dart2js/memory_compiler.dart
@@ -263,7 +263,8 @@
   }
 
   @override
-  void forEachLibrary(void f(l)) => copiedLibraries.values.forEach(f);
+  void forEachLibrary(void f(LibraryEntity l)) =>
+      copiedLibraries.values.forEach((l) => f(l));
 
   @override
   getLibrary(Uri uri) => copiedLibraries[uri];
diff --git a/tests/corelib_2/corelib_2.status b/tests/corelib_2/corelib_2.status
index 99f9267..400410a 100644
--- a/tests/corelib_2/corelib_2.status
+++ b/tests/corelib_2/corelib_2.status
@@ -16,8 +16,7 @@
 bool_from_environment2_test/03: Crash
 int_modulo_arith_test/modPow: RuntimeError
 int_modulo_arith_test/none: RuntimeError
-null_nosuchmethod_test/01: CompileTimeError
-null_nosuchmethod_test/none: CompileTimeError
+null_nosuchmethod_test/01: RuntimeError # Issue 32088
 string_from_environment3_test/03: Crash
 
 [ $compiler == precompiler ]
@@ -137,6 +136,7 @@
 
 [ $compiler == dart2js && $runtime != none && !$checked && !$dart2js_with_kernel ]
 growable_list_test: RuntimeError # Concurrent modifications test always runs
+null_nosuchmethod_test: RuntimeError # Like many tests, fails if type checks are skipped.
 splay_tree_from_iterable_test: RuntimeError
 string_split_test/checkedstore: RuntimeError # Issue 30548: does not check stores into List<String>
 
@@ -231,8 +231,6 @@
 list_test/01: RuntimeError
 list_test/none: RuntimeError
 nan_infinity_test/01: RuntimeError
-null_nosuchmethod_test/01: CompileTimeError
-null_nosuchmethod_test/none: CompileTimeError
 queue_test: Crash # 'file:*/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart': Failed assertion: line 391 pos 16: 'receiver.nonCheck() == user.inputs[1].nonCheck()': is not true.
 splay_tree_from_iterable_test: RuntimeError
 stacktrace_fromstring_test: Crash # Assertion failure: Runtime type information not available for type_variable_local(bindCallback.R) in (local(_RootZone.bindCallback#)) for j:closure_call(_RootZone_bindCallback_closure.call).
@@ -268,8 +266,6 @@
 list_test/01: RuntimeError
 list_test/none: RuntimeError
 nan_infinity_test/01: RuntimeError
-null_nosuchmethod_test/01: CompileTimeError
-null_nosuchmethod_test/none: CompileTimeError
 queue_test: RuntimeError
 splay_tree_from_iterable_test: RuntimeError
 stacktrace_fromstring_test: Crash # Assertion failure: Runtime type information not available for type_variable_local(bindCallback.R) in (local(_RootZone.bindCallback#)) for j:closure_call(_RootZone_bindCallback_closure.call).
@@ -282,6 +278,7 @@
 
 [ $compiler == dart2js && $dart2js_with_kernel && $strong ]
 map_unmodifiable_cast_test: Crash
+null_nosuchmethod_test: RuntimeError
 
 [ $compiler == dart2js && $dart2js_with_kernel && !$strong ]
 *: SkipByDesign
@@ -331,6 +328,7 @@
 
 [ $compiler == dartdevc && $runtime != none ]
 compare_to2_test: CompileTimeError # invalid test
+null_nosuchmethod_test/01: RuntimeError # Issue 32088
 symbol_operator_test: RuntimeError # Issue 29921
 
 [ $compiler != dartdevc && $compiler != dartdevk && $checked && !$strong ]
@@ -341,9 +339,6 @@
 string_replace_static_test: MissingCompileTimeError
 string_static_test: MissingCompileTimeError
 
-[ $compiler != dartdevc && $runtime != none && !$checked && !$strong ]
-null_nosuchmethod_test: RuntimeError # needs Dart 2 or checked mode
-
 # Enabling of dartk for sim{arm,arm64,dbc64} revelaed these test failures, which
 # are to be triaged.  Isolate tests are skipped on purpose due to the usage of
 # batch mode.
@@ -358,8 +353,6 @@
 iterable_fold_test/02: RuntimeError
 iterable_reduce_test/01: CompileTimeError # Issue 31533
 iterable_reduce_test/none: RuntimeError
-null_nosuchmethod_test/01: CompileTimeError # Issue 31402 (Invocation arguments)
-null_nosuchmethod_test/none: CompileTimeError # Issue 31402 (Invocation arguments)
 symbol_operator_test/03: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
 symbol_reserved_word_test/06: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
 symbol_reserved_word_test/09: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
@@ -373,8 +366,6 @@
 iterable_fold_test/02: RuntimeError
 iterable_reduce_test/01: CompileTimeError # Issue 31533
 iterable_reduce_test/none: RuntimeError
-null_nosuchmethod_test/01: CompileTimeError # Issue 31402 (Invocation arguments)
-null_nosuchmethod_test/none: CompileTimeError # Issue 31402 (Invocation arguments)
 regexp/stack-overflow_test: RuntimeError
 symbol_operator_test/03: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
 symbol_reserved_word_test/06: RuntimeError # Issues 11669 and 31936 - throwing const constructors.
@@ -453,7 +444,6 @@
 list_removeat_test: RuntimeError # Issue 29921
 main_test: RuntimeError # Issue 29921
 nan_infinity_test/01: RuntimeError # Issue 29921
-null_nosuchmethod_test/01: RuntimeError # DDC checks type before too many arguments, so TypeError instead of NSM
 regexp/alternative-length-miscalculation_test: RuntimeError # Issue 29921
 regexp/ascii-regexp-subject_test: RuntimeError # Issue 29921
 regexp/bol-with-multiline_test: RuntimeError # Issue 29921
diff --git a/tests/corelib_2/null_nosuchmethod_test.dart b/tests/corelib_2/null_nosuchmethod_test.dart
index eefcf30..37ff417 100644
--- a/tests/corelib_2/null_nosuchmethod_test.dart
+++ b/tests/corelib_2/null_nosuchmethod_test.dart
@@ -12,21 +12,23 @@
 }
 
 main() {
-  var x;
+  dynamic x;
   // Non-existing method calls noSuchMethod.
   Expect.throwsNoSuchMethodError(() => x.foo());
 
+  var invocation = InvocationFactory.instance.foo;
+
   // Calling noSuchMethod directly.
-  Expect.throwsNoSuchMethodError(() => x.noSuchMethod("foo", []));
+  Expect.throwsNoSuchMethodError(() => x.noSuchMethod(invocation, []));
 
   // Closurizing noSuchMethod and calling it.
-  var nsm = x.noSuchMethod;
+  dynamic nsm = x.noSuchMethod;
   Expect.notEquals(null, nsm);
   Expect.throwsTypeError(() => nsm("foo"));
 
-  var i = InvocationFactory.instance.foo;
-  Expect.throwsNoSuchMethodError(() => nsm(i));
-  Expect.throwsNoSuchMethodError(() => nsm(i, [])); // wrong number of args
+  Expect.throwsNoSuchMethodError(() => nsm(invocation));
+  Expect.throwsNoSuchMethodError(
+      () => nsm(invocation, [])); // wrong number of args
 
   // Wrong number and type of arguments.
   Expect.throwsNoSuchMethodError(() => nsm("foo", [])); //# 01: ok
diff --git a/tests/language_2/const_constructor2_test.dart b/tests/language_2/const_constructor2_test.dart
index facc6d5..6d782ce 100644
--- a/tests/language_2/const_constructor2_test.dart
+++ b/tests/language_2/const_constructor2_test.dart
@@ -28,9 +28,9 @@
 }
 
 class E {
-  const factory E.redirecting1(var a) = F<int>;
-  const factory E.redirecting2(var a) = F<int>.redirecting;
-  const factory E.redirecting3(var a) = F<double>.redirecting;
+  const factory E.redirecting1(int a) = F<int>;
+  const factory E.redirecting2(int a) = F<int>.redirecting;
+  const factory E.redirecting3(double a) = F<double>.redirecting;
 }
 
 class F<V> implements E {
diff --git a/tests/language_2/covariant_subtyping_test.dart b/tests/language_2/covariant_subtyping_test.dart
index af5118e..e0ff19b 100644
--- a/tests/language_2/covariant_subtyping_test.dart
+++ b/tests/language_2/covariant_subtyping_test.dart
@@ -77,8 +77,9 @@
   Expect.throwsTypeError(() => cObj.setterForT());
   Expect.throwsTypeError(() => (cObj.setterForT() as F<Object>));
   FnChecks<dynamic> cDyn = cInt;
-  cDyn.setterForT(); // allowed fuzzy arrow
-  Expect.throwsTypeError(() => cDyn.setterForT()('hi')); // dcall throws
+  Expect.throwsTypeError(() => cDyn.setterForT());
+  Expect.throwsTypeError(
+      () => (cDyn as dynamic).setterForT()('hi')); // dcall throws
   cInt.setterForT()(42);
   Expect.equals(cObj.getT(), 42);
 }
@@ -106,8 +107,8 @@
   FnChecks<String> cStr = c;
   cStr.f('hi');
   FnChecks<dynamic> cDyn = c;
-  cDyn.f; // allowed fuzzy arrow
-  Expect.throwsTypeError(() => cDyn.f(42)); // dcall throws
+  Expect.throwsTypeError(() => cDyn.f);
+  Expect.throwsTypeError(() => (cDyn as dynamic).f(42)); // dcall throws
 }
 
 testFieldOfGenericFunctionType() {
diff --git a/tests/language_2/function_subtype_cast0_test.dart b/tests/language_2/function_subtype_cast0_test.dart
index d5dc206..f93ffc8 100644
--- a/tests/language_2/function_subtype_cast0_test.dart
+++ b/tests/language_2/function_subtype_cast0_test.dart
@@ -13,7 +13,7 @@
 void bar(int i) {}
 
 void main() {
-  Expect.isNotNull(bar as Foo);
+  Expect.throws(() => bar as Foo);
   Expect.throws(() => bar as Foo<bool>);
   Expect.isNotNull(bar as Foo<int>);
   Expect.isNotNull(bar as Bar);
diff --git a/tests/language_2/instantiate_type_variable_test.dart b/tests/language_2/instantiate_type_variable_test.dart
index cfb52db..5bf7f76 100644
--- a/tests/language_2/instantiate_type_variable_test.dart
+++ b/tests/language_2/instantiate_type_variable_test.dart
@@ -7,7 +7,7 @@
 class Foo<T> {
   Foo() {}
   T make() {
-    return new T(); //# 01: runtime error
+    return new T(); //# 01: compile-time error
   }
 }
 
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index 325899a..92ce014 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -126,7 +126,6 @@
 script2_negative_test: CompileTimeError
 setter_declaration2_negative_test: CompileTimeError
 setter_declaration_negative_test: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
 source_self_negative_test: CompileTimeError
 static_call_wrong_argument_count_negative_test: Fail # Issue 12156
 string_escape4_negative_test: CompileTimeError
@@ -1126,6 +1125,7 @@
 const_types_test/14: MissingCompileTimeError # Incorrectly allows using type parameter in const expression.
 const_types_test/15: MissingCompileTimeError # Incorrectly allows using type parameter in const expression.
 constant_type_literal_test/01: MissingCompileTimeError # Issue 28823
+default_implementation2_test: CompileTimeError # Issue 30855
 error_stacktrace_test/00: Pass
 field3a_negative_test: StaticWarning # Issue 28823
 forwarding_stub_tearoff_test: CompileTimeError
@@ -1135,7 +1135,6 @@
 generic_no_such_method_dispatcher_test: CompileTimeError
 generic_tearoff_test: CompileTimeError
 import_core_prefix_test: CompileTimeError # "dynamic" should be defined in core.
-instantiate_type_variable_test/01: CompileTimeError
 interceptor6_test: CompileTimeError
 issue13673_test: StaticWarning # Issue 31925
 issue15606_test/none: CompileTimeError # invalid use of void for dart 2
@@ -1156,6 +1155,7 @@
 mixin_super_2_test/03: MissingCompileTimeError
 mixin_supertype_subclass_test/02: MissingCompileTimeError
 mixin_supertype_subclass_test/05: MissingCompileTimeError
+mixin_type_parameter_inference_test/11: MissingCompileTimeError # Fuzzy arrow, 32114
 multiline_newline_test/01: CompileTimeError
 multiline_newline_test/01r: CompileTimeError
 multiline_newline_test/02: CompileTimeError
@@ -1419,7 +1419,7 @@
 import_self_test/01: MissingCompileTimeError
 inferrer_constructor5_test/01: MissingCompileTimeError
 initializing_formal_type_test: MissingCompileTimeError
-instantiate_type_variable_test/01: StaticWarning
+instantiate_type_variable_test/01: MissingCompileTimeError
 interceptor6_test: StaticWarning
 invalid_cast_test/01: MissingCompileTimeError
 invalid_cast_test/02: MissingCompileTimeError
@@ -1612,7 +1612,7 @@
 return_type_test: MissingCompileTimeError
 rewrite_implicit_this_test/01: MissingCompileTimeError
 setter4_test: MissingCompileTimeError
-setter_no_getter_call_test/01: StaticWarning
+setter_no_getter_call_test/01: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
 setter_override_test/02: MissingCompileTimeError
 string_interpolation_test/01: MissingCompileTimeError
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index e525517..864fec3 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -83,6 +83,7 @@
 function_subtype_bound_closure7_test: RuntimeError
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
+function_subtype_cast0_test: RuntimeError
 function_subtype_cast1_test: RuntimeError
 function_subtype_cast2_test: RuntimeError
 function_subtype_cast3_test: RuntimeError
@@ -198,6 +199,7 @@
 function_subtype_bound_closure7_test: RuntimeError
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
+function_subtype_cast0_test: RuntimeError
 function_subtype_cast1_test: RuntimeError
 function_subtype_cast2_test: RuntimeError
 function_subtype_cast3_test: RuntimeError
@@ -916,7 +918,6 @@
 built_in_identifier_type_annotation_test/66: MissingCompileTimeError # Issue 28815
 built_in_identifier_type_annotation_test/67: MissingCompileTimeError # Issue 28815
 built_in_identifier_type_annotation_test/68: MissingCompileTimeError # Issue 28815
-callable_test/none: RuntimeError
 call_method_as_cast_test/01: RuntimeError
 call_method_as_cast_test/02: RuntimeError
 call_method_as_cast_test/03: RuntimeError
@@ -932,15 +933,16 @@
 call_method_is_check_test/04: RuntimeError
 call_method_is_check_test/05: RuntimeError
 call_with_no_such_method_test: RuntimeError
+callable_test/none: RuntimeError
 checked_setter2_test: RuntimeError # Issue 31128
 checked_setter_test: RuntimeError # Issue 31128
 extract_type_arguments_test: Crash # Issue 31371
 function_propagation_test: RuntimeError
-function_type_call_getter2_test/none: RuntimeError
 function_type_call_getter2_test/00: MissingCompileTimeError
 function_type_call_getter2_test/01: MissingCompileTimeError
 function_type_call_getter2_test/02: MissingCompileTimeError
 function_type_call_getter2_test/03: MissingCompileTimeError
+function_type_call_getter2_test/none: RuntimeError
 generic_test/01: MissingCompileTimeError # front end does not validate `extends`
 implicit_downcast_during_constructor_invocation_test: RuntimeError
 implicit_downcast_during_for_in_element_test: RuntimeError
@@ -1694,7 +1696,6 @@
 instantiate_tearoff_after_contravariance_check_test: Crash # Assertion failure: kind=special,memberName=instantiate,callStructure:CallStructure(arity=0, types=1)
 instantiate_tearoff_of_call_test: CompileTimeError
 instantiate_tearoff_test: Crash # Assertion failure: kind=special,memberName=instantiate,callStructure:CallStructure(arity=0, types=1)
-instantiate_type_variable_test/01: CompileTimeError
 int64_literal_test/01: RuntimeError
 int64_literal_test/02: RuntimeError
 int64_literal_test/03: MissingCompileTimeError
@@ -1984,7 +1985,6 @@
 regress_32012_test: Crash # Unsupported operation: Unsupported type parameter type node B.
 runtime_type_function_test: RuntimeError
 setter4_test: MissingCompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
 setter_no_getter_test/01: CompileTimeError
 setter_override_test/00: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
@@ -2111,8 +2111,8 @@
 void_type_usage_test/conditional2_dynamic_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_for: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_literal_list_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
-void_type_usage_test/conditional2_literal_map_value_init2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_literal_map_value_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
+void_type_usage_test/conditional2_literal_map_value_init2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_null_equals2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_parens: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional2_return: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
@@ -2128,8 +2128,8 @@
 void_type_usage_test/conditional3_dynamic_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_for: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_literal_list_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
-void_type_usage_test/conditional3_literal_map_value_init2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_literal_map_value_init: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
+void_type_usage_test/conditional3_literal_map_value_init2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_null_equals2: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_parens: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
 void_type_usage_test/conditional3_return: Crash # 'package:front_end/src/fasta/type_inference/type_schema_environment.dart': Failed assertion: line 214 pos 12: 'false': is not true.
@@ -2264,9 +2264,6 @@
 config_import_test: RuntimeError
 const_constructor2_test/11: CompileTimeError
 const_constructor2_test/12: CompileTimeError
-const_constructor2_test/20: MissingCompileTimeError
-const_constructor2_test/22: MissingCompileTimeError
-const_constructor2_test/24: MissingCompileTimeError
 const_constructor3_test/03: CompileTimeError
 const_dynamic_type_literal_test/02: MissingCompileTimeError
 const_evaluation_test/01: CompileTimeError
@@ -2376,7 +2373,6 @@
 function_subtype_bound_closure7_test: RuntimeError
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
-function_subtype_cast0_test: RuntimeError
 function_subtype_cast1_test: RuntimeError
 function_subtype_checked0_test: RuntimeError
 function_subtype_closure0_test: RuntimeError
@@ -2454,7 +2450,6 @@
 initializing_formal_type_annotation_test/02: MissingCompileTimeError
 instance_creation_in_function_annotation_test: CompileTimeError
 instantiate_tearoff_of_call_test: CompileTimeError
-instantiate_type_variable_test/01: CompileTimeError
 int64_literal_test/01: RuntimeError
 int64_literal_test/02: RuntimeError
 int64_literal_test/03: MissingCompileTimeError
@@ -2748,7 +2743,6 @@
 regress_32012_test: Crash # Issue 32078
 runtime_type_function_test: RuntimeError
 setter4_test: MissingCompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
 setter_no_getter_test/01: CompileTimeError
 setter_override_test/00: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
@@ -3286,6 +3280,7 @@
 instantiate_tearoff_after_contravariance_check_test: RuntimeError
 instantiate_tearoff_of_call_test: RuntimeError
 instantiate_tearoff_test: RuntimeError
+instantiate_type_variable_test/01: MissingCompileTimeError
 int64_literal_test/*: Skip # This is testing Dart 2.0 int64 semantics.
 integer_division_by_zero_test: RuntimeError # Issue 8301
 interface_test/00: MissingCompileTimeError
@@ -3647,6 +3642,7 @@
 scope_variable_test/01: MissingCompileTimeError # Issue 13016
 setter4_test: CompileTimeError # issue 13639
 setter4_test: MissingCompileTimeError
+setter_no_getter_call_test/01: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
 setter_override_test/02: MissingCompileTimeError
 stacktrace_demangle_ctors_test: Fail # dart2js stack traces are not always compliant, issue 12698
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index c86380f..b300a43 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -49,6 +49,7 @@
 const_types_test/14: MissingCompileTimeError
 const_types_test/15: MissingCompileTimeError
 constant_type_literal_test/01: MissingCompileTimeError # DDC allows type parameter type literals in const expressions.
+default_implementation2_test: CompileTimeError # Issue 30855
 emit_const_fields_test: CompileTimeError
 enum_syntax_test/05: MissingCompileTimeError
 enum_syntax_test/06: MissingCompileTimeError
@@ -101,15 +102,20 @@
 mixin_supertype_subclass_test/05: MissingCompileTimeError
 mixin_type_parameter_inference_previous_mixin_test/01: CompileTimeError
 mixin_type_parameter_inference_previous_mixin_test/02: CompileTimeError
+mixin_type_parameter_inference_previous_mixin_test/02: RuntimeError # should be CompileTimeError, fuzzy arrows
+mixin_type_parameter_inference_previous_mixin_test/03: MissingCompileTimeError # fuzzy arrows
 mixin_type_parameter_inference_previous_mixin_test/04: MissingCompileTimeError
 mixin_type_parameter_inference_previous_mixin_test/05: RuntimeError
 mixin_type_parameter_inference_test/01: CompileTimeError
-mixin_type_parameter_inference_test/02: CompileTimeError
-mixin_type_parameter_inference_test/03: CompileTimeError
+mixin_type_parameter_inference_test/02: RuntimeError # CompileTimeError, except Fuzzy arrows
+mixin_type_parameter_inference_test/03: RuntimeError # CompileTimeError, except Fuzzy arrows
+mixin_type_parameter_inference_test/04: MissingCompileTimeError
+mixin_type_parameter_inference_test/05: MissingCompileTimeError
 mixin_type_parameter_inference_test/06: MissingCompileTimeError
 mixin_type_parameter_inference_test/07: MissingCompileTimeError
 mixin_type_parameter_inference_test/08: RuntimeError
 mixin_type_parameter_inference_test/09: RuntimeError
+mixin_type_parameter_inference_test/11: MissingCompileTimeError
 mock_writable_final_private_field_test: CompileTimeError # Issue 30848
 multiline_newline_test/01: CompileTimeError
 multiline_newline_test/01r: CompileTimeError
@@ -341,9 +347,6 @@
 conditional_import_string_test: CompileTimeError # Test is broken
 conditional_import_test: CompileTimeError # Test is broken
 config_import_test: CompileTimeError
-const_constructor2_test/20: MissingCompileTimeError
-const_constructor2_test/22: MissingCompileTimeError
-const_constructor2_test/24: MissingCompileTimeError
 const_constructor3_test/04: MissingCompileTimeError
 const_constructor_nonconst_field_test/01: MissingCompileTimeError
 const_dynamic_type_literal_test/02: MissingCompileTimeError
@@ -402,7 +405,6 @@
 field_override_test/00: MissingCompileTimeError
 field_override_test/01: MissingCompileTimeError
 function_propagation_test: RuntimeError
-function_subtype_cast0_test: RuntimeError # CastError: Casting value of type '(int) => void' to type '(dynamic) => void' which is incompatible
 function_subtype_closure0_test: RuntimeError # Expect.throws(TypeError) fails: Did not throw
 function_type_call_getter2_test/00: MissingCompileTimeError
 function_type_call_getter2_test/01: MissingCompileTimeError
@@ -434,7 +436,6 @@
 initializing_formal_type_annotation_test/02: MissingCompileTimeError
 instance_call_wrong_argument_count_negative_test: Fail
 instantiate_tearoff_of_call_test: CompileTimeError
-instantiate_type_variable_test/01: CompileTimeError
 invocation_mirror_test: CompileTimeError # Issue 31402 Error: A value of type 'dart.core::int' can't be assigned to a variable of type 'dart.core::Invocation'.
 issue18628_2_test/01: MissingCompileTimeError
 issue31596_override_test/07: MissingCompileTimeError
@@ -630,7 +631,6 @@
 regress_30339_test: RuntimeError # Uncaught Expect.isTrue(false) fails.
 runtime_type_function_test: RuntimeError # Expect.fail('Type print string does not match expectation
 setter4_test: MissingCompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
 setter_no_getter_test/01: CompileTimeError
 setter_override_test/00: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
@@ -693,10 +693,6 @@
 void_type_usage_test/conditional_return_to_void: MissingCompileTimeError, Crash
 wrong_number_type_arguments_test/01: MissingCompileTimeError
 
-[ $compiler == dartdevc && $runtime == none ]
-instantiate_type_variable_test/01: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
-
 [ $compiler == dartdevc && $runtime != none ]
 assertion_test: RuntimeError # Issue 30326; Expect.equals(expected: <1>, actual: <0>) fails.
 async_star_test/01: RuntimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index ab0002a..fe5e5a7 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -638,14 +638,11 @@
 conditional_import_test: CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
 config_import_corelib_test: CompileTimeError # Issue 31533
 config_import_test: RuntimeError # KernelVM bug: Configurable imports.
-const_constructor2_test/11: CompileTimeError # Issue 31402 (Invocation arguments)
-const_constructor2_test/12: CompileTimeError # Issue 31402 (Invocation arguments)
 const_dynamic_type_literal_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
 const_list_test: RuntimeError
 const_locals_test: RuntimeError
 const_map4_test: RuntimeError
 const_nested_test: RuntimeError # KernelVM bug: Constant evaluation.
-const_redirecting_factory_test: CompileTimeError # Issue 31402 (Field declaration)
 const_string_test: RuntimeError
 constructor12_test: RuntimeError
 constructor3_test: Fail, OK, Pass
@@ -708,7 +705,6 @@
 function_subtype_call0_test: RuntimeError, OK
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
-function_subtype_cast0_test: RuntimeError
 function_subtype_inline2_test: RuntimeError
 function_type2_test: RuntimeError
 function_type_alias6_test/none: RuntimeError
@@ -1024,14 +1020,11 @@
 conditional_import_test: CompileTimeError # KernelVM bug: Deferred loading kernel issue 30273.
 config_import_corelib_test: CompileTimeError # Issue 31533
 config_import_test: RuntimeError # KernelVM bug: Configurable imports.
-const_constructor2_test/11: CompileTimeError # Issue 31402 (Invocation arguments)
-const_constructor2_test/12: CompileTimeError # Issue 31402 (Invocation arguments)
 const_dynamic_type_literal_test/02: RuntimeError # KernelVM bug: Constant map duplicated key.
 const_evaluation_test: SkipByDesign
 const_list_test: RuntimeError
 const_map4_test: RuntimeError
 const_nested_test: RuntimeError # KernelVM bug: Constant evaluation.
-const_redirecting_factory_test: CompileTimeError # Issue 31402 (Field declaration)
 constructor12_test: RuntimeError
 constructor3_test: Fail, OK, Pass
 ct_const2_test: Skip # Incompatible flag: --compile_all
@@ -1103,7 +1096,6 @@
 function_subtype_call0_test: RuntimeError, OK
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
-function_subtype_cast0_test: RuntimeError
 function_subtype_inline2_test: RuntimeError
 function_type2_test: RuntimeError
 function_type_alias6_test/none: RuntimeError
diff --git a/tests/language_2/language_2_precompiled.status b/tests/language_2/language_2_precompiled.status
index 56a3d0d..55727a6 100644
--- a/tests/language_2/language_2_precompiled.status
+++ b/tests/language_2/language_2_precompiled.status
@@ -214,15 +214,6 @@
 conditional_property_increment_decrement_test/40: MissingCompileTimeError
 const_constructor2_test/05: MissingCompileTimeError
 const_constructor2_test/06: MissingCompileTimeError
-const_constructor2_test/13: MissingCompileTimeError
-const_constructor2_test/14: MissingCompileTimeError
-const_constructor2_test/15: MissingCompileTimeError
-const_constructor2_test/16: MissingCompileTimeError
-const_constructor2_test/17: MissingCompileTimeError
-const_constructor2_test/18: MissingCompileTimeError
-const_constructor2_test/20: MissingCompileTimeError
-const_constructor2_test/22: MissingCompileTimeError
-const_constructor2_test/24: MissingCompileTimeError
 const_constructor3_test/02: MissingCompileTimeError
 const_constructor3_test/04: MissingCompileTimeError
 const_dynamic_type_literal_test/02: MissingCompileTimeError
@@ -371,6 +362,7 @@
 function_subtype_bound_closure7_test: RuntimeError
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
+function_subtype_cast0_test: RuntimeError
 function_subtype_cast1_test: RuntimeError
 function_subtype_checked0_test: RuntimeError
 function_subtype_closure0_test: RuntimeError
@@ -1083,6 +1075,15 @@
 checked_setter2_test: RuntimeError
 checked_setter3_test: RuntimeError
 checked_setter_test: RuntimeError
+const_constructor2_test/13: MissingCompileTimeError
+const_constructor2_test/14: MissingCompileTimeError
+const_constructor2_test/15: MissingCompileTimeError
+const_constructor2_test/16: MissingCompileTimeError
+const_constructor2_test/17: MissingCompileTimeError
+const_constructor2_test/18: MissingCompileTimeError
+const_constructor2_test/20: MissingCompileTimeError
+const_constructor2_test/22: MissingCompileTimeError
+const_constructor2_test/24: MissingCompileTimeError
 covariance_field_test/01: RuntimeError
 covariance_field_test/02: RuntimeError
 covariance_field_test/03: RuntimeError
diff --git a/tests/language_2/language_2_vm.status b/tests/language_2/language_2_vm.status
index 9d8d97d..cac738d 100644
--- a/tests/language_2/language_2_vm.status
+++ b/tests/language_2/language_2_vm.status
@@ -415,6 +415,7 @@
 function_subtype_bound_closure7_test: RuntimeError
 function_subtype_call1_test: RuntimeError
 function_subtype_call2_test: RuntimeError
+function_subtype_cast0_test: RuntimeError
 function_subtype_cast1_test: RuntimeError
 function_subtype_cast2_test: RuntimeError
 function_subtype_cast3_test: RuntimeError
@@ -521,6 +522,7 @@
 instantiate_tearoff_after_contravariance_check_test: RuntimeError
 instantiate_tearoff_of_call_test: RuntimeError
 instantiate_tearoff_test: RuntimeError
+instantiate_type_variable_test/01: MissingCompileTimeError
 interface_test/00: MissingCompileTimeError
 invalid_cast_test/01: MissingCompileTimeError
 invalid_cast_test/02: MissingCompileTimeError
@@ -865,6 +867,7 @@
 rewrite_implicit_this_test/01: MissingCompileTimeError
 runtime_type_function_test: RuntimeError
 setter4_test: MissingCompileTimeError # Issue 14736
+setter_no_getter_call_test/01: MissingCompileTimeError
 setter_override_test/01: MissingCompileTimeError
 setter_override_test/02: MissingCompileTimeError
 static_field1_test/01: MissingCompileTimeError
diff --git a/tests/language_2/named_parameters_default_eq_test.dart b/tests/language_2/named_parameters_default_eq_test.dart
index cb8bfe9..9a02777 100644
--- a/tests/language_2/named_parameters_default_eq_test.dart
+++ b/tests/language_2/named_parameters_default_eq_test.dart
@@ -18,12 +18,14 @@
   int y;
   int z;
   A({this.x = 3, this.y: 5, z}) : z = z ?? 2;
-  A.redirect({x = 3, y: 5, z}) : this(x: x, y: y, z: z);
-  factory A.factory({x = 3, y: 5, z}) => new A(x: x, y: y, z: z ?? 2);
-  factory A.redirectFactory({x, y, z}) = A;
+  A.redirect({int x = 3, int y: 5, int z}) : this(x: x, y: y, z: z);
+  factory A.factory({int x = 3, int y: 5, int z}) =>
+      new A(x: x, y: y, z: z ?? 2);
+  factory A.redirectFactory({int x, int y, int z}) = A;
 
   // Default values are not allowed on redirecting factory constructors.
-  factory A.badRedirectFactory({x = 3, y}) = A; //# 02: compile-time error
+  factory A.badRedirectFactory({int x = 3, int y}) = //# 02: compile-time error
+      A; //# 02: compile-time error
 
   int get value => x * y * z;
 
diff --git a/tests/language_2/redirecting_factory_default_values_test.dart b/tests/language_2/redirecting_factory_default_values_test.dart
index 6ba3305..e384252 100644
--- a/tests/language_2/redirecting_factory_default_values_test.dart
+++ b/tests/language_2/redirecting_factory_default_values_test.dart
@@ -8,9 +8,9 @@
 
 class A {
   A(this.a, [this.b = 0]);
-  factory A.f(a) = A;
-  factory A.g(a, [b = 0]) = A; // //# 01: compile-time error
-  factory A.h(a, {b: 0}) = A; // //# 02: compile-time error
+  factory A.f(int a) = A;
+  factory A.g(int a, [int b = 0]) = A; // //# 01: compile-time error
+  factory A.h(int a, {int b: 0}) = A; // //# 02: compile-time error
 
   int a;
   int b;
diff --git a/tests/language_2/setter_no_getter_call_test.dart b/tests/language_2/setter_no_getter_call_test.dart
index 92a648f..81adc5d 100644
--- a/tests/language_2/setter_no_getter_call_test.dart
+++ b/tests/language_2/setter_no_getter_call_test.dart
@@ -6,9 +6,9 @@
 
 var topLevelClosure;
 
-/* //   //# 01: runtime error
+/* //# 01: compile-time error
 get topLevel => topLevelClosure;
-*/ //  //# 01: continued
+*/ //# 01: continued
 set topLevel(var value) {}
 
 initialize() {
diff --git a/tests/lib_2/async/future_value_chain4_test.dart b/tests/lib_2/async/future_value_chain4_test.dart
index ea0a887..29f1477 100644
--- a/tests/lib_2/async/future_value_chain4_test.dart
+++ b/tests/lib_2/async/future_value_chain4_test.dart
@@ -7,9 +7,9 @@
 import 'dart:async';
 
 class MyFuture<T> implements Future<T> {
-  Future<S> then<S>(FutureOr<S> valueHandler(T), {Function onError}) {
+  Future<S> then<S>(FutureOr<S> valueHandler(T x), {Function onError}) {
     scheduleMicrotask(() {
-      valueHandler(499);
+      valueHandler(null);
     });
     return null;
   }
diff --git a/tests/lib_2/lib_2.status b/tests/lib_2/lib_2.status
index 4191543..6560052 100644
--- a/tests/lib_2/lib_2.status
+++ b/tests/lib_2/lib_2.status
@@ -120,15 +120,13 @@
 [ !$strong ]
 async/stream_first_where_test/badType: MissingCompileTimeError
 async/stream_last_where_test/badType: MissingCompileTimeError
+mirrors/redirecting_factory_different_type_test/02: MissingCompileTimeError
 
 [ $builder_tag == mac10_7 && $runtime == safari ]
 typed_data/setRange_2_test: Fail # Safari doesn't fully implement spec for TypedArray.set
 typed_data/setRange_3_test: Fail # Safari doesn't fully implement spec for TypedArray.set
 typed_data/setRange_4_test: Fail # Safari doesn't fully implement spec for TypedArray.set
 
-[ $compiler != dart2analyzer && $runtime != none && $checked && !$strong ]
-mirrors/redirecting_factory_different_type_test/02: RuntimeError
-
 [ $compiler != dartdevc && $checked && !$strong ]
 async/future_or_only_in_async_test/00: MissingCompileTimeError
 
@@ -138,6 +136,7 @@
 mirrors/load_library_test: RuntimeError, OK # Deferred loaded eagerly
 
 [ $compiler == none && !$checked ]
+mirrors/redirecting_factory_different_type_test/none: RuntimeError
 mirrors/reflected_type_generics_test/02: Fail, OK # Type check for a bounded type argument.
 
 [ $runtime == chrome && $system == linux ]
diff --git a/tests/lib_2/lib_2_dartdevc.status b/tests/lib_2/lib_2_dartdevc.status
index 8821482..e4f21b3 100644
--- a/tests/lib_2/lib_2_dartdevc.status
+++ b/tests/lib_2/lib_2_dartdevc.status
@@ -7,7 +7,6 @@
 html/xhr_test/xhr: RuntimeError # Issue 29922, strong mode cast failure
 
 [ $compiler == dartdevk ]
-async/future_value_chain4_test: CompileTimeError
 async/slow_consumer_test: CompileTimeError
 collection/list_test: RuntimeError
 convert/chunked_conversion1_test: RuntimeError
diff --git a/tests/lib_2/lib_2_kernel.status b/tests/lib_2/lib_2_kernel.status
index 91efe02..dad45d5 100644
--- a/tests/lib_2/lib_2_kernel.status
+++ b/tests/lib_2/lib_2_kernel.status
@@ -67,7 +67,6 @@
 # ===== dartk + vm status lines =====
 [ $compiler == dartk && $runtime == vm && $strong ]
 async/async_await_sync_completer_test: RuntimeError
-async/future_value_chain4_test: CompileTimeError # Issue 31616
 async/slow_consumer2_test: CompileTimeError # Issue 31402 (Invocation arguments)
 async/stream_controller_async_test: CompileTimeError # Issue 31402 (Invocation arguments)
 async/stream_join_test: CompileTimeError # Issue 31402 (Invocation arguments)
@@ -296,7 +295,6 @@
 async/async_await_sync_completer_test: RuntimeError
 async/future_test/01: RuntimeError
 async/future_test/none: RuntimeError
-async/future_value_chain4_test: CompileTimeError # Issue 31616
 async/slow_consumer2_test: RuntimeError # Issue 31402 (Invocation arguments)
 async/stream_controller_async_test: RuntimeError
 async/stream_distinct_test: RuntimeError
diff --git a/tests/lib_2/mirrors/redirecting_factory_different_type_test.dart b/tests/lib_2/mirrors/redirecting_factory_different_type_test.dart
index 014c5c1..527765b 100644
--- a/tests/lib_2/mirrors/redirecting_factory_different_type_test.dart
+++ b/tests/lib_2/mirrors/redirecting_factory_different_type_test.dart
@@ -12,7 +12,9 @@
 class A {
   factory A(
     String //# 01: compile-time error
-      x) = B;
+    var    //# 02: compile-time error
+    int    //# none: ok
+      x) = B; 
   A._();
 }
 
@@ -28,5 +30,5 @@
   // The type-annotation in A's constructor must be ignored.
   var b = cm.newInstance(const Symbol(''), [499]).reflectee;
   Expect.equals(499, b.x);
-  cm.newInstance(const Symbol(''), ["str"]); //# 02: ok
+  Expect.throws(() => cm.newInstance(const Symbol(''), ["str"]), (e) => e is TypeError); 
 }
diff --git a/tools/VERSION b/tools/VERSION
index 17fcce9..71e6bbd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 0
 PATCH 0
-PRERELEASE 27
+PRERELEASE 28
 PRERELEASE_PATCH 0
diff --git a/tools/bots/dart_tests.isolate b/tools/bots/dart_tests.isolate
index 5f635b6..74ac8b4 100644
--- a/tools/bots/dart_tests.isolate
+++ b/tools/bots/dart_tests.isolate
@@ -15,14 +15,20 @@
               'tests/',
               'pkg/async_helper/',
               'pkg/browser/',
+              'pkg/compiler/',
               'pkg/dart_internal/',
               'pkg/expect/',
+              'pkg/front_end/',
               'pkg/js/',
+              'pkg/js_ast/',
+              'pkg/kernel/',
               'pkg/meta/',
               'pkg/pkg.status',
               'pkg/status_file/',
               'pkg/vm/',
               'runtime/tests/',
+              'sdk/lib/_internal/js_runtime/',
+              'sdk/lib/_internal/sdk_library_metadata/',
               '.packages']
   }
 }
diff --git a/tools/infra/config/cq.cfg b/tools/infra/config/cq.cfg
index 728288d..2c729e6 100644
--- a/tools/infra/config/cq.cfg
+++ b/tools/infra/config/cq.cfg
@@ -24,15 +24,16 @@
       builders { name: "vm-linux-release-x64-try"}
       builders { name: "vm-mac-release-x64-try"}
       builders { name: "dart2js-linux-d8-hostchecked-try"}
+      builders {
+        name: "dart2js-linux-d8-kernel-minified-try"
+        experiment_percentage: 100
+      }
       builders { name: "dart2js-linux-none-only-unittest-try"}
       builders { name: "pkg-linux-release-try"}
       builders { name: "dart2js-linux-chrome-try"}
       builders { name: "ddc-linux-release-chrome-try"}
       builders { name: "vm-linux-product-x64-try"}
-      builders {
-        name: "dart-sdk-windows-try"
-        experiment_percentage: 100
-      }
+      builders { name: "dart-sdk-windows-try"}
       builders { name: "vm-kernel-mac-release-x64-try"}
       builders { name: "benchmark-linux-try"}
     }
diff --git a/tools/make_version.py b/tools/make_version.py
index 68d6064..94d8b34 100755
--- a/tools/make_version.py
+++ b/tools/make_version.py
@@ -40,14 +40,21 @@
   'symbols.cc',
 ]
 
-def makeVersionString(quiet, no_svn):
-  version_string = utils.GetSemanticSDKVersion(ignore_svn_revision=no_svn)
+def MakeVersionString(quiet, no_git_hash, custom_for_pub=None):
+  if custom_for_pub:
+    if no_git_hash:
+      version_string = ("%s-%s" % (utils.GetLatestDevTag(), custom_for_pub))
+    else:
+      version_string = ("%s-%s-%s" %
+          (utils.GetLatestDevTag(), custom_for_pub, utils.GetShortGitHash()))
+  else:
+    version_string = utils.GetSemanticSDKVersion(no_git_hash=no_git_hash)
   if not quiet:
     debugLog("Returning version string: %s " % version_string)
   return version_string
 
 
-def makeSnapshotHashString():
+def MakeSnapshotHashString():
   vmhash = hashlib.md5()
   for vmfilename in VM_SNAPSHOT_FILES:
     vmfilepath = os.path.join(utils.DART_DIR, 'runtime', 'vm', vmfilename)
@@ -56,17 +63,17 @@
   return vmhash.hexdigest()
 
 
-def makeFile(quiet, output_file, input_file, ignore_svn_revision):
+def MakeFile(quiet, output_file, input_file, no_git_hash, custom_for_pub):
   version_cc_text = open(input_file).read()
-  version_string = makeVersionString(quiet, ignore_svn_revision)
+  version_string = MakeVersionString(quiet, no_git_hash, custom_for_pub)
   version_cc_text = version_cc_text.replace("{{VERSION_STR}}",
                                             version_string)
   version_time = utils.GetGitTimestamp()
-  if ignore_svn_revision or version_time == None:
+  if no_git_hash or version_time == None:
     version_time = "Unknown timestamp"
   version_cc_text = version_cc_text.replace("{{COMMIT_TIME}}",
                                             version_time)
-  snapshot_hash = makeSnapshotHashString()
+  snapshot_hash = MakeSnapshotHashString()
   version_cc_text = version_cc_text.replace("{{SNAPSHOT_HASH}}",
                                             snapshot_hash)
   open(output_file, 'w').write(version_cc_text)
@@ -77,18 +84,27 @@
   try:
     # Parse input.
     parser = OptionParser()
-    parser.add_option("-q", "--quiet",
-                      action="store_true", default=False,
-                      help="disable console output")
-    parser.add_option("--ignore_svn_revision",
-                      action="store_true", default=False,
-                      help="Don't try to determine svn revision")
-    parser.add_option("--output",
-                      action="store", type="string",
-                      help="output file name")
+    parser.add_option("--custom_for_pub",
+        action="store",
+        type="string",
+        help=("Generates a version string that works with pub that includes"
+              "the given string"))
     parser.add_option("--input",
-                      action="store", type="string",
-                      help="input template file")
+        action="store",
+        type="string",
+        help="input template file")
+    parser.add_option("--no_git_hash",
+        action="store_true",
+        default=False,
+        help="Don't try to determine svn revision")
+    parser.add_option("--output",
+        action="store",
+        type="string",
+        help="output file name")
+    parser.add_option("-q", "--quiet",
+        action="store_true",
+        default=False,
+        help="disable console output")
 
     (options, args) = parser.parse_args()
     if not options.output:
@@ -102,8 +118,8 @@
     for arg in args:
       files.append(arg)
 
-    if not makeFile(options.quiet, options.output, options.input,
-                    options.ignore_svn_revision):
+    if not MakeFile(options.quiet, options.output, options.input,
+                    options.no_git_hash, options.custom_for_pub):
       return -1
 
     return 0
diff --git a/tools/observatory_tool.py b/tools/observatory_tool.py
index 4dbb6ab..21dd72b 100755
--- a/tools/observatory_tool.py
+++ b/tools/observatory_tool.py
@@ -217,7 +217,7 @@
                       options.pub_executable,
                       options.pub_snapshot,
                       ['build',
-                       '-DOBS_VER=' + utils.GetVersion(ignore_svn_revision=True),
+                       '-DOBS_VER=' + utils.GetVersion(no_git_hash=True),
                        '--output', args[0]],
                       options.silent)
   elif (cmd == 'deploy'):
diff --git a/tools/testing/dart/compiler_configuration.dart b/tools/testing/dart/compiler_configuration.dart
index 5501b92..fc3dead 100644
--- a/tools/testing/dart/compiler_configuration.dart
+++ b/tools/testing/dart/compiler_configuration.dart
@@ -1087,36 +1087,36 @@
 }
 
 class FastaCompilerConfiguration extends CompilerConfiguration {
-  static const String mimeType = "application/x.dill";
+  static final _compilerLocation =
+      Repository.uri.resolve("pkg/front_end/tool/_fasta/compile.dart");
 
-  final Uri _compilerLocation;
-
-  final Uri _plaformDill;
+  final Uri _platformDill;
 
   final Uri _vmExecutable;
 
-  final bool _isLegacy;
+  bool get _isLegacy => !_configuration.isStrong;
 
-  FastaCompilerConfiguration(Configuration configuration)
-      : this._(
-            Repository.uri.resolve("pkg/front_end/tool/_fasta/compile.dart"),
-            Uri.base
-                .resolveUri(new Uri.directory(configuration.buildDirectory)),
-            !configuration.isStrong,
-            configuration.useSdk,
-            configuration);
+  factory FastaCompilerConfiguration(Configuration configuration) {
+    var buildDirectory =
+        Uri.base.resolveUri(new Uri.directory(configuration.buildDirectory));
 
-  FastaCompilerConfiguration._(this._compilerLocation, Uri buildDirectory,
-      this._isLegacy, bool useSdk, Configuration configuration)
-      : _plaformDill = (useSdk
-                ? buildDirectory.resolve("dart-sdk/lib/_internal/")
-                : buildDirectory)
-            .resolve(
-                _isLegacy ? "vm_platform.dill" : "vm_platform_strong.dill"),
-        _vmExecutable = useSdk
-            ? buildDirectory.resolve("dart-sdk/bin/dart")
-            : buildDirectory.resolve("dart"),
-        super._subclass(configuration);
+    var dillDir = buildDirectory;
+    if (configuration.useSdk) {
+      dillDir = buildDirectory.resolve("dart-sdk/lib/_internal/");
+    }
+
+    var suffix = configuration.isStrong ? "_strong" : "";
+    var platformDill = dillDir.resolve("vm_platform$suffix.dill");
+
+    var vmExecutable = buildDirectory
+        .resolve(configuration.useSdk ? "dart-sdk/bin/dart" : "dart");
+    return new FastaCompilerConfiguration._(
+        platformDill, vmExecutable, configuration);
+  }
+
+  FastaCompilerConfiguration._(
+      this._platformDill, this._vmExecutable, Configuration configuration)
+      : super._subclass(configuration);
 
   @override
   bool get useDfe => true;
@@ -1125,58 +1125,40 @@
   bool get runRuntimeDespiteMissingCompileTimeError => true;
 
   @override
-  int get timeoutMultiplier => 1;
-
-  @override
-  bool get hasCompiler => true;
-
-  @override
-  List<Uri> bootstrapDependencies() => <Uri>[_plaformDill];
+  List<Uri> bootstrapDependencies() => [_platformDill];
 
   @override
   Command createCommand(String inputFile, String outputFile,
       List<String> sharedOptions, Map<String, String> environment) {
-    throw "not implemented yet";
+    throw new UnimplementedError();
   }
 
   @override
   CommandArtifact computeCompilationArtifact(String tempDir,
       List<String> arguments, Map<String, String> environmentOverrides) {
-    Uri output =
+    var output =
         Uri.base.resolveUri(new Uri.directory(tempDir)).resolve("out.dill");
-    String outputFileName = output.toFilePath();
-    String vmPath = _vmExecutable.toFilePath();
-    if (Platform.isWindows) {
-      vmPath += ".exe";
-    }
-    List<String> compilerArguments = <String>[];
+    var outputFileName = output.toFilePath();
+
+    var compilerArguments = <String>[];
     if (!_isLegacy) {
       compilerArguments.add("--strong-mode");
     }
-    compilerArguments
-      ..addAll(<String>[
-        "-o",
-        outputFileName,
-        "--platform",
-        _plaformDill.toFilePath(),
-      ])
-      ..addAll(arguments);
 
-    return new CommandArtifact(
-      [
-        Command.fasta(
+    compilerArguments.addAll(
+        ["-o", outputFileName, "--platform", _platformDill.toFilePath()]);
+    compilerArguments.addAll(arguments);
+
+    return new CommandArtifact([
+      Command.fasta(
           _compilerLocation,
           output,
           bootstrapDependencies(),
           _vmExecutable,
           compilerArguments,
           environmentOverrides,
-          Repository.uri,
-        )
-      ],
-      outputFileName,
-      mimeType,
-    );
+          Repository.uri)
+    ], outputFileName, "application/x.dill");
   }
 
   @override
@@ -1185,8 +1167,8 @@
       List<String> sharedOptions,
       List<String> dart2jsOptions,
       List<String> args) {
-    List<String> arguments = <String>[];
-    for (String argument in args) {
+    var arguments = <String>[];
+    for (var argument in args) {
       if (argument != "--ignore-unrecognized-flags") {
         arguments.add(argument);
       }
@@ -1205,6 +1187,7 @@
     if (runtimeConfiguration is! NoneRuntimeConfiguration) {
       throw "--compiler=fasta only supports --runtime=none";
     }
+
     return <String>[];
   }
 }
diff --git a/tools/utils.py b/tools/utils.py
index 266b035..8d665bb 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -30,10 +30,12 @@
 DART_DIR = os.path.abspath(
     os.path.normpath(os.path.join(__file__, '..', '..')))
 
+
 def GetBotUtils():
   '''Dynamically load the tools/bots/bot_utils.py python module.'''
   return imp.load_source('bot_utils', os.path.join(DART_DIR, 'tools', 'bots', 'bot_utils.py'))
 
+
 class Version(object):
   def __init__(self, channel, major, minor, patch, prerelease,
                prerelease_patch):
@@ -44,6 +46,7 @@
     self.prerelease = prerelease
     self.prerelease_patch = prerelease_patch
 
+
 # Try to guess the host operating system.
 def GuessOS():
   os_id = platform.system()
@@ -106,6 +109,7 @@
     return int(win_cpu_count)
   return 2
 
+
 def GetWindowsRegistryKeyName(name):
   import win32process
   # Check if python process is 64-bit or if it's 32-bit running in 64-bit OS.
@@ -117,6 +121,7 @@
     wow6432Node = ''
   return r'SOFTWARE\%s%s' % (wow6432Node, name)
 
+
 # Try to guess Visual Studio location when buiding on Windows.
 def GuessVisualStudioPath():
   defaultPath = r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7" \
@@ -197,6 +202,7 @@
     result.append(line)
   return result
 
+
 # Filters out all arguments until the next '--' argument
 # occurs.
 def ListArgCallback(option, value, parser):
@@ -265,23 +271,28 @@
 DART_DIR = os.path.abspath(os.path.join(__file__, '..', '..'))
 VERSION_FILE = os.path.join(DART_DIR, 'tools', 'VERSION')
 
+
 def GetBuildbotGSUtilPath():
   gsutil = '/b/build/scripts/slave/gsutil'
   if platform.system() == 'Windows':
     gsutil = 'e:\\\\b\\build\\scripts\\slave\\gsutil'
   return gsutil
 
+
 def GetBuildMode(mode):
   return BUILD_MODES[mode]
 
+
 def GetArchFamily(arch):
   return ARCH_FAMILY[arch]
 
+
 def IsCrossBuild(target_os, arch):
   host_arch = ARCH_GUESS
   return ((GetArchFamily(host_arch) != GetArchFamily(arch)) or
           (target_os != GuessOS()))
 
+
 def GetBuildConf(mode, arch, conf_os=None):
   if conf_os == 'android':
     return '%s%s%s' % (GetBuildMode(mode), conf_os.title(), arch.upper())
@@ -293,35 +304,41 @@
       cross_build = 'X'
     return '%s%s%s' % (GetBuildMode(mode), cross_build, arch.upper())
 
+
 def GetBuildDir(host_os):
   return BUILD_ROOT[host_os]
 
+
 def GetBuildRoot(host_os, mode=None, arch=None, target_os=None):
   build_root = GetBuildDir(host_os)
   if mode:
     build_root = os.path.join(build_root, GetBuildConf(mode, arch, target_os))
   return build_root
 
+
 def GetBuildSdkBin(host_os, mode=None, arch=None, target_os=None):
   build_root = GetBuildRoot(host_os, mode, arch, target_os)
   return os.path.join(build_root, 'dart-sdk', 'bin')
 
+
 def GetBaseDir():
   return BASE_DIR
 
+
 def GetShortVersion():
   version = ReadVersionFile()
   return ('%s.%s.%s.%s.%s' % (
       version.major, version.minor, version.patch, version.prerelease,
       version.prerelease_patch))
 
-def GetSemanticSDKVersion(ignore_svn_revision=False):
+
+def GetSemanticSDKVersion(no_git_hash=False):
   version = ReadVersionFile()
   if not version:
     return None
 
   if version.channel == 'be':
-    postfix = '-edge' if ignore_svn_revision else '-edge.%s' % GetGitRevision()
+    postfix = '-edge' if no_git_hash else '-edge.%s' % GetGitRevision()
   elif version.channel == 'dev':
     postfix = '-dev.%s.%s' % (version.prerelease, version.prerelease_patch)
   else:
@@ -330,8 +347,10 @@
 
   return '%s.%s.%s%s' % (version.major, version.minor, version.patch, postfix)
 
-def GetVersion(ignore_svn_revision=False):
-  return GetSemanticSDKVersion(ignore_svn_revision)
+
+def GetVersion(no_git_hash=False):
+  return GetSemanticSDKVersion(no_git_hash)
+
 
 # The editor used to produce the VERSION file put on gcs. We now produce this
 # in the bots archiving the sdk.
@@ -347,16 +366,19 @@
             "revision": GetGitRevision()}
   return json.dumps(result, indent=2)
 
+
 def GetChannel():
   version = ReadVersionFile()
   return version.channel
 
+
 def GetUserName():
   key = 'USER'
   if sys.platform == 'win32':
     key = 'USERNAME'
   return os.environ.get(key, '')
 
+
 def ReadVersionFile():
   def match_against(pattern, file_content):
     match = re.search(pattern, file_content, flags=re.MULTILINE)
@@ -425,6 +447,37 @@
     return None
   return output
 
+
+def GetShortGitHash():
+  p = subprocess.Popen(['git', 'log', '-n', '1', '--pretty=format:%h'],
+                       stdout = subprocess.PIPE,
+                       stderr = subprocess.STDOUT, shell=IsWindows(),
+                       cwd = DART_DIR)
+  output, _ = p.communicate()
+  if p.wait() != 0:
+    return None
+  return output
+
+
+def GetLatestDevTag():
+  cmd = [
+    'git',
+    'for-each-ref',
+    'refs/tags/*dev*',
+    '--sort=-taggerdate',
+    "--format=%(refname:lstrip=2)",
+    '--count=1',
+  ]
+  p = subprocess.Popen(cmd,
+                       stdout = subprocess.PIPE,
+                       stderr = subprocess.STDOUT, shell=IsWindows(),
+                       cwd = DART_DIR)
+  output, _ = p.communicate()
+  if p.wait() != 0:
+    return None
+  return output.strip()
+
+
 def GetGitTimestamp():
   p = subprocess.Popen(['git', 'log', '-n', '1', '--pretty=format:%cd'],
                        stdout = subprocess.PIPE,
@@ -435,6 +488,7 @@
     return None
   return output
 
+
 # To eliminate clashing with older archived builds on bleeding edge we add
 # a base number bigger the largest svn revision (this also gives us an easy
 # way of seeing if an archive comes from git based or svn based commits).
@@ -452,6 +506,7 @@
     print "Warning: could not parse git count, output was %s" % output
   return None
 
+
 def ParseGitInfoOutput(output):
   """Given a git log, determine the latest corresponding svn revision."""
   for line in output.split('\n'):
@@ -460,12 +515,14 @@
       return tokens[1].split('@')[1]
   return None
 
+
 def ParseSvnInfoOutput(output):
   revision_match = re.search('Last Changed Rev: (\d+)', output)
   if revision_match:
     return revision_match.group(1)
   return None
 
+
 def RewritePathSeparator(path, workspace):
   # Paths in test files are always specified using '/'
   # as the path separator. Replace with the actual
@@ -559,6 +616,7 @@
   print "GetVersionFileContent() -> ", GetVersionFileContent()
   print "GetGitNumber() -> ", GetGitNumber()
 
+
 class Error(Exception):
   pass
 
@@ -683,6 +741,7 @@
   def __exit__(self, *_):
     shutil.rmtree(self._temp_dir, ignore_errors=True)
 
+
 class ChangedWorkingDirectory(object):
   def __init__(self, working_directory):
     self._working_directory = working_directory
@@ -706,6 +765,7 @@
   def __str__(self):
     return "Crash(%s: %s %s)" % (self.test, self.binary, self.pid)
 
+
 class SiteConfigBotoFileDisabler(object):
   def __init__(self):
     self._old_aws = None
@@ -726,6 +786,7 @@
     if self._old_boto:
       os.environ['BOTO_CONFIG'] = self._old_boto
 
+
 class PosixCoreDumpEnabler(object):
   def __init__(self):
     self._old_limits = None
@@ -737,6 +798,7 @@
   def __exit__(self, *_):
     resource.setrlimit(resource.RLIMIT_CORE, self._old_limits)
 
+
 # TODO(whesse): Re-enable after issue #30205 is addressed
 class LinuxCoreDumpEnabler(PosixCoreDumpEnabler):
   def __enter__(self):
@@ -750,6 +812,7 @@
     # CheckLinuxCoreDumpPattern(fatal=True)
     # super(LinuxCoreDumpEnabler, self).__exit__(*args)
 
+
 class WindowsCoreDumpEnabler(object):
   """Configure Windows Error Reporting to store crash dumps.
 
@@ -835,6 +898,7 @@
       handle.Close()
       self.winreg.DeleteKeyEx(key, subkey, wowbit, 0)
 
+
 class BaseCoreDumpArchiver(object):
   """This class reads coredumps file written by UnexpectedCrashDumpArchiver
   into the current working directory and uploads all cores and binaries
@@ -976,14 +1040,17 @@
     if os.path.exists(core_filename):
       return core_filename
 
+
 class LinuxCoreDumpArchiver(PosixCoreDumpArchiver):
   def __init__(self):
     super(LinuxCoreDumpArchiver, self).__init__(os.getcwd())
 
+
 class MacOSCoreDumpArchiver(PosixCoreDumpArchiver):
   def __init__(self):
     super(MacOSCoreDumpArchiver, self).__init__('/cores')
 
+
 class WindowsCoreDumpArchiver(BaseCoreDumpArchiver):
   def __init__(self):
     super(WindowsCoreDumpArchiver, self).__init__(os.path.join(
@@ -1030,10 +1097,12 @@
       missing_as_string = ', '.join([str(c) for c in missing])
       raise Exception('Missing crash dumps for: %s' % missing_as_string)
 
+
 @contextlib.contextmanager
 def NooptCoreDumpArchiver():
   yield
 
+
 def CoreDumpArchiver(args):
   enabled = '--copy-coredumps' in args