Add UnlinkedScope

This scope is for use when building an AST before building the outline.

Change-Id: I90786449fb4e7d755e64d40f91c4cb67a02a6082
Reviewed-on: https://dart-review.googlesource.com/57581
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_body_builder.dart b/pkg/analyzer/lib/src/fasta/ast_body_builder.dart
index 4c4d6ec6..92c95b7 100644
--- a/pkg/analyzer/lib/src/fasta/ast_body_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_body_builder.dart
@@ -14,6 +14,9 @@
 import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
 import 'package:kernel/core_types.dart' show CoreTypes;
 
+export 'package:front_end/src/fasta/kernel/unlinked_scope.dart'
+    show UnlinkedScope;
+
 class AstBodyBuilder extends BodyBuilder<Expression, Statement, dynamic> {
   @override
   final AstBuildingForest forest;
@@ -38,4 +41,9 @@
   void enterThenForTypePromotion(Expression condition) {
     // Do nothing.
   }
+
+  @override
+  void logEvent(String name) {
+    throw "Parser event '$name' not implemented";
+  }
 }
diff --git a/pkg/analyzer/lib/src/fasta/ast_building_factory.dart b/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
index e2f5c56..d794f10 100644
--- a/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_building_factory.dart
@@ -602,6 +602,15 @@
   }
 
   @override
+  Generator<Expression, Statement, _Arguments> unlinkedGenerator(
+      ExpressionGeneratorHelper<Expression, Statement, _Arguments> helper,
+      Token token,
+      UnlinkedDeclaration declaration) {
+    // TODO(brianwilkerson) Implement this.
+    throw new UnimplementedError();
+  }
+
+  @override
   Generator<Expression, Statement, _Arguments> unresolvedNameGenerator(
       ExpressionGeneratorHelper<Expression, Statement, _Arguments> helper,
       Token token,
diff --git a/pkg/analyzer/test/src/fasta/body_builder_test_helper.dart b/pkg/analyzer/test/src/fasta/body_builder_test_helper.dart
index 74c0314..aaf8164 100644
--- a/pkg/analyzer/test/src/fasta/body_builder_test_helper.dart
+++ b/pkg/analyzer/test/src/fasta/body_builder_test_helper.dart
@@ -516,8 +516,8 @@
       AstBodyBuilder builder = new AstBodyBuilder(
         library,
         procedureBuilder,
-        library.scope,
-        procedureBuilder.computeFormalParameterScope(library.scope),
+        new UnlinkedScope(),
+        null,
         kernelTarget.loader.hierarchy,
         kernelTarget.loader.coreTypes,
         null /* classBuilder */,
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 c72e5ab..350f96f 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -90,6 +90,7 @@
         ThisAccessGenerator,
         ThisPropertyAccessGenerator,
         TypeUseGenerator,
+        UnlinkedGenerator,
         UnresolvedNameGenerator,
         VariableUseGenerator,
         buildIsNull;
@@ -1391,6 +1392,9 @@
       {bool isQualified: false, PrefixBuilder prefix}) {
     int charOffset = offsetForToken(token);
     Declaration declaration = scope.lookup(name, charOffset, uri);
+    if (declaration is UnlinkedDeclaration) {
+      return new UnlinkedGenerator(this, token, declaration);
+    }
     if (declaration == null &&
         prefix == null &&
         (classBuilder?.isPatch ?? false)) {
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 ab4d78d..c69b304 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -32,7 +32,8 @@
         Identifier,
         LoadLibraryBuilder,
         PrefixBuilder,
-        TypeDeclarationBuilder;
+        TypeDeclarationBuilder,
+        UnlinkedDeclaration;
 
 import 'kernel_ast_api.dart'
     show
@@ -837,3 +838,27 @@
         isSetter: isSetter);
   }
 }
+
+abstract class UnlinkedGenerator<Expression, Statement, Arguments>
+    implements Generator<Expression, Statement, Arguments> {
+  factory UnlinkedGenerator(
+      ExpressionGeneratorHelper<Expression, Statement, Arguments> helper,
+      Token token,
+      UnlinkedDeclaration declaration) {
+    return helper.forest.unlinkedGenerator(helper, token, declaration);
+  }
+
+  UnlinkedDeclaration get declaration;
+
+  @override
+  String get plainNameForRead => declaration.name;
+
+  @override
+  String get debugName => "UnlinkedGenerator";
+
+  @override
+  void printOn(StringSink sink) {
+    sink.write(", name: ");
+    sink.write(declaration.name);
+  }
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
index cfc6ad9..a976f3b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/fangorn.dart
@@ -58,6 +58,7 @@
         KernelThisIndexedAccessGenerator,
         KernelThisPropertyAccessGenerator,
         KernelTypeUseGenerator,
+        KernelUnlinkedGenerator,
         KernelUnresolvedNameGenerator,
         KernelVariableUseGenerator;
 
@@ -109,7 +110,8 @@
         Generator,
         LoadLibraryBuilder,
         PrefixBuilder,
-        TypeDeclarationBuilder;
+        TypeDeclarationBuilder,
+        UnlinkedDeclaration;
 
 /// A shadow tree factory.
 class Fangorn extends Forest<Expression, Statement, Token, Arguments> {
@@ -765,6 +767,14 @@
       Name name) {
     return new KernelUnresolvedNameGenerator(helper, token, name);
   }
+
+  @override
+  KernelUnlinkedGenerator unlinkedGenerator(
+      ExpressionGeneratorHelper<Expression, Statement, Arguments> helper,
+      Token token,
+      UnlinkedDeclaration declaration) {
+    return new KernelUnlinkedGenerator(helper, token, declaration);
+  }
 }
 
 class _VariablesDeclaration extends Statement {
diff --git a/pkg/front_end/lib/src/fasta/kernel/forest.dart b/pkg/front_end/lib/src/fasta/kernel/forest.dart
index 0aeec3d..ac39797 100644
--- a/pkg/front_end/lib/src/fasta/kernel/forest.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/forest.dart
@@ -19,7 +19,11 @@
 import 'expression_generator_helper.dart' show ExpressionGeneratorHelper;
 
 import 'kernel_builder.dart'
-    show LoadLibraryBuilder, PrefixBuilder, TypeDeclarationBuilder;
+    show
+        LoadLibraryBuilder,
+        PrefixBuilder,
+        TypeDeclarationBuilder,
+        UnlinkedDeclaration;
 
 export 'body_builder.dart' show Identifier, Operator;
 
@@ -28,7 +32,11 @@
 export 'expression_generator_helper.dart' show ExpressionGeneratorHelper;
 
 export 'kernel_builder.dart'
-    show LoadLibraryBuilder, PrefixBuilder, TypeDeclarationBuilder;
+    show
+        LoadLibraryBuilder,
+        PrefixBuilder,
+        TypeDeclarationBuilder,
+        UnlinkedDeclaration;
 
 /// A tree factory.
 ///
@@ -459,6 +467,11 @@
       Location location,
       kernel.Name name);
 
+  Generator<Expression, Statement, Arguments> unlinkedGenerator(
+      ExpressionGeneratorHelper<Expression, Statement, Arguments> helper,
+      Location location,
+      UnlinkedDeclaration declaration);
+
   // TODO(ahe): Remove this method when all users are moved here.
   kernel.Arguments castArguments(Arguments arguments) {
     dynamic a = arguments;
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_builder.dart
index aa4860e..a2fbeca 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_builder.dart
@@ -4,6 +4,18 @@
 
 library fasta.kernel_builder;
 
+import 'package:kernel/ast.dart'
+    show
+        Combinator,
+        Constructor,
+        Initializer,
+        Procedure,
+        RedirectingInitializer;
+
+import '../combinator.dart' as fasta;
+
+export '../builder/builder.dart';
+
 export 'kernel_class_builder.dart' show KernelClassBuilder;
 
 export 'kernel_enum_builder.dart' show KernelEnumBuilder;
@@ -12,20 +24,22 @@
 
 export 'kernel_formal_parameter_builder.dart' show KernelFormalParameterBuilder;
 
-export 'kernel_function_type_builder.dart' show KernelFunctionTypeBuilder;
-
 export 'kernel_function_type_alias_builder.dart'
     show KernelFunctionTypeAliasBuilder;
 
-export 'kernel_prefix_builder.dart' show KernelPrefixBuilder;
+export 'kernel_function_type_builder.dart' show KernelFunctionTypeBuilder;
 
-export 'kernel_named_type_builder.dart' show KernelNamedTypeBuilder;
+export 'kernel_invalid_type_builder.dart' show KernelInvalidTypeBuilder;
 
 export 'kernel_library_builder.dart' show KernelLibraryBuilder;
 
 export 'kernel_mixin_application_builder.dart'
     show KernelMixinApplicationBuilder;
 
+export 'kernel_named_type_builder.dart' show KernelNamedTypeBuilder;
+
+export 'kernel_prefix_builder.dart' show KernelPrefixBuilder;
+
 export 'kernel_procedure_builder.dart'
     show
         KernelConstructorBuilder,
@@ -37,23 +51,11 @@
 
 export 'kernel_type_variable_builder.dart' show KernelTypeVariableBuilder;
 
-export '../builder/builder.dart';
-
 export 'kernel_variable_builder.dart' show KernelVariableBuilder;
 
-export 'kernel_invalid_type_builder.dart' show KernelInvalidTypeBuilder;
-
 export 'load_library_builder.dart' show LoadLibraryBuilder;
 
-import 'package:kernel/ast.dart'
-    show
-        Combinator,
-        Constructor,
-        Initializer,
-        Procedure,
-        RedirectingInitializer;
-
-import '../combinator.dart' as fasta;
+export 'unlinked_scope.dart' show UnlinkedDeclaration;
 
 int compareProcedures(Procedure a, Procedure b) {
   int i = "${a.fileUri}".compareTo("${b.fileUri}");
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
index 97d3930..bc2d940 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
@@ -2,7 +2,8 @@
 // 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:kernel/ast.dart' show Arguments, Expression, Node, Statement;
+import 'package:kernel/ast.dart'
+    show Arguments, Expression, InvalidExpression, Node, Statement;
 
 import '../../scanner/token.dart' show Token;
 
@@ -61,6 +62,7 @@
         ThisIndexedAccessGenerator,
         ThisPropertyAccessGenerator,
         TypeUseGenerator,
+        UnlinkedGenerator,
         UnresolvedNameGenerator,
         VariableUseGenerator;
 
@@ -68,7 +70,8 @@
 
 import 'forest.dart' show Forest;
 
-import 'kernel_builder.dart' show LoadLibraryBuilder, PrefixBuilder;
+import 'kernel_builder.dart'
+    show LoadLibraryBuilder, PrefixBuilder, UnlinkedDeclaration;
 
 import 'kernel_api.dart' show NameSystem, printNodeOn, printQualifiedNameOn;
 
@@ -1462,6 +1465,53 @@
   }
 }
 
+class KernelUnlinkedGenerator extends KernelGenerator
+    with UnlinkedGenerator<Expression, Statement, Arguments> {
+  @override
+  final UnlinkedDeclaration declaration;
+
+  final Expression receiver;
+
+  final Name name;
+
+  KernelUnlinkedGenerator(
+      ExpressionGeneratorHelper<Expression, Statement, Arguments> helper,
+      Token token,
+      this.declaration)
+      : name = new Name(declaration.name, helper.library.target),
+        receiver = new InvalidExpression(declaration.name)
+          ..fileOffset = offsetForToken(token),
+        super(helper, token);
+
+  @override
+  Expression _makeRead(ShadowComplexAssignment complexAssignment) {
+    return unsupported("_makeRead", offsetForToken(token), uri);
+  }
+
+  @override
+  Expression _makeWrite(Expression value, bool voidContext,
+      ShadowComplexAssignment complexAssignment) {
+    return unsupported("_makeWrite", offsetForToken(token), uri);
+  }
+
+  @override
+  Expression buildAssignment(Expression value, {bool voidContext}) {
+    return new PropertySet(receiver, name, value)
+      ..fileOffset = offsetForToken(token);
+  }
+
+  @override
+  Expression buildSimpleRead() {
+    return new ShadowPropertyGet(receiver, name)
+      ..fileOffset = offsetForToken(token);
+  }
+
+  @override
+  Expression doInvocation(int offset, Arguments arguments) {
+    return unsupported("doInvocation", offset, uri);
+  }
+}
+
 Expression makeLet(VariableDeclaration variable, Expression body) {
   if (variable == null) return body;
   return new Let(variable, body);
diff --git a/pkg/front_end/lib/src/fasta/kernel/unlinked_scope.dart b/pkg/front_end/lib/src/fasta/kernel/unlinked_scope.dart
new file mode 100644
index 0000000..6949064
--- /dev/null
+++ b/pkg/front_end/lib/src/fasta/kernel/unlinked_scope.dart
@@ -0,0 +1,38 @@
+// 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 'kernel_builder.dart' show Declaration, Scope;
+
+/// Scope that returns an [UnlinkedDeclaration] if a name can't be resolved.
+/// This is intended to be used as the `enclosingScope` in `BodyBuilder` to
+/// create ASTs with building outlines.
+class UnlinkedScope extends Scope {
+  UnlinkedScope() : super.top(isModifiable: false);
+
+  Declaration lookupIn(String name, int charOffset, Uri fileUri,
+      Map<String, Declaration> map, bool isInstanceScope) {
+    return new UnlinkedDeclaration(name, isInstanceScope, charOffset, fileUri);
+  }
+}
+
+class UnlinkedDeclaration extends Declaration {
+  final String name;
+
+  final bool isInstanceScope;
+
+  @override
+  final int charOffset;
+
+  @override
+  final Uri fileUri;
+
+  UnlinkedDeclaration(
+      this.name, this.isInstanceScope, this.charOffset, this.fileUri);
+
+  @override
+  Declaration get parent => null;
+
+  @override
+  String get fullNameForErrors => name;
+}
diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart
index 1aa4ce9..8d1dad4 100644
--- a/pkg/front_end/test/fasta/generator_to_string_test.dart
+++ b/pkg/front_end/test/fasta/generator_to_string_test.dart
@@ -37,7 +37,8 @@
         KernelTypeVariableBuilder,
         LoadLibraryBuilder,
         PrefixBuilder,
-        TypeDeclarationBuilder;
+        TypeDeclarationBuilder,
+        UnlinkedDeclaration;
 
 import 'package:front_end/src/fasta/kernel/kernel_target.dart'
     show KernelTarget;
@@ -70,11 +71,12 @@
         KernelThisIndexedAccessGenerator,
         KernelThisPropertyAccessGenerator,
         KernelTypeUseGenerator,
+        KernelUnlinkedGenerator,
+        KernelUnresolvedNameGenerator,
         KernelVariableUseGenerator,
         ParenthesizedExpressionGenerator,
         SendAccessGenerator,
-        ThisAccessGenerator,
-        KernelUnresolvedNameGenerator;
+        ThisAccessGenerator;
 
 import 'package:front_end/src/fasta/scanner.dart' show Token, scanString;
 
@@ -229,5 +231,9 @@
             helper, token, prefixBuilder, -1, declaration, "foo"));
     check("UnresolvedNameGenerator(offset: 4, name: bar)",
         new KernelUnresolvedNameGenerator(helper, token, name));
+    check(
+        "UnlinkedGenerator(offset: 4, name: foo)",
+        new KernelUnlinkedGenerator(
+            helper, token, new UnlinkedDeclaration("foo", false, -1, null)));
   });
 }
diff --git a/pkg/front_end/test/fasta/unlinked_scope_test.dart b/pkg/front_end/test/fasta/unlinked_scope_test.dart
new file mode 100644
index 0000000..1f6c28f
--- /dev/null
+++ b/pkg/front_end/test/fasta/unlinked_scope_test.dart
@@ -0,0 +1,113 @@
+// 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:expect/expect.dart' show Expect;
+
+import 'package:kernel/ast.dart' show Expression, ProcedureKind;
+
+import 'package:kernel/target/targets.dart' show NoneTarget, TargetFlags;
+
+import 'package:front_end/src/api_prototype/compiler_options.dart'
+    show CompilerOptions, FormattedMessage, ProblemHandler;
+
+import 'package:front_end/src/base/processed_options.dart'
+    show ProcessedOptions;
+
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+
+import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
+
+import 'package:front_end/src/fasta/kernel/kernel_body_builder.dart'
+    show KernelBodyBuilder;
+
+import 'package:front_end/src/fasta/kernel/kernel_builder.dart'
+    show KernelLibraryBuilder, KernelProcedureBuilder;
+
+import 'package:front_end/src/fasta/kernel/kernel_target.dart'
+    show KernelTarget;
+
+import 'package:front_end/src/fasta/kernel/unlinked_scope.dart'
+    show UnlinkedScope;
+
+import 'package:front_end/src/fasta/parser.dart' show Parser;
+
+import 'package:front_end/src/fasta/scanner.dart' show Token, scanString;
+
+import 'package:front_end/src/fasta/scope.dart' show Scope;
+
+import 'package:front_end/src/fasta/severity.dart' show Severity;
+
+ProblemHandler handler;
+
+class MockLibraryBuilder extends KernelLibraryBuilder {
+  MockLibraryBuilder(Uri uri)
+      : super(
+            uri,
+            uri,
+            new KernelTarget(
+                    null,
+                    false,
+                    new DillTarget(
+                        null, null, new NoneTarget(new TargetFlags())),
+                    null)
+                .loader,
+            null,
+            null);
+
+  KernelProcedureBuilder mockProcedure(String name) {
+    return new KernelProcedureBuilder(null, 0, null, name, null, null,
+        ProcedureKind.Getter, this, -1, -1, -1);
+  }
+}
+
+class MockBodyBuilder extends KernelBodyBuilder {
+  MockBodyBuilder.internal(
+      MockLibraryBuilder libraryBuilder, String name, Scope scope)
+      : super(libraryBuilder, libraryBuilder.mockProcedure(name), scope, scope,
+            null, null, null, false, libraryBuilder.uri, null);
+
+  MockBodyBuilder(Uri uri, String name, Scope scope)
+      : this.internal(new MockLibraryBuilder(uri), name, scope);
+}
+
+Expression compileExpression(String source) {
+  KernelBodyBuilder listener = new MockBodyBuilder(
+      Uri.parse("org-dartlang-test:my_library.dart"),
+      "<test>",
+      new UnlinkedScope());
+
+  handler = (FormattedMessage problem, Severity severity,
+      List<FormattedMessage> context) {
+    throw problem.formatted;
+  };
+
+  Token token = scanString(source).tokens;
+  Parser parser = new Parser(listener);
+  parser.parseExpression(parser.syntheticPreviousToken(token));
+  Expression e = listener.popForValue();
+  listener.checkEmpty(-1);
+  return e;
+}
+
+void testExpression(String source, [String expected]) {
+  Expression e = compileExpression(source);
+  String actual =
+      "$e".replaceAll(new RegExp(r'invalid-expression "[^"]*"\.'), "");
+  Expect.stringEquals(expected ?? source, actual);
+  print(e);
+}
+
+main() {
+  CompilerContext context = new CompilerContext(new ProcessedOptions(
+      new CompilerOptions()
+        ..onProblem = (FormattedMessage problem, Severity severity,
+            List<FormattedMessage> context) {
+          handler(problem, severity, context);
+        }));
+  context.runInContext((_) {
+    testExpression("unresolved");
+    testExpression("a + b", "a.+(b)");
+    testExpression("a = b");
+  });
+}