[ddc] Add constructor symbol information

- Adds tests for various types of constructors.
- Recognize when a constructor is const and mark the class symbol
  accordingly.

Change-Id: I9ae7fc2837a6821b196f06a267603fd18c3e5075
Issue: https://github.com/dart-lang/sdk/issues/40273
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208362
Reviewed-by: Anna Gringauze <annagrin@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 0297cf8..f55ce1a 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -1046,8 +1046,9 @@
           if (name != '' || hasUnnamedSuper)
             _emitSuperConstructorCall(className, name, jsParams),
         ];
-        body.add(_addConstructorToClass(
-            c, className, name, js_ast.Fun(jsParams, js_ast.Block(ctorBody))));
+        // TODO(nshahan) Record the name for this constructor in memberNames.
+        body.add(_addConstructorToClass(c, className, _constructorName(name),
+            js_ast.Fun(jsParams, js_ast.Block(ctorBody))));
       }
     }
 
@@ -1197,14 +1198,17 @@
       return body;
     }
 
-    void addConstructor(String name, js_ast.Expression jsCtor) {
+    void addConstructor(js_ast.LiteralString name, js_ast.Expression jsCtor) {
       body.add(_addConstructorToClass(c, className, name, jsCtor));
     }
 
     var fields = c.fields;
     for (var ctor in c.constructors) {
       if (ctor.isExternal) continue;
-      addConstructor(ctor.name.text, _emitConstructor(ctor, fields, className));
+      var constructorName = _constructorName(ctor.name.text);
+      memberNames[ctor] = constructorName.valueWithoutQuotes;
+      addConstructor(
+          constructorName, _emitConstructor(ctor, fields, className));
     }
 
     // If classElement has only factory constructors, and it can be mixed in,
@@ -1617,7 +1621,7 @@
     return body;
   }
 
-  js_ast.Expression _constructorName(String name) {
+  js_ast.LiteralString _constructorName(String name) {
     if (name == '') {
       // Default constructors (factory or not) use `new` as their name.
       return propertyName('new');
@@ -1773,8 +1777,8 @@
   }
 
   js_ast.Statement _addConstructorToClass(Class c, js_ast.Expression className,
-      String name, js_ast.Expression jsCtor) {
-    jsCtor = defineValueOnClass(c, className, _constructorName(name), jsCtor);
+      js_ast.LiteralString name, js_ast.Expression jsCtor) {
+    jsCtor = defineValueOnClass(c, className, name, jsCtor);
     return js.statement('#.prototype = #.prototype;', [jsCtor, className]);
   }
 
@@ -2084,9 +2088,10 @@
     /// [_emitFunction] instead.
     var name = node.name.text;
     var jsBody = _emitSyncFunctionBody(function, name);
+    var jsName = _constructorName(name);
+    memberNames[node] = jsName.valueWithoutQuotes;
 
-    return js_ast.Method(
-        _constructorName(name), js_ast.Fun(_emitParameters(function), jsBody),
+    return js_ast.Method(jsName, js_ast.Fun(_emitParameters(function), jsBody),
         isStatic: true)
       ..sourceInformation = _nodeEnd(node.fileEndOffset);
   }
diff --git a/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart b/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart
index e99dc07..2b458bb 100644
--- a/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart
+++ b/pkg/dev_compiler/lib/src/kernel/module_symbols_collector.dart
@@ -49,12 +49,38 @@
       // TODO(nshahan) How to handle function types or types from other modules?
       type is InterfaceType ? _classJsNames[type.classNode] : null;
 
+  /// Returns the symbol for the function defined by [node].
+  void _createFunctionSymbol(Member node) {
+    var functionSymbol = FunctionSymbol(
+        name: node.name.text,
+        // TODO(nshahan) typeId - probably should canonicalize but keep original
+        // type argument names.
+        // TODO(nshahan) Should we mark all constructors static?
+        isStatic: node is Procedure ? node.isStatic : false,
+        isConst: node.isConst,
+        localId: _memberJsNames[node] ?? _procedureJsNames[node],
+        scopeId: _scopes.last.id,
+        variableIds: <String>[],
+        scopeIds: <String>[],
+        location: SourceLocation(
+            scriptId: _scriptId(node.location.file),
+            tokenPos: node.fileOffset,
+            endTokenPos: node.fileEndOffset));
+
+    _scopes.add(functionSymbol);
+    node.visitChildren(this);
+    _scopes
+      ..removeLast()
+      ..last.scopeIds.add(functionSymbol.id);
+    _moduleSymbols.functions.add(functionSymbol);
+  }
+
   @override
   void visitClass(Class node) {
     var classSymbol = ClassSymbol(
         name: node.name,
         isAbstract: node.isAbstract,
-        // TODO(nshahan) isConst - Does this mean has a const constructor?
+        isConst: node.constructors.any((constructor) => constructor.isConst),
         superClassId: _classJsNames[node.superclass],
         interfaceIds: [
           for (var type in node.implementedTypes) _classJsNames[type.classNode]
@@ -83,6 +109,9 @@
   }
 
   @override
+  void visitConstructor(Constructor node) => _createFunctionSymbol(node);
+
+  @override
   void visitField(Field node) {
     var fieldSymbol = VariableSymbol(
         name: node.name.text,
@@ -149,27 +178,7 @@
     // Also avoid adding information for the static methods introduced by the
     // CFE lowering for constructor tearoffs.
     if (node.function.body == null || isTearOffLowering(node)) return;
-    var functionSymbol = FunctionSymbol(
-        name: node.name.text,
-        // TODO(nshahan) typeId - probably should canonicalize but keep original
-        // type argument names.
-        isStatic: node.isStatic,
-        isConst: node.isConst,
-        localId: _memberJsNames[node] ?? _procedureJsNames[node],
-        scopeId: _scopes.last.id,
-        variableIds: <String>[],
-        scopeIds: <String>[],
-        location: SourceLocation(
-            scriptId: _scriptId(node.location.file),
-            tokenPos: node.fileOffset,
-            endTokenPos: node.fileEndOffset));
-
-    _scopes.add(functionSymbol);
-    node.visitChildren(this);
-    _scopes
-      ..removeLast()
-      ..last.scopeIds.add(functionSymbol.id);
-    _moduleSymbols.functions.add(functionSymbol);
+    _createFunctionSymbol(node);
   }
 
   @override
diff --git a/pkg/dev_compiler/test/module_symbols/class_symbols_test.dart b/pkg/dev_compiler/test/module_symbols/class_symbols_test.dart
index c08995f..7d89f6d0 100644
--- a/pkg/dev_compiler/test/module_symbols/class_symbols_test.dart
+++ b/pkg/dev_compiler/test/module_symbols/class_symbols_test.dart
@@ -10,6 +10,13 @@
 import '../shared_test_options.dart';
 import 'module_symbols_test_shared.dart';
 
+/// Returns the [FunctionSymbol] from [ModuleSymbols] with the [name] in the
+/// original Dart source code or throws an error if there isn't exactly one.
+FunctionSymbol _symbolForDartFunction(ModuleSymbols symbols, String name) =>
+    symbols.functions
+        .where((functionSymbol) => functionSymbol.name == name)
+        .single;
+
 void main() async {
   for (var mode in [
     NullSafetyTestOption('Sound Mode:', true),
@@ -20,6 +27,7 @@
       group('simple class debug symbols', () {
         TestDriver driver;
         ClassSymbol classSymbol;
+        FunctionSymbol functionSymbol;
         final source = '''
           ${options.dartLangComment}
 
@@ -29,6 +37,7 @@
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
+          functionSymbol = result.symbols.functions.single;
         });
         tearDownAll(() {
           driver.cleanUp();
@@ -39,7 +48,9 @@
         test('is not abstract', () async {
           expect(classSymbol.isAbstract, isFalse);
         });
-        // TODO test isConst
+        test('is not const', () async {
+          expect(classSymbol.isConst, isFalse);
+        });
         test('has no superclassId', () async {
           expect(classSymbol.superClassId, isNull);
         });
@@ -71,7 +82,14 @@
         test('no fields', () async {
           expect(classSymbol.variableIds, isEmpty);
         });
-        // TODO only has the implicit constructor in scopeIds.
+        test('only default constructor in functionIds', () {
+          var constructorId = classSymbol.functionIds.single;
+          expect(constructorId, functionSymbol.id);
+          // Default constructor has no name in Dart Kernel AST.
+          expect(functionSymbol.name, isEmpty);
+          // Default constructor is named 'new' in JavaScript.
+          expect(functionSymbol.id, endsWith('A|new'));
+        });
       });
       group('abstract class debug symbols', () {
         TestDriver driver;
@@ -308,14 +326,15 @@
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicInstanceMethod');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
           expect(methodSymbol.id, endsWith('A|publicInstanceMethod'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -340,14 +359,15 @@
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateInstanceMethod');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
           expect(methodSymbol.id, endsWith('A|Symbol(_privateInstanceMethod)'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -372,14 +392,15 @@
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicStaticMethod');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
           expect(methodSymbol.id, endsWith('A|publicStaticMethod'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -404,14 +425,15 @@
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateStaticMethod');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
           expect(methodSymbol.id, endsWith('A|_privateStaticMethod'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -429,21 +451,22 @@
           ${options.dartLangComment}
 
           class A {
-            String get publicGetter() => 'Fosse';
+            String get publicInstanceGetter() => 'Fosse';
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicInstanceGetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|publicGetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|publicInstanceGetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -461,21 +484,22 @@
           ${options.dartLangComment}
 
           class A {
-            String get _privateGetter() => 'Fosse';
+            String get _privateInstanceGetter() => 'Fosse';
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateInstanceGetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|Symbol(_privateGetter)'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|Symbol(_privateInstanceGetter)'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -494,21 +518,22 @@
 
           class A {
             var _value
-            set publicSetter(String v) => _value = v;
+            set publicInstanceSetter(String v) => _value = v;
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicInstanceSetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|publicSetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|publicInstanceSetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -527,21 +552,22 @@
 
           class A {
             var _value
-            set _privateSetter(String v) => _value = v;
+            set _privateInstanceSetter(String v) => _value = v;
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateInstanceSetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|Symbol(_privateSetter)'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|Symbol(_privateInstanceSetter)'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -559,21 +585,22 @@
           ${options.dartLangComment}
 
           class A {
-            static String get publicGetter() => 'Fosse';
+            static String get publicStaticGetter() => 'Fosse';
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicStaticGetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|publicGetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|publicStaticGetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -591,21 +618,22 @@
           ${options.dartLangComment}
 
           class A {
-            static String get _privateGetter() => 'Fosse';
+            static String get _privateStaticGetter() => 'Fosse';
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateStaticGetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|_privateGetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|_privateStaticGetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -624,21 +652,22 @@
 
           class A {
             var _value;
-            static set publicSetter(String v) => _value = v;
+            static set publicStaticSetter(String v) => _value = v;
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicStaticSetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|publicSetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|publicStaticSetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -657,21 +686,22 @@
 
           class A {
             var _value;
-            static set _privateSetter(String v) => _value = v;
+            static set _privateStaticSetter(String v) => _value = v;
           }
           ''';
         setUpAll(() async {
           driver = TestDriver(options, source);
           var result = await driver.compile();
           classSymbol = result.symbols.classes.single;
-          methodSymbol = result.symbols.functions.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateStaticSetter');
         });
         tearDownAll(() {
           driver.cleanUp();
         });
         test('functionId in classSymbol', () async {
-          expect(methodSymbol.id, endsWith('A|_privateSetter'));
-          expect(methodSymbol.id, classSymbol.functionIds.single);
+          expect(methodSymbol.id, endsWith('A|_privateStaticSetter'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
         });
         test('has class scopeId', () async {
           expect(methodSymbol.scopeId, endsWith('|A'));
@@ -681,6 +711,257 @@
           expect(methodSymbol.isStatic, isTrue);
         });
       });
+      group('class public const constructor debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            const A();
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol = _symbolForDartFunction(result.symbols, '');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|new'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+        test('is const', () async {
+          expect(methodSymbol.isConst, isTrue);
+          expect(classSymbol.isConst, isTrue);
+        });
+      });
+      group('class public named constructor debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            A.named();
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol = _symbolForDartFunction(result.symbols, 'named');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|named'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class private named constructor debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A._(this._value);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol = _symbolForDartFunction(result.symbols, '_');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|__'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class unnamed factory debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A._(this._value);
+            factory A() => A._(10);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol = _symbolForDartFunction(result.symbols, '');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|new'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class public named factory debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A._(this._value);
+            factory A.publicFactory() => A._(10);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicFactory');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|publicFactory'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class private named factory debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A._(this._value);
+            factory A._privateFactory() => A._(10);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateFactory');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|_privateFactory'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class public redirecting constructor debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A(this._value);
+            A.publicRedirecting() : this(10);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, 'publicRedirecting');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|publicRedirecting'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
+      group('class private redirecting constructor debug symbols', () {
+        TestDriver driver;
+        ClassSymbol classSymbol;
+        FunctionSymbol methodSymbol;
+        final source = '''
+          ${options.dartLangComment}
+
+          class A {
+            var _value;
+            A(this._value);
+            A._privateRedirecting() : this(10);
+          }
+          ''';
+        setUpAll(() async {
+          driver = TestDriver(options, source);
+          var result = await driver.compile();
+          classSymbol = result.symbols.classes.single;
+          methodSymbol =
+              _symbolForDartFunction(result.symbols, '_privateRedirecting');
+        });
+        tearDownAll(() {
+          driver.cleanUp();
+        });
+        test('functionId in classSymbol', () async {
+          expect(methodSymbol.id, endsWith('A|_privateRedirecting'));
+          expect(classSymbol.functionIds, contains(methodSymbol.id));
+        });
+        test('has class scopeId', () async {
+          expect(methodSymbol.scopeId, endsWith('|A'));
+          expect(methodSymbol.scopeId, classSymbol.id);
+        });
+      });
     });
   }
 }