Version 2.14.0-103.0.dev

Merge commit '823b429d86ef0be534e96b922ad115df42a47d80' into 'dev'
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 9ea7506..ca116b4 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -766,12 +766,6 @@
   }
 
   @override
-  int compareTo(js.Name other) {
-    assert(_value != null, 'value not set for $this');
-    return _value.compareTo(other);
-  }
-
-  @override
   Iterable<js.Node> get containedNodes {
     return _value != null ? [_value] : const [];
   }
diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart
index 645d026..ba2dfd1 100644
--- a/pkg/compiler/lib/src/js_backend/namer.dart
+++ b/pkg/compiler/lib/src/js_backend/namer.dart
@@ -559,14 +559,6 @@
         entity.rootOfScope, () => new NamingScope());
   }
 
-  /// Return a reference to the given [name].
-  ///
-  /// This is used to ensure that every use site of a name has a unique node so
-  /// that we can properly attribute source information.
-  jsAst.Name _newReference(jsAst.Name name) {
-    return new _NameReference(name);
-  }
-
   /// Disambiguated name for [constant].
   ///
   /// Unique within the global-member namespace.
@@ -581,7 +573,7 @@
       result = getFreshName(_constantScope, longName);
       _constantNames[constant] = result;
     }
-    return _newReference(result);
+    return result;
   }
 
   /// Proposed name for [constant].
@@ -946,7 +938,7 @@
       newName = getFreshName(globalScope, name);
       internalGlobals[name] = newName;
     }
-    return _newReference(newName);
+    return newName;
   }
 
   /// Generates a unique key for [library].
@@ -986,7 +978,7 @@
       newName = getFreshName(globalScope, proposedName);
       globals[element] = newName;
     }
-    return _newReference(newName);
+    return newName;
   }
 
   /// Returns the disambiguated name for an instance method or field
@@ -1027,7 +1019,7 @@
       userInstanceMembers[key] = newName;
       userInstanceMembersOriginalName[key] = '$originalName';
     }
-    return _newReference(newName);
+    return newName;
   }
 
   /// Returns the disambiguated name for the instance member identified by
@@ -1051,7 +1043,7 @@
       // TODO(sigmund): consider plumbing the original name instead.
       userInstanceMembersOriginalName[key] = name;
     }
-    return _newReference(newName);
+    return newName;
   }
 
   /// Forces the public instance member with [originalName] to have the given
@@ -1099,7 +1091,7 @@
         internalInstanceMembers[element] = newName;
       }
     }
-    return _newReference(newName);
+    return newName;
   }
 
   /// Disambiguated name for the given operator.
@@ -1114,7 +1106,7 @@
       newName = getFreshName(instanceScope, operatorIdentifier);
       userInstanceOperators[operatorIdentifier] = newName;
     }
-    return _newReference(newName);
+    return newName;
   }
 
   String _generateFreshStringForName(String proposedName, NamingScope scope,
diff --git a/pkg/compiler/lib/src/js_backend/namer_names.dart b/pkg/compiler/lib/src/js_backend/namer_names.dart
index 233f053..fe43140 100644
--- a/pkg/compiler/lib/src/js_backend/namer_names.dart
+++ b/pkg/compiler/lib/src/js_backend/namer_names.dart
@@ -6,19 +6,47 @@
 
 abstract class _NamerName extends jsAst.Name {
   int get _kind;
-  _NamerName get _target => this;
+
+  @override
+  _NamerName withSourceInformation(
+      jsAst.JavaScriptNodeSourceInformation newSourceInformation) {
+    if (sourceInformation == newSourceInformation) return this;
+    final name = this; // Variable needed for type promotion in next line.
+    final underlying = name is _NameReference ? name._target : this;
+    return _NameReference(underlying, newSourceInformation);
+  }
+
+  int _compareSameKind(covariant _NamerName);
 
   @override
   String toString() {
     if (DEBUG_MODE) {
       return 'Name($key)';
     }
-    throw new UnsupportedError("Cannot convert a name to a string");
+    throw UnsupportedError("Cannot convert a name to a string");
   }
 }
 
 enum _NamerNameKinds { StringBacked, Getter, Setter, Async, Compound, Token }
 
+/// An arbitrary but stable sorting comparison method.
+int compareNames(jsAst.Name aName, jsAst.Name bName) {
+  _NamerName dereference(jsAst.Name name) {
+    if (name is ModularName) return dereference(name.value);
+    if (name is _NameReference) return dereference(name._target);
+    if (name is _NamerName) return name;
+    throw UnsupportedError('Cannot find underlying _NamerName: $name');
+  }
+
+  _NamerName a = dereference(aName);
+  _NamerName b = dereference(bName);
+
+  int aKind = a._kind;
+  int bKind = b._kind;
+  if (bKind != aKind) return bKind - aKind;
+  return a._compareSameKind(b);
+}
+
 class StringBackedName extends _NamerName {
   @override
   final String name;
@@ -31,26 +59,18 @@
   String get key => name;
 
   @override
-  operator ==(other) {
-    if (other is _NameReference) other = other._target;
+  operator ==(Object other) {
+    if (other is _NameReference) return this == other._target;
     if (identical(this, other)) return true;
-    return (other is StringBackedName) && other.name == name;
+    return other is StringBackedName && name == other.name;
   }
 
   @override
   int get hashCode => name.hashCode;
 
   @override
-  int compareTo(jsAst.Name other) {
-    _NamerName otherNamerName;
-    if (other is ModularName) {
-      otherNamerName = other.value;
-    } else {
-      otherNamerName = other;
-    }
-    otherNamerName = otherNamerName._target;
-    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
-    return name.compareTo(otherNamerName.name);
+  int _compareSameKind(StringBackedName other) {
+    return name.compareTo(other.name);
   }
 }
 
@@ -72,11 +92,12 @@
   String get key => prefix.key + base.key;
 
   @override
-  bool operator ==(other) {
-    if (other is _NameReference) other = other._target;
+  bool operator ==(Object other) {
+    if (other is _NameReference) return this == other._target;
     if (identical(this, other)) return true;
-    if (other is! _PrefixedName) return false;
-    return other.base == base && other.prefix == prefix;
+    return other is _PrefixedName &&
+        base == other.base &&
+        prefix == other.prefix;
   }
 
   @override
@@ -86,22 +107,10 @@
   bool get isFinalized => prefix.isFinalized && base.isFinalized;
 
   @override
-  int compareTo(jsAst.Name other) {
-    _NamerName otherNamerName;
-    if (other is ModularName) {
-      otherNamerName = other.value;
-    } else {
-      otherNamerName = other;
-    }
-    otherNamerName = otherNamerName._target;
-    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
-    _PrefixedName otherSameKind = otherNamerName;
-    int result = prefix.compareTo(otherSameKind.prefix);
+  int _compareSameKind(_PrefixedName other) {
+    int result = compareNames(prefix, other.prefix);
     if (result == 0) {
-      result = prefix.compareTo(otherSameKind.prefix);
-      if (result == 0) {
-        result = base.compareTo(otherSameKind.base);
-      }
+      result = compareNames(base, other.base);
     }
     return result;
   }
@@ -150,25 +159,24 @@
 
   @override
   String get name {
-    if (_cachedName == null) {
-      _cachedName = _parts.map((jsAst.Name name) => name.name).join();
-    }
-    return _cachedName;
+    return _cachedName ??= _parts.map((jsAst.Name name) => name.name).join();
   }
 
   @override
   String get key => _parts.map((_NamerName name) => name.key).join();
 
   @override
-  bool operator ==(other) {
-    if (other is _NameReference) other = other._target;
+  bool operator ==(Object other) {
+    if (other is _NameReference) return this == other._target;
     if (identical(this, other)) return true;
-    if (other is! CompoundName) return false;
-    if (other._parts.length != _parts.length) return false;
-    for (int i = 0; i < _parts.length; ++i) {
-      if (other._parts[i] != _parts[i]) return false;
+    if (other is CompoundName) {
+      if (other._parts.length != _parts.length) return false;
+      for (int i = 0; i < _parts.length; ++i) {
+        if (_parts[i] != other._parts[i]) return false;
+      }
+      return true;
     }
-    return true;
+    return false;
   }
 
   @override
@@ -183,22 +191,10 @@
   }
 
   @override
-  int compareTo(jsAst.Name other) {
-    _NamerName otherNamerName;
-    if (other is ModularName) {
-      otherNamerName = other.value;
-    } else {
-      otherNamerName = other;
-    }
-    otherNamerName = otherNamerName._target;
-    if (otherNamerName._kind != _kind) return otherNamerName._kind - _kind;
-    CompoundName otherSameKind = otherNamerName;
-    if (otherSameKind._parts.length != _parts.length) {
-      return otherSameKind._parts.length - _parts.length;
-    }
-    int result = 0;
+  int _compareSameKind(CompoundName other) {
+    int result = _parts.length.compareTo(other._parts.length);
     for (int pos = 0; result == 0 && pos < _parts.length; pos++) {
-      result = _parts[pos].compareTo(otherSameKind._parts[pos]);
+      result = compareNames(_parts[pos], other._parts[pos]);
     }
     return result;
   }
@@ -225,19 +221,16 @@
   }
 
   @override
-  int compareTo(covariant _NamerName other) {
-    other = other._target;
-    if (other._kind != _kind) return other._kind - _kind;
-    TokenName otherToken = other;
-    return key.compareTo(otherToken.key);
+  int _compareSameKind(TokenName other) {
+    return key.compareTo(other.key);
   }
 
   @override
   void markSeen(jsAst.TokenCounter counter) => _rc++;
 
   @override
-  bool operator ==(other) {
-    if (other is _NameReference) other = other._target;
+  bool operator ==(Object other) {
+    if (other is _NameReference) return this == other._target;
     if (identical(this, other)) return true;
     return false;
   }
@@ -254,9 +247,18 @@
   }
 }
 
+/// A [_NameReference] behaves like the underlying (target) [_NamerName] but
+/// carries its own [JavaScriptNodeSourceInformation].
+// TODO(sra): See if this functionality can be moved into js_ast.
 class _NameReference extends _NamerName implements jsAst.AstContainer {
+  final _NamerName _target;
+  final jsAst.JavaScriptNodeSourceInformation _sourceInformation;
+
+  _NameReference(this._target, this._sourceInformation);
+
   @override
-  _NamerName _target;
+  jsAst.JavaScriptNodeSourceInformation get sourceInformation =>
+      _sourceInformation;
 
   @override
   int get _kind => _target._kind;
@@ -266,27 +268,19 @@
   @override
   Iterable<jsAst.Node> get containedNodes => [_target];
 
-  _NameReference(this._target);
-
   @override
   String get name => _target.name;
 
   @override
-  int compareTo(jsAst.Name other) {
-    _NamerName otherNamerName;
-    if (other is ModularName) {
-      otherNamerName = other.value;
-    } else {
-      otherNamerName = other;
-    }
-    return _target.compareTo(otherNamerName);
+  int _compareSameKind(_NameReference other) {
+    throw StateError('Should have been dereferenced: $this');
   }
 
   @override
   bool get isFinalized => _target.isFinalized;
 
   @override
-  bool operator ==(other) => _target == other;
+  bool operator ==(Object other) => _target == other;
 
   @override
   int get hashCode => _target.hashCode;
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index 5eb2990..26556d7 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -21,7 +21,7 @@
 import '../../js_backend/custom_elements_analysis.dart';
 import '../../js_backend/inferred_data.dart';
 import '../../js_backend/interceptor_data.dart';
-import '../../js_backend/namer.dart' show Namer, StringBackedName;
+import '../../js_backend/namer.dart' show Namer, StringBackedName, compareNames;
 import '../../js_backend/native_data.dart';
 import '../../js_backend/runtime_types.dart' show RuntimeTypesChecks;
 import '../../js_backend/runtime_types_codegen.dart' show TypeCheck;
@@ -998,7 +998,7 @@
           "${interceptorMap[name]}, new ${interceptor}.");
       interceptorMap[name] = interceptor;
     }
-    names.sort();
+    names.sort(compareNames);
     return names.map((js.Name name) {
       SpecializedGetInterceptor interceptor = interceptorMap[name];
       js.Expression code =
@@ -1099,7 +1099,7 @@
           "${interceptorMap[name]}, new ${interceptor}.");
       interceptorMap[name] = interceptor;
     }
-    names.sort();
+    names.sort(compareNames);
     return names.map((js.Name name) {
       OneShotInterceptor interceptor = interceptorMap[name];
       js.Expression code =
diff --git a/pkg/compiler/test/codegen/model_data/regress_36222.dart b/pkg/compiler/test/codegen/model_data/regress_36222.dart
index 6a5dbe9..0ed6089 100644
--- a/pkg/compiler/test/codegen/model_data/regress_36222.dart
+++ b/pkg/compiler/test/codegen/model_data/regress_36222.dart
@@ -14,7 +14,12 @@
     return x + y;
   }
 
-  /*member: A.foo:elided,stubCalls=[foo$2:call$2(arg0,arg1),foo$2:main_A_defaultFoo$closure(0)]*/
+  /*member: A.foo:
+   elided,
+   stubCalls=[
+    foo$2:call$2(2),
+    foo$2:main_A_defaultFoo$closure(0)]
+  */
   final BinaryFunc foo;
 }
 
diff --git a/pkg/compiler/test/codegen/model_test.dart b/pkg/compiler/test/codegen/model_test.dart
index 9e694ee..6748f8b 100644
--- a/pkg/compiler/test/codegen/model_test.dart
+++ b/pkg/compiler/test/codegen/model_test.dart
@@ -14,7 +14,6 @@
 import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
 import 'package:compiler/src/elements/entities.dart';
 import 'package:compiler/src/js/js.dart' as js;
-import 'package:compiler/src/js_backend/namer.dart';
 import 'package:compiler/src/js_emitter/model.dart';
 import 'package:compiler/src/js_model/element_map.dart';
 import 'package:compiler/src/js_model/js_world.dart';
@@ -99,7 +98,6 @@
         String name;
         if (selector is js.Name) {
           name = selector.key;
-          fixedNameCall = selector is StringBackedName;
         } else if (selector is js.LiteralString) {
           /// Call to fixed backend name, so we include the argument
           /// values to test encoding of optional parameters in native
diff --git a/pkg/js_ast/lib/src/nodes.dart b/pkg/js_ast/lib/src/nodes.dart
index 2868346..903026e 100644
--- a/pkg/js_ast/lib/src/nodes.dart
+++ b/pkg/js_ast/lib/src/nodes.dart
@@ -445,8 +445,6 @@
     return clone;
   }
 
-  VariableUse asVariableUse() => null;
-
   bool get isCommaOperator => false;
 
   bool get isFinalized => true;
@@ -1001,15 +999,15 @@
 
 abstract class Declaration implements VariableReference {}
 
-/// An implementation of [Name] represents a potentially late bound name in
-/// the generated ast.
+/// [Name] is an extension point to allow a JavaScript AST to contain
+/// identifiers that are bound later. This is used in minification.
 ///
-/// While [Name] implements comparable, there is no requirement on the actual
-/// implementation of [compareTo] other than that it needs to be stable.
-/// In particular, there is no guarantee that implementations of [compareTo]
-/// will implement some form of lexicographic ordering like [String.compareTo].
-abstract class Name extends Literal
-    implements Declaration, Parameter, Comparable<Name> {
+/// [Name] is a [Literal] so that it can occur as a property access selector.
+//
+// TODO(sra): Figure out why [Name] is a Declaration and Parameter, and where
+// that is used. How should the printer know if an occurrence of a Name is meant
+// to be a Literal or a Declaration (which includes a VariableUse)?
+abstract class Name extends Literal implements Declaration, Parameter {
   T accept<T>(NodeVisitor<T> visitor) => visitor.visitName(this);
 
   R accept1<R, A>(NodeVisitor1<R, A> visitor, A arg) =>
@@ -1017,6 +1015,12 @@
 
   Name _clone();
 
+  /// Returns the text of this name.
+  ///
+  /// May throw if the text has not been decided. Typically the text is decided
+  /// in some finalization phase that happens before the AST is printed.
+  String get name;
+
   /// Returns a unique [key] for this name.
   ///
   /// The key is unrelated to the actual name and is not intended for human
@@ -1395,8 +1399,6 @@
 
   VariableUse _clone() => new VariableUse(name);
 
-  VariableUse asVariableUse() => this;
-
   String toString() => 'VariableUse($name)';
 }
 
diff --git a/pkg/test_runner/pubspec.yaml b/pkg/test_runner/pubspec.yaml
index 9fad3c1..506b9b2 100644
--- a/pkg/test_runner/pubspec.yaml
+++ b/pkg/test_runner/pubspec.yaml
@@ -20,6 +20,8 @@
   status_file:
     path: ../status_file
 dev_dependencies:
+  analyzer:
+    path: ../../pkg/analyzer
   expect:
     path: ../expect
   file: any
@@ -27,6 +29,8 @@
 dependency_overrides:
   # Other packages in the dependency graph have normal hosted dependencies on
   # this, so just override it to force the local one.
+  analyzer:
+    path: ../../pkg/analyzer
   args:
     path: ../../third_party/pkg/args
   file:
diff --git a/pkg/test_runner/tool/orphan_files.dart b/pkg/test_runner/tool/orphan_files.dart
new file mode 100644
index 0000000..8d7e8f6
--- /dev/null
+++ b/pkg/test_runner/tool/orphan_files.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2019, 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.
+
+/// Looks for ".dart" files in "tests/" that appear to be orphaned. That means
+/// they don't end in "_test.dart" so aren't run as tests by the test_runner,
+/// but they also don't appear to be referenced by any other tests.
+///
+/// Usually this means that someone accidentally left off the "_test" and the
+/// file is supposed to be a test but is silently getting ignored.
+import 'dart:io';
+
+import 'package:analyzer/dart/analysis/analysis_context.dart';
+import 'package:analyzer/dart/analysis/context_builder.dart';
+import 'package:analyzer/dart/analysis/context_locator.dart';
+import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+
+import 'package:test_runner/src/path.dart';
+
+AnalysisContext _analysisContext;
+
+void main(List<String> arguments) {
+  _initAnalysisContext();
+
+  var suites = Directory('tests').listSync();
+  suites.sort((a, b) => a.path.compareTo(b.path));
+
+  for (var entry in suites) {
+    // Skip the co19 tests since they don't use '_test.dart'.
+    if (entry is Directory && !entry.path.contains('co19')) {
+      _checkTestDirectory(entry);
+    }
+  }
+}
+
+void _initAnalysisContext() {
+  var roots = ContextLocator().locateRoots(includedPaths: ['test']);
+  if (roots.length != 1) {
+    throw StateError('Expected to find exactly one context root, got $roots');
+  }
+
+  _analysisContext = ContextBuilder().createContext(contextRoot: roots[0]);
+}
+
+void _checkTestDirectory(Directory directory) {
+  print('-- ${directory.path} --');
+  var paths = directory
+      .listSync(recursive: true)
+      .map((entry) => entry.path)
+      .where((path) => path.endsWith('.dart'))
+      .toList();
+  paths.sort();
+
+  // Collect the set of all files that are known to be referred to by others.
+  print('Finding referenced files...');
+  var importedPaths = <String>{};
+  for (var path in paths) {
+    _parseReferences(importedPaths, path);
+  }
+
+  // Find the ".dart" files that don't end in "_test.dart" but also aren't used
+  // by another library. Those should probably be tests.
+  var hasOrphan = false;
+  for (var path in paths) {
+    if (!path.endsWith('_test.dart') && !importedPaths.contains(path)) {
+      print('Suspected orphan: $path');
+      hasOrphan = true;
+    }
+  }
+
+  if (!hasOrphan) print('No orphans :)');
+}
+
+void _parseReferences(Set<String> importedPaths, String filePath) {
+  var absolute = Path(filePath).absolute.toNativePath();
+  var parseResult = _analysisContext.currentSession.getParsedUnit2(absolute);
+  var unit = (parseResult as ParsedUnitResult).unit;
+
+  void add(String importPath) {
+    if (importPath.startsWith('dart:')) return;
+
+    var resolved = Uri.file(filePath).resolve(importPath).path;
+    importedPaths.add(resolved);
+  }
+
+  for (var directive in unit.directives) {
+    if (directive is UriBasedDirective) {
+      add(directive.uri.stringValue);
+    }
+  }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 60d5468..30b3cd5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 102
+PRERELEASE 103
 PRERELEASE_PATCH 0
\ No newline at end of file