Unify QualifiedName and Identifier

Change-Id: Ie7e7131c58186efd5ed4e81e2f010cc240ce8d03
Reviewed-on: https://dart-review.googlesource.com/73341
Reviewed-by: Jens Johansen <jensj@google.com>
Commit-Queue: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/builder.dart b/pkg/front_end/lib/src/fasta/builder/builder.dart
index 914ee9e..2efbcb9 100644
--- a/pkg/front_end/lib/src/fasta/builder/builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/builder.dart
@@ -4,6 +4,14 @@
 
 library fasta.builder;
 
+export '../identifiers.dart'
+    show
+        Identifier,
+        InitializedIdentifier,
+        QualifiedName,
+        deprecated_extractToken,
+        flattenName;
+
 export '../scope.dart' show AccessErrorBuilder, Scope, ScopeBuilder;
 
 export 'builtin_type_builder.dart' show BuiltinTypeBuilder;
@@ -44,8 +52,6 @@
 
 export 'procedure_builder.dart' show ProcedureBuilder;
 
-export 'qualified_name.dart' show QualifiedName;
-
 export 'type_builder.dart' show TypeBuilder;
 
 export 'type_declaration_builder.dart' show TypeDeclarationBuilder;
diff --git a/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart b/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
index bc7a3cb..d3f8d63 100644
--- a/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/constructor_reference_builder.dart
@@ -14,7 +14,8 @@
         PrefixBuilder,
         QualifiedName,
         Scope,
-        TypeBuilder;
+        TypeBuilder,
+        flattenName;
 
 class ConstructorReferenceBuilder {
   final int charOffset;
@@ -34,14 +35,17 @@
       Declaration parent, this.charOffset)
       : fileUri = parent.fileUri;
 
-  String get fullNameForErrors => "$name${suffix == null ? '' : '.$suffix'}";
+  String get fullNameForErrors {
+    return "${flattenName(name, charOffset, fileUri)}"
+        "${suffix == null ? '' : '.$suffix'}";
+  }
 
   void resolveIn(Scope scope, LibraryBuilder accessingLibrary) {
     final name = this.name;
     Declaration declaration;
     if (name is QualifiedName) {
-      String prefix = name.prefix;
-      String middle = name.suffix;
+      String prefix = name.qualifier;
+      String middle = name.name;
       declaration = scope.lookup(prefix, charOffset, fileUri);
       if (declaration is PrefixBuilder) {
         PrefixBuilder prefix = declaration;
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index 1c3c79f..41ae499 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -10,16 +10,20 @@
         templateMissingExplicitTypeArguments,
         templateTypeArgumentMismatch;
 
+import '../problems.dart' show unhandled;
+
 import 'builder.dart'
     show
         Declaration,
+        Identifier,
         InvalidTypeBuilder,
         LibraryBuilder,
         PrefixBuilder,
         QualifiedName,
         Scope,
         TypeBuilder,
-        TypeDeclarationBuilder;
+        TypeDeclarationBuilder,
+        flattenName;
 
 abstract class NamedTypeBuilder<T extends TypeBuilder, R> extends TypeBuilder {
   final Object name;
@@ -45,21 +49,34 @@
     final name = this.name;
     Declaration member;
     if (name is QualifiedName) {
-      Declaration prefix = scope.lookup(name.prefix, charOffset, fileUri);
+      Object qualifier = name.qualifier;
+      String prefixName = flattenName(qualifier, charOffset, fileUri);
+      Declaration prefix = scope.lookup(prefixName, charOffset, fileUri);
       if (prefix is PrefixBuilder) {
-        member = prefix.lookup(name.suffix, name.charOffset, fileUri);
+        member = prefix.lookup(name.name, name.charOffset, fileUri);
       }
-    } else {
+    } else if (name is String) {
       member = scope.lookup(name, charOffset, fileUri);
+    } else {
+      unhandled("${name.runtimeType}", "resolveIn", charOffset, fileUri);
     }
     if (member is TypeDeclarationBuilder) {
       declaration = member.origin;
       if (arguments == null && declaration.typeVariablesCount != 0) {
+        String typeName;
+        int typeNameOffset;
+        if (name is Identifier) {
+          typeName = name.name;
+          typeNameOffset = name.charOffset;
+        } else {
+          typeName = name;
+          typeNameOffset = charOffset;
+        }
         library.addProblem(
             templateMissingExplicitTypeArguments
                 .withArguments(declaration.typeVariablesCount),
-            charOffset,
-            "$name".length,
+            typeNameOffset,
+            typeName.length,
             fileUri);
       }
       return;
diff --git a/pkg/front_end/lib/src/fasta/builder/qualified_name.dart b/pkg/front_end/lib/src/fasta/builder/qualified_name.dart
deleted file mode 100644
index e879e46..0000000
--- a/pkg/front_end/lib/src/fasta/builder/qualified_name.dart
+++ /dev/null
@@ -1,15 +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 fasta.qualified_name;
-
-class QualifiedName {
-  final prefix;
-  final String suffix;
-  final int charOffset;
-
-  QualifiedName(this.prefix, this.suffix, this.charOffset);
-
-  toString() => "$prefix.$suffix";
-}
diff --git a/pkg/front_end/lib/src/fasta/identifiers.dart b/pkg/front_end/lib/src/fasta/identifiers.dart
new file mode 100644
index 0000000..64af67b
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/identifiers.dart
@@ -0,0 +1,144 @@
+// 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 fasta.qualified_name;
+
+import 'package:kernel/ast.dart' show Expression;
+
+import 'problems.dart' show unhandled, unsupported;
+
+import 'scanner.dart' show Token;
+
+class Identifier {
+  final String name;
+  final int charOffset;
+
+  Identifier(Token token)
+      : name = token.lexeme,
+        charOffset = token.charOffset;
+
+  Identifier._(this.name, this.charOffset);
+
+  factory Identifier.preserveToken(Token token) {
+    return new _TokenIdentifier(token);
+  }
+
+  Expression get initializer => null;
+
+  QualifiedName withQualifier(Object qualifier) {
+    return new QualifiedName._(qualifier, name, charOffset);
+  }
+
+  @override
+  String toString() => "identifier($name)";
+}
+
+class _TokenIdentifier implements Identifier {
+  final Token token;
+
+  _TokenIdentifier(this.token);
+
+  @override
+  String get name => token.lexeme;
+
+  @override
+  int get charOffset => token.charOffset;
+
+  @override
+  Expression get initializer => null;
+
+  @override
+  QualifiedName withQualifier(Object qualifier) {
+    return new _TokenQualifiedName(qualifier, token);
+  }
+
+  @override
+  String toString() => "token-identifier($name)";
+}
+
+class InitializedIdentifier extends _TokenIdentifier {
+  @override
+  final Expression initializer;
+
+  InitializedIdentifier(_TokenIdentifier identifier, this.initializer)
+      : super(identifier.token);
+
+  @override
+  QualifiedName withQualifier(Object qualifier) {
+    return unsupported("withQualifier", charOffset, null);
+  }
+
+  @override
+  String toString() => "initialized-identifier($name, $initializer)";
+}
+
+class QualifiedName extends Identifier {
+  final Object qualifier;
+
+  QualifiedName(this.qualifier, Token suffix) : super(suffix);
+
+  QualifiedName._(this.qualifier, String name, int charOffset)
+      : super._(name, charOffset);
+
+  @override
+  QualifiedName withQualifier(Object qualifier) {
+    return unsupported("withQualifier", charOffset, null);
+  }
+
+  @override
+  String toString() => "qualified-name($qualifier, $name)";
+}
+
+class _TokenQualifiedName extends _TokenIdentifier implements QualifiedName {
+  @override
+  final Object qualifier;
+
+  _TokenQualifiedName(this.qualifier, Token suffix)
+      : assert(qualifier is! Identifier || qualifier is _TokenIdentifier),
+        super(suffix);
+
+  @override
+  QualifiedName withQualifier(Object qualifier) {
+    return unsupported("withQualifier", charOffset, null);
+  }
+
+  @override
+  String toString() => "token-qualified-name($qualifier, $name)";
+}
+
+void flattenQualifiedNameOn(
+    QualifiedName name, StringBuffer buffer, int charOffset, Uri fileUri) {
+  final Object qualifier = name.qualifier;
+  if (qualifier is QualifiedName) {
+    flattenQualifiedNameOn(qualifier, buffer, charOffset, fileUri);
+  } else if (qualifier is Identifier) {
+    buffer.write(qualifier.name);
+  } else if (qualifier is String) {
+    buffer.write(qualifier);
+  } else {
+    unhandled("${qualifier.runtimeType}", "flattenQualifiedNameOn", charOffset,
+        fileUri);
+  }
+  buffer.write(".");
+  buffer.write(name.name);
+}
+
+String flattenName(Object name, int charOffset, Uri fileUri) {
+  if (name is String) {
+    return name;
+  } else if (name is QualifiedName) {
+    StringBuffer buffer = new StringBuffer();
+    flattenQualifiedNameOn(name, buffer, charOffset, fileUri);
+    return "$buffer";
+  } else if (name is Identifier) {
+    return name.name;
+  } else {
+    unhandled("${name.runtimeType}", "flattenName", charOffset, fileUri);
+  }
+}
+
+Token deprecated_extractToken(Identifier identifier) {
+  _TokenIdentifier tokenIdentifier = identifier;
+  return tokenIdentifier?.token;
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index b559fa4..82fcfe8 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -453,7 +453,9 @@
       if (expression is Identifier) {
         Identifier identifier = expression;
         expression = new UnresolvedNameGenerator(
-            this, identifier.token, new Name(identifier.name, library.library));
+            this,
+            deprecated_extractToken(identifier),
+            new Name(identifier.name, library.library));
       }
       if (name?.isNotEmpty ?? false) {
         Token period = periodBeforeName ?? beginToken.next.next;
@@ -1564,7 +1566,7 @@
       addProblem(
           fasta.messageNotAConstantExpression, token.charOffset, token.length);
     }
-    push(new Identifier(token));
+    push(new Identifier.preserveToken(token));
   }
 
   /// Look up [name] in [scope] using [token] as location information (both to
@@ -1703,9 +1705,9 @@
   @override
   void handleQualified(Token period) {
     debugEvent("Qualified");
-    Identifier name = pop();
-    Object receiver = pop();
-    push([receiver, name]);
+    Identifier identifier = pop();
+    Object qualifier = pop();
+    push(identifier.withQualifier(qualifier));
   }
 
   @override
@@ -1905,12 +1907,12 @@
     bool isFinal = (currentLocalVariableModifiers & finalMask) != 0;
     assert(isConst == (constantContext == ConstantContext.inferred));
     push(new VariableDeclarationJudgment(identifier.name, functionNestingLevel,
-        forSyntheticToken: identifier.token.isSynthetic,
+        forSyntheticToken: deprecated_extractToken(identifier).isSynthetic,
         initializer: initializer,
         type: currentLocalVariableType,
         isFinal: isFinal,
         isConst: isConst)
-      ..fileOffset = offsetForToken(identifier.token)
+      ..fileOffset = identifier.charOffset
       ..fileEqualsOffset = offsetForToken(equalsToken));
   }
 
@@ -2273,19 +2275,16 @@
     debugEvent("Type");
     List<DartType> arguments = pop();
     Object name = pop();
-    if (name is List<Object>) {
-      List<Object> list = name;
-      if (list.length != 2) {
-        unexpected("${list.length}", "2", beginToken.charOffset, uri);
-      }
-      Object prefix = list[0];
-      Identifier suffix = list[1];
+    if (name is QualifiedName) {
+      QualifiedName qualified = name;
+      Object prefix = qualified.qualifier;
+      Token suffix = deprecated_extractToken(qualified);
       if (prefix is Generator) {
-        name = prefix.prefixedLookup(suffix.token);
+        name = prefix.qualifiedLookup(suffix);
       } else {
-        String displayName = debugName(getNodeName(prefix), suffix.name);
+        String displayName = debugName(getNodeName(prefix), suffix.lexeme);
         addProblem(fasta.templateNotAType.withArguments(displayName),
-            offsetForToken(beginToken), lengthOfSpan(beginToken, suffix.token));
+            offsetForToken(beginToken), lengthOfSpan(beginToken, suffix));
         push(const InvalidType());
         return;
       }
@@ -2492,14 +2491,15 @@
     } else {
       variable = new VariableDeclarationJudgment(
           name?.name, functionNestingLevel,
-          forSyntheticToken: name?.token?.isSynthetic ?? false,
+          forSyntheticToken:
+              deprecated_extractToken(name)?.isSynthetic ?? false,
           type: type,
           initializer: name?.initializer,
           isFinal: isFinal,
           isConst: isConst);
       if (name != null) {
         // TODO(ahe): Need an offset when name is null.
-        variable.fileOffset = offsetForToken(name.token);
+        variable.fileOffset = name.charOffset;
       }
     }
     if (annotations != null) {
@@ -2567,7 +2567,7 @@
     debugEvent("ValuedFormalParameter");
     Expression initializer = popForValue();
     Identifier name = pop();
-    push(new InitializedIdentifier(name.token, initializer));
+    push(new InitializedIdentifier(name, initializer));
   }
 
   @override
@@ -2832,21 +2832,21 @@
     Identifier identifier;
     List<DartType> typeArguments = pop();
     Object type = pop();
-    if (type is List<Object>) {
-      List<Object> list = type;
-      Object prefix = list[0];
-      identifier = list[1];
-      if (prefix is TypeUseGenerator) {
-        type = prefix;
+    if (type is QualifiedName) {
+      identifier = type;
+      QualifiedName qualified = type;
+      Object qualifier = qualified.qualifier;
+      if (qualifier is TypeUseGenerator) {
+        type = qualifier;
         if (typeArguments != null) {
           addProblem(fasta.messageConstructorWithTypeArguments,
-              identifier.token.charOffset, identifier.name.length);
+              identifier.charOffset, identifier.name.length);
         }
-      } else if (prefix is Generator) {
-        type = prefix.prefixedLookup(identifier.token);
+      } else if (qualifier is Generator) {
+        type = qualifier.qualifiedLookup(deprecated_extractToken(identifier));
         identifier = null;
       } else {
-        unhandled("${prefix.runtimeType}", "pushQualifiedReference",
+        unhandled("${qualifier.runtimeType}", "pushQualifiedReference",
             start.charOffset, uri);
       }
     }
@@ -3090,7 +3090,8 @@
       Token nameToken, int offset, Constness constness) {
     Arguments arguments = pop();
     Identifier nameLastIdentifier = pop(NullValue.Identifier);
-    Token nameLastToken = nameLastIdentifier?.token ?? nameToken;
+    Token nameLastToken =
+        deprecated_extractToken(nameLastIdentifier) ?? nameToken;
     String name = pop();
     List<DartType> typeArguments = pop();
 
@@ -3263,17 +3264,18 @@
     Expression value = popForValue();
     Identifier identifier = pop();
     push(new NamedExpressionJudgment(
-        tokensSaver?.namedExpressionTokens(identifier.token, colon),
-        identifier.token.lexeme,
+        tokensSaver?.namedExpressionTokens(
+            deprecated_extractToken(identifier), colon),
+        identifier.name,
         value)
-      ..fileOffset = offsetForToken(identifier.token));
+      ..fileOffset = identifier.charOffset);
   }
 
   @override
   void endFunctionName(Token beginToken, Token token) {
     debugEvent("FunctionName");
     Identifier name = pop();
-    Token nameToken = name.token;
+    Token nameToken = deprecated_extractToken(name);
     VariableDeclaration variable = new VariableDeclarationJudgment(
         name.name, functionNestingLevel,
         forSyntheticToken: nameToken.isSynthetic,
@@ -3282,7 +3284,7 @@
       ..fileOffset = offsetForToken(nameToken);
     if (scope.local[variable.name] != null) {
       addProblem(fasta.templateDuplicatedName.withArguments(variable.name),
-          offsetForToken(nameToken), nameToken.length);
+          name.charOffset, nameToken.length);
     }
     push(new FunctionDeclarationJudgment(
         variable,
@@ -3555,7 +3557,7 @@
   void handleLabel(Token token) {
     debugEvent("Label");
     Identifier identifier = pop();
-    push(forest.label(identifier.token, token));
+    push(forest.label(deprecated_extractToken(identifier), token));
   }
 
   @override
@@ -3896,8 +3898,8 @@
               continueKeyword.next.charOffset));
           return;
         }
-        switchScope.forwardDeclareLabel(identifier.name,
-            target = createGotoTarget(offsetForToken(identifier.token)));
+        switchScope.forwardDeclareLabel(
+            identifier.name, target = createGotoTarget(identifier.charOffset));
       }
       if (target.isGotoTarget &&
           target.functionNestingLevel == functionNestingLevel) {
@@ -3979,7 +3981,7 @@
       // Something went wrong when pre-parsing the type variables.
       // Assume an error is reported elsewhere.
       variable = new KernelTypeVariableBuilder(
-          name.name, library, offsetForToken(name.token), null);
+          name.name, library, name.charOffset, null);
       variable.binder = _typeInferrer.binderForTypeVariable(
           variable, variable.charOffset, variable.name);
     }
@@ -4329,7 +4331,7 @@
   @override
   void handleSymbolVoid(Token token) {
     debugEvent("SymbolVoid");
-    push(new Identifier(token));
+    push(new Identifier.preserveToken(token));
   }
 
   @override
@@ -4544,17 +4546,6 @@
   }
 }
 
-class Identifier {
-  final Token token;
-  String get name => token.lexeme;
-
-  Identifier(this.token);
-
-  Expression get initializer => null;
-
-  String toString() => "identifier($name)";
-}
-
 class Operator {
   final Token token;
   String get name => token.stringValue;
@@ -4566,14 +4557,6 @@
   String toString() => "operator($name)";
 }
 
-class InitializedIdentifier extends Identifier {
-  final Expression initializer;
-
-  InitializedIdentifier(Token token, this.initializer) : super(token);
-
-  String toString() => "initialized-identifier($name, $initializer)";
-}
-
 class JumpTarget extends Declaration {
   final List<Statement> users = <Statement>[];
 
@@ -4835,6 +4818,8 @@
     return node.isSuper ? "super" : "this";
   } else if (node is Generator) {
     return node.plainNameForRead;
+  } else if (node is QualifiedName) {
+    return flattenName(node, node.charOffset, null);
   } else {
     return unhandled("${node.runtimeType}", "getNodeName", -1, null);
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 7a06db1..eb6b1dc 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -230,7 +230,7 @@
     return const InvalidType();
   }
 
-  /* Expression | Generator */ Object prefixedLookup(Token name) {
+  /* Expression | Generator */ Object qualifiedLookup(Token name) {
     return new UnexpectedQualifiedUseGenerator(helper, name, this, false);
   }
 
@@ -882,7 +882,7 @@
   }
 
   @override
-  /* Expression | Generator */ Object prefixedLookup(Token name) {
+  /* Expression | Generator */ Object qualifiedLookup(Token name) {
     helper.storeUnresolved(token);
     return new UnexpectedQualifiedUseGenerator(helper, name, this, true);
   }
@@ -1110,7 +1110,7 @@
   Expression buildSimpleRead() => makeInvalidRead();
 
   @override
-  /* Expression | Generator */ Object prefixedLookup(Token name) {
+  /* Expression | Generator */ Object qualifiedLookup(Token name) {
     if (helper.constantContext != ConstantContext.none && prefix.deferred) {
       helper.addProblem(
           templateCantUseDeferredPrefixAsConstant.withArguments(token),
@@ -1149,7 +1149,7 @@
     if (send is IncompleteSendGenerator) {
       assert(send.name.name == send.token.lexeme,
           "'${send.name.name}' != ${send.token.lexeme}");
-      Object result = prefixedLookup(send.token);
+      Object result = qualifiedLookup(send.token);
       if (send is SendAccessGenerator) {
         result = helper.finishSend(
             result,
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index b66c371..26da213 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -14,7 +14,7 @@
         Procedure,
         Statement;
 
-import 'body_builder.dart' show Identifier, LabelTarget;
+import 'body_builder.dart' show LabelTarget;
 
 import 'expression_generator.dart' show Generator, PrefixUseGenerator;
 
@@ -22,6 +22,7 @@
 
 import 'kernel_builder.dart'
     show
+        Identifier,
         LoadLibraryBuilder,
         PrefixBuilder,
         TypeDeclarationBuilder,
@@ -29,7 +30,7 @@
 
 import '../scanner.dart' show Token;
 
-export 'body_builder.dart' show Identifier, Operator;
+export 'body_builder.dart' show Operator;
 
 export 'constness.dart' show Constness;
 
@@ -39,6 +40,7 @@
 
 export 'kernel_builder.dart'
     show
+        Identifier,
         LoadLibraryBuilder,
         PrefixBuilder,
         TypeDeclarationBuilder,
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 0ae7b46..ba6a4e2 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
@@ -319,14 +319,6 @@
       }
     }
     if (type is KernelMixinApplicationBuilder) {
-      String extractName(name) {
-        if (name is QualifiedName) {
-          return name.suffix;
-        } else {
-          return name;
-        }
-      }
-
       // Documentation below assumes the given mixin application is in one of
       // these forms:
       //
@@ -1306,3 +1298,5 @@
   return unhandled("no library parent", "${declaration.runtimeType}",
       declaration.charOffset, declaration.fileUri);
 }
+
+String extractName(name) => name is QualifiedName ? name.name : name;
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_named_type_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_named_type_builder.dart
index b10ffcf..cbccf69 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_named_type_builder.dart
@@ -22,7 +22,8 @@
         NamedTypeBuilder,
         TypeBuilder,
         TypeDeclarationBuilder,
-        TypeVariableBuilder;
+        TypeVariableBuilder,
+        flattenName;
 
 class KernelNamedTypeBuilder
     extends NamedTypeBuilder<KernelTypeBuilder, DartType>
@@ -34,7 +35,8 @@
       [Message message]) {
     // TODO(ahe): Consider if it makes sense to pass a QualifiedName to
     // KernelInvalidTypeBuilder?
-    return new KernelInvalidTypeBuilder("$name", charOffset, fileUri, message);
+    return new KernelInvalidTypeBuilder(
+        flattenName(name, charOffset, fileUri), charOffset, fileUri, message);
   }
 
   Supertype handleInvalidSupertype(
@@ -43,8 +45,10 @@
         ? templateSupertypeIsTypeVariable
         : templateSupertypeIsIllegal;
     library.addProblem(
-        template.withArguments("$name"), charOffset, noLength, fileUri,
-        severity: Severity.error);
+        template.withArguments(flattenName(name, charOffset, fileUri)),
+        charOffset,
+        noLength,
+        fileUri);
     return null;
   }
 
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 1d0fa47..ad4a1df 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -320,7 +320,8 @@
     debugEvent("handleQualified");
     String suffix = pop();
     var prefix = pop();
-    push(new QualifiedName(prefix, suffix, period.charOffset));
+    assert(identical(suffix, period.next.lexeme));
+    push(new QualifiedName(prefix, period.next));
   }
 
   @override
@@ -823,7 +824,7 @@
     Declaration declaration;
     String suffix;
     if (nameOrQualified is QualifiedName) {
-      suffix = nameOrQualified.suffix;
+      suffix = nameOrQualified.name;
     } else {
       suffix = nameOrQualified == currentClass.name ? "" : nameOrQualified;
     }
diff --git a/pkg/front_end/lib/src/fasta/source/outline_builder.dart b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
index de663b7..8bbedb6 100644
--- a/pkg/front_end/lib/src/fasta/source/outline_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/outline_builder.dart
@@ -377,9 +377,11 @@
     debugEvent("handleQualified");
     int suffixOffset = pop();
     String suffix = pop();
+    assert(identical(suffix, period.next.lexeme));
+    assert(suffixOffset == period.next.charOffset);
     int offset = pop();
     var prefix = pop();
-    push(new QualifiedName(prefix, suffix, suffixOffset));
+    push(new QualifiedName(prefix, period.next));
     push(offset);
   }
 
@@ -391,7 +393,7 @@
     Object name = pop();
     List<MetadataBuilder> metadata = pop();
     library.documentationComment = documentationComment;
-    library.name = "${name}";
+    library.name = flattenName(name, offsetForToken(libraryKeyword), uri);
     library.metadata = metadata;
   }
 
@@ -1207,7 +1209,8 @@
     Object containingLibrary = pop();
     List<MetadataBuilder> metadata = pop();
     if (hasName) {
-      library.addPartOf(metadata, "$containingLibrary", null, charOffset);
+      library.addPartOf(metadata,
+          flattenName(containingLibrary, charOffset, uri), null, charOffset);
     } else {
       library.addPartOf(metadata, null, containingLibrary, charOffset);
     }
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 37b6584..5923555 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -200,8 +200,8 @@
     String prefix;
     String suffix;
     if (name is QualifiedName) {
-      prefix = name.prefix;
-      suffix = name.suffix;
+      prefix = name.qualifier;
+      suffix = name.name;
     } else {
       prefix = name;
       suffix = null;
@@ -822,7 +822,7 @@
       for (UnresolvedType<T> type in types) {
         Object nameOrQualified = type.builder.name;
         String name = nameOrQualified is QualifiedName
-            ? nameOrQualified.prefix
+            ? nameOrQualified.qualifier
             : nameOrQualified;
         TypeVariableBuilder builder;
         if (name != null) {
diff --git a/pkg/front_end/testcases/qualified.dart b/pkg/front_end/testcases/qualified.dart
index 794f6bd..397020d 100644
--- a/pkg/front_end/testcases/qualified.dart
+++ b/pkg/front_end/testcases/qualified.dart
@@ -15,6 +15,8 @@
 
 class WithMixin extends lib.Supertype with lib.Mixin {}
 
+class IllegalSupertype extends lib.VoidFunction {}
+
 main() {
   new C<String>();
   new C<String>.a();
@@ -24,4 +26,5 @@
   new lib.C<String>.b();
   new WithMixin().supertypeMethod();
   new WithMixin().foo();
+  new IllegalSupertype();
 }
diff --git a/pkg/front_end/testcases/qualified.dart.direct.expect b/pkg/front_end/testcases/qualified.dart.direct.expect
index e062fce..b5e33b4 100644
--- a/pkg/front_end/testcases/qualified.dart.direct.expect
+++ b/pkg/front_end/testcases/qualified.dart.direct.expect
@@ -7,6 +7,10 @@
 // pkg/front_end/testcases/qualified.dart:11:19: Error: Type 'lib.Missing' not found.
 // class Bad extends lib.Missing {
 //                   ^
+//
+// pkg/front_end/testcases/qualified.dart:18:7: Error: The type 'lib.VoidFunction' can't be used as supertype.
+// class IllegalSupertype extends lib.VoidFunction {}
+//       ^
 
 library test.qualified.main;
 import self as self;
@@ -24,6 +28,11 @@
     : super lib::Supertype::•()
     ;
 }
+class IllegalSupertype extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
 class C<T extends core::Object = dynamic> extends core::Object { // from org-dartlang-testcase:///qualified_part.dart
   static field dynamic _redirecting# = <dynamic>[self::C::b];
   constructor •() → void
@@ -44,4 +53,5 @@
   new lib::C::a<core::String>();
   new self::WithMixin::•().supertypeMethod();
   new self::WithMixin::•().foo();
+  new self::IllegalSupertype::•();
 }
diff --git a/pkg/front_end/testcases/qualified.dart.direct.transformed.expect b/pkg/front_end/testcases/qualified.dart.direct.transformed.expect
index a9175a1..3ffad7c 100644
--- a/pkg/front_end/testcases/qualified.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/qualified.dart.direct.transformed.expect
@@ -7,6 +7,10 @@
 // pkg/front_end/testcases/qualified.dart:11:19: Error: Type 'lib.Missing' not found.
 // class Bad extends lib.Missing {
 //                   ^
+//
+// pkg/front_end/testcases/qualified.dart:18:7: Error: The type 'lib.VoidFunction' can't be used as supertype.
+// class IllegalSupertype extends lib.VoidFunction {}
+//       ^
 
 library test.qualified.main;
 import self as self;
@@ -30,6 +34,11 @@
     : super lib::Supertype::•()
     ;
 }
+class IllegalSupertype extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
 class C<T extends core::Object = dynamic> extends core::Object { // from org-dartlang-testcase:///qualified_part.dart
   static field dynamic _redirecting# = <dynamic>[self::C::b];
   constructor •() → void
@@ -50,4 +59,5 @@
   new lib::C::a<core::String>();
   new self::WithMixin::•().supertypeMethod();
   new self::WithMixin::•().foo();
+  new self::IllegalSupertype::•();
 }
diff --git a/pkg/front_end/testcases/qualified.dart.outline.expect b/pkg/front_end/testcases/qualified.dart.outline.expect
index b04c33a..f72c9c6 100644
--- a/pkg/front_end/testcases/qualified.dart.outline.expect
+++ b/pkg/front_end/testcases/qualified.dart.outline.expect
@@ -15,6 +15,10 @@
   synthetic constructor •() → void
     ;
 }
+class IllegalSupertype extends core::Object {
+  synthetic constructor •() → void
+    ;
+}
 class C<T extends core::Object = dynamic> extends core::Object { // from org-dartlang-testcase:///qualified_part.dart
   static field dynamic _redirecting# = <dynamic>[self::C::b];
   constructor •() → void
diff --git a/pkg/front_end/testcases/qualified.dart.strong.expect b/pkg/front_end/testcases/qualified.dart.strong.expect
index fa6e54e..f1a7aea 100644
--- a/pkg/front_end/testcases/qualified.dart.strong.expect
+++ b/pkg/front_end/testcases/qualified.dart.strong.expect
@@ -11,6 +11,10 @@
 // pkg/front_end/testcases/qualified.dart:11:19: Error: Type 'lib.Missing' not found.
 // class Bad extends lib.Missing {
 //                   ^
+//
+// pkg/front_end/testcases/qualified.dart:18:7: Error: The type 'lib.VoidFunction' can't be used as supertype.
+// class IllegalSupertype extends lib.VoidFunction {}
+//       ^
 
 library test.qualified.main;
 import self as self;
@@ -28,6 +32,11 @@
     : super lib::Supertype::•()
     ;
 }
+class IllegalSupertype extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
 class C<T extends core::Object = dynamic> extends core::Object { // from org-dartlang-testcase:///qualified_part.dart
   static field dynamic _redirecting# = <dynamic>[self::C::b];
   constructor •() → void
@@ -48,4 +57,5 @@
   new lib::C::a<core::String>();
   new self::WithMixin::•().{lib::Supertype::supertypeMethod}();
   new self::WithMixin::•().{lib::Mixin::foo}();
+  new self::IllegalSupertype::•();
 }
diff --git a/pkg/front_end/testcases/qualified.dart.strong.transformed.expect b/pkg/front_end/testcases/qualified.dart.strong.transformed.expect
index 9894c5e..34c0697 100644
--- a/pkg/front_end/testcases/qualified.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/qualified.dart.strong.transformed.expect
@@ -11,6 +11,10 @@
 // pkg/front_end/testcases/qualified.dart:11:19: Error: Type 'lib.Missing' not found.
 // class Bad extends lib.Missing {
 //                   ^
+//
+// pkg/front_end/testcases/qualified.dart:18:7: Error: The type 'lib.VoidFunction' can't be used as supertype.
+// class IllegalSupertype extends lib.VoidFunction {}
+//       ^
 
 library test.qualified.main;
 import self as self;
@@ -34,6 +38,11 @@
     : super lib::Supertype::•()
     ;
 }
+class IllegalSupertype extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+}
 class C<T extends core::Object = dynamic> extends core::Object { // from org-dartlang-testcase:///qualified_part.dart
   static field dynamic _redirecting# = <dynamic>[self::C::b];
   constructor •() → void
@@ -54,4 +63,5 @@
   new lib::C::a<core::String>();
   new self::WithMixin::•().{lib::Supertype::supertypeMethod}();
   new self::WithMixin::•().{lib::Mixin::foo}();
+  new self::IllegalSupertype::•();
 }
diff --git a/pkg/front_end/testcases/qualified_lib.dart b/pkg/front_end/testcases/qualified_lib.dart
index dba8057..ae00a71 100644
--- a/pkg/front_end/testcases/qualified_lib.dart
+++ b/pkg/front_end/testcases/qualified_lib.dart
@@ -23,3 +23,5 @@
     print("I'm Mixin.foo");
   }
 }
+
+typedef VoidFunction = void Function();