Version 1.9.0-dev.8.2

svn merge -c 43763,43787,43790,43791,43792 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

R=kustermann@google.com

Review URL: https://codereview.chromium.org//931103003

git-svn-id: http://dart.googlecode.com/svn/trunk@43808 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 2c5f528..e1ef9fc5 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -158,7 +158,7 @@
   /**
    * Process a `completion.getSuggestions` request.
    */
-  Response processRequest(Request request) {
+  Response processRequest(Request request, [CompletionManager manager]) {
     performance = new CompletionPerformance();
     // extract params
     CompletionGetSuggestionsParams params =
@@ -168,7 +168,9 @@
     AnalysisContext context = server.getAnalysisContext(params.file);
     Source source = server.getSource(params.file);
     recordRequest(performance, context, source, params.offset);
-    CompletionManager manager = completionManagerFor(context, source);
+    if (manager == null) {
+      manager = completionManagerFor(context, source);
+    }
     CompletionRequest completionRequest =
         new CompletionRequest(params.offset, performance);
     int notificationCount = 0;
diff --git a/pkg/analysis_server/lib/src/services/completion/common_usage_computer.dart b/pkg/analysis_server/lib/src/services/completion/common_usage_computer.dart
new file mode 100644
index 0000000..5c7d340
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/completion/common_usage_computer.dart
@@ -0,0 +1,160 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library services.completion.computer.dart.relevance;
+
+import 'package:analysis_server/src/protocol_server.dart' as protocol;
+import 'package:analysis_server/src/protocol_server.dart' show
+    CompletionSuggestion, CompletionSuggestionKind;
+import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+
+/**
+ * A map of <library>.<classname> to an ordered list of method names,
+ * field names, getter names, and named constructors.
+ * The names are ordered from most relevant to least relevant.
+ * Names not listed are considered equally less relevant than those listed.
+ */
+const Map<String, List<String>> defaultSelectorRelevance = const {//
+// Sample implementation which updates the relevance of the following
+//     new Random().nextInt(...)
+//     new Random().nextDouble(...)
+//     new Random().nextBool() - not commonly used thus omitted from list
+// Entries should look something like this
+//     'dart.math.Random': const ['nextInt', 'nextDouble'],
+//     'dart.async.Future': const ['value', 'wait'],
+};
+
+/**
+ * A computer for adjusting the relevance of completions computed by others
+ * based upon common Dart usage patterns.
+ */
+class CommonUsageComputer {
+  /**
+   * A map of <library>.<classname> to an ordered list of method names,
+   * field names, getter names, and named constructors.
+   * The names are ordered from most relevant to least relevant.
+   * Names not listed are considered equally less relevant than those listed.
+   */
+  Map<String, List<String>> selectorRelevance;
+
+  CommonUsageComputer([this.selectorRelevance = defaultSelectorRelevance]);
+
+  /**
+   * Adjusts the relevance based on the given completion context.
+   * The compilation unit and completion node
+   * in the given completion context may not be resolved.
+   * This method should execute quickly and not block waiting for any analysis.
+   */
+  void computeFast(DartCompletionRequest request) {
+    _update(request);
+  }
+
+  /**
+   * Adjusts the relevance based on the given completion context.
+   * The compilation unit and completion node
+   * in the given completion context are resolved.
+   */
+  void computeFull(DartCompletionRequest request) {
+    _update(request);
+  }
+
+  /**
+   * Adjusts the relevance based on the given completion context.
+   * The compilation unit and completion node
+   * in the given completion context may not be resolved.
+   */
+  void _update(DartCompletionRequest request) {
+    var visitor = new _BestTypeVisitor(request.target.entity);
+    DartType type = request.target.containingNode.accept(visitor);
+    if (type != null) {
+      Element typeElem = type.element;
+      if (typeElem != null) {
+        LibraryElement libElem = typeElem.library;
+        if (libElem != null) {
+          _updateInvocationRelevance(request, type, libElem);
+        }
+      }
+    }
+  }
+
+  /**
+   * Adjusts the relevance of all method suggestions based upon the given
+   * target type and library.
+   */
+  void _updateInvocationRelevance(DartCompletionRequest request, DartType type,
+      LibraryElement libElem) {
+    String typeName = type.name;
+    List<String> selectors = selectorRelevance['${libElem.name}.${typeName}'];
+    if (selectors != null) {
+      for (CompletionSuggestion suggestion in request.suggestions) {
+        protocol.Element element = suggestion.element;
+        if (element != null &&
+            (element.kind == protocol.ElementKind.CONSTRUCTOR ||
+                element.kind == protocol.ElementKind.FIELD ||
+                element.kind == protocol.ElementKind.GETTER ||
+                element.kind == protocol.ElementKind.METHOD ||
+                element.kind == protocol.ElementKind.SETTER) &&
+            suggestion.kind == CompletionSuggestionKind.INVOCATION &&
+            suggestion.declaringType == typeName) {
+          int index = selectors.indexOf(suggestion.completion);
+          if (index != -1) {
+            suggestion.relevance = DART_RELEVANCE_COMMON_USAGE - index;
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * An [AstVisitor] used to determine the best defining type of a node.
+ */
+class _BestTypeVisitor extends GeneralizingAstVisitor {
+
+  /**
+   * The entity which the completed text will replace (or which will be
+   * displaced once the completed text is inserted).  This may be an AstNode or
+   * a Token, or it may be null if the cursor is after all tokens in the file.
+   * See field of the same name in [CompletionTarget].
+   */
+  final Object entity;
+
+  _BestTypeVisitor(this.entity);
+
+  DartType visitConstructorName(ConstructorName node) {
+    if (node.period != null && node.name == entity) {
+      TypeName typeName = node.type;
+      if (typeName != null) {
+        return typeName.type;
+      }
+    }
+    return null;
+  }
+
+  DartType visitNode(AstNode node) {
+    return null;
+  }
+
+  DartType visitPrefixedIdentifier(PrefixedIdentifier node) {
+    if (node.identifier == entity) {
+      SimpleIdentifier prefix = node.prefix;
+      if (prefix != null) {
+        return prefix.bestType;
+      }
+    }
+    return null;
+  }
+
+  DartType visitPropertyAccess(PropertyAccess node) {
+    if (node.propertyName == entity) {
+      Expression target = node.realTarget;
+      if (target != null) {
+        return target.bestType;
+      }
+    }
+    return null;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
index 3c20785..818d8a6 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/protocol.dart';
 import 'package:analysis_server/src/services/completion/arglist_computer.dart';
 import 'package:analysis_server/src/services/completion/combinator_computer.dart';
+import 'package:analysis_server/src/services/completion/common_usage_computer.dart';
 import 'package:analysis_server/src/services/completion/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/completion_target.dart';
 import 'package:analysis_server/src/services/completion/dart_completion_cache.dart';
@@ -20,24 +21,24 @@
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analyzer/src/generated/ast.dart';
 import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
 
-// Relevance highest to lowest
-const int DART_RELEVANCE_HIGH = 2000;
-const int DART_RELEVANCE_LOCAL_VARIABLE = 1059;
-const int DART_RELEVANCE_PARAMETER = 1059;
-const int DART_RELEVANCE_INHERITED_FIELD = 1058;
-const int DART_RELEVANCE_LOCAL_FIELD = 1058;
-const int DART_RELEVANCE_INHERITED_ACCESSOR = 1057;
-const int DART_RELEVANCE_INHERITED_METHOD = 1057;
-const int DART_RELEVANCE_LOCAL_ACCESSOR = 1057;
-const int DART_RELEVANCE_LOCAL_METHOD = 1057;
-const int DART_RELEVANCE_LOCAL_FUNCTION = 1056;
-const int DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE = 1056;
-const int DART_RELEVANCE_KEYWORD = 1055;
+const int DART_RELEVANCE_COMMON_USAGE = 1200;
 const int DART_RELEVANCE_DEFAULT = 1000;
+const int DART_RELEVANCE_HIGH = 2000;
+const int DART_RELEVANCE_INHERITED_ACCESSOR = 1057;
+const int DART_RELEVANCE_INHERITED_FIELD = 1058;
+const int DART_RELEVANCE_INHERITED_METHOD = 1057;
+const int DART_RELEVANCE_KEYWORD = 1055;
+const int DART_RELEVANCE_LOCAL_ACCESSOR = 1057;
+const int DART_RELEVANCE_LOCAL_FIELD = 1058;
+const int DART_RELEVANCE_LOCAL_FUNCTION = 1056;
+const int DART_RELEVANCE_LOCAL_METHOD = 1057;
+const int DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE = 1056;
+const int DART_RELEVANCE_LOCAL_VARIABLE = 1059;
 const int DART_RELEVANCE_LOW = 500;
+const int DART_RELEVANCE_PARAMETER = 1059;
 
 /**
  * The base class for computing code completion suggestions.
@@ -69,9 +70,10 @@
   final SearchEngine searchEngine;
   final DartCompletionCache cache;
   List<DartCompletionComputer> computers;
+  CommonUsageComputer commonUsageComputer;
 
   DartCompletionManager(AnalysisContext context, this.searchEngine,
-      Source source, this.cache, [this.computers])
+      Source source, this.cache, [this.computers, this.commonUsageComputer])
       : super(context, source) {
     if (computers == null) {
       computers = [
@@ -82,6 +84,9 @@
           new ImportedComputer(),
           new InvocationComputer()];
     }
+    if (commonUsageComputer == null) {
+      commonUsageComputer = new CommonUsageComputer();
+    }
   }
 
   /**
@@ -141,6 +146,7 @@
           return c.computeFast(request);
         });
       });
+      commonUsageComputer.computeFast(request);
       sendResults(request, todo.isEmpty);
       return todo;
     });
@@ -179,6 +185,7 @@
               request.performance.logElapseTime(completeTag);
               bool last = --count == 0;
               if (changed || last) {
+                commonUsageComputer.computeFull(request);
                 sendResults(request, last);
               }
             });
diff --git a/pkg/analysis_server/test/mock_sdk.dart b/pkg/analysis_server/test/mock_sdk.dart
index 90b5eac..4589c22 100644
--- a/pkg/analysis_server/test/mock_sdk.dart
+++ b/pkg/analysis_server/test/mock_sdk.dart
@@ -108,6 +108,8 @@
 import 'dart:math';
 
 class Future<T> {
+  factory Future.delayed(Duration duration, [T computation()]) => null;
+  factory Future.value([value]) => null;
   static Future wait(List<Future> futures) => null;
 }
 
@@ -143,7 +145,11 @@
 external double cos(num x);
 external double sin(num x);
 external double sqrt(num x);
-class Random {}
+class Random {
+  bool nextBool() => true;
+  double nextDouble() => 2.0;
+  int nextInt() => 1;
+}
 ''');
 
   static const _MockSdkLibrary LIB_HTML =
diff --git a/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart b/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart
new file mode 100644
index 0000000..039911c
--- /dev/null
+++ b/pkg/analysis_server/test/services/completion/common_usage_computer_test.dart
@@ -0,0 +1,249 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.services.completion.computer.dart.relevance;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/domain_completion.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/completion/common_usage_computer.dart';
+import 'package:analysis_server/src/services/completion/dart_completion_cache.dart';
+import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
+import 'package:analysis_server/src/services/index/index.dart';
+import 'package:analysis_server/src/services/index/local_memory_index.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:unittest/unittest.dart';
+
+import '../../analysis_abstract.dart';
+import '../../mocks.dart';
+import '../../reflective_tests.dart';
+
+main() {
+  groupSep = ' | ';
+  runReflectiveTests(CommonUsageComputerTest);
+}
+
+@reflectiveTest
+class CommonUsageComputerTest extends AbstractAnalysisTest {
+  String completionId;
+  int completionOffset;
+  int replacementOffset;
+  int replacementLength;
+  List<CompletionSuggestion> suggestions = [];
+  bool suggestionsDone = false;
+
+  String addTestFile(String content) {
+    completionOffset = content.indexOf('^');
+    expect(completionOffset, isNot(equals(-1)), reason: 'missing ^');
+    int nextOffset = content.indexOf('^', completionOffset + 1);
+    expect(nextOffset, equals(-1), reason: 'too many ^');
+    return super.addTestFile(
+        content.substring(0, completionOffset) +
+            content.substring(completionOffset + 1));
+  }
+
+  void assertHasResult(CompletionSuggestionKind kind, String completion,
+      [int relevance = DART_RELEVANCE_DEFAULT, bool isDeprecated = false,
+      bool isPotential = false]) {
+    var cs;
+    suggestions.forEach((s) {
+      if (s.completion == completion) {
+        if (cs == null) {
+          cs = s;
+        } else {
+          fail('expected exactly one $completion but found > 1');
+        }
+      }
+    });
+    if (cs == null) {
+      var completions = suggestions.map((s) => s.completion).toList();
+      fail('expected "$completion" but found\n $completions');
+    }
+    expect(cs.kind, equals(kind));
+    expect(cs.relevance, equals(relevance));
+    expect(cs.selectionOffset, equals(completion.length));
+    expect(cs.selectionLength, equals(0));
+    expect(cs.isDeprecated, equals(isDeprecated));
+    expect(cs.isPotential, equals(isPotential));
+  }
+
+  void assertNoResult(String completion) {
+    if (suggestions.any((cs) => cs.completion == completion)) {
+      fail('did not expect completion: $completion');
+    }
+  }
+
+  void assertValidId(String id) {
+    expect(id, isNotNull);
+    expect(id.isNotEmpty, isTrue);
+  }
+
+  @override
+  Index createIndex() {
+    return createLocalMemoryIndex();
+  }
+
+  Future getSuggestions(Map<String, List<String>> selectorRelevance) async {
+    await waitForTasksFinished();
+    CompletionGetSuggestionsParams params =
+        new CompletionGetSuggestionsParams(testFile, completionOffset);
+    Request request = params.toRequest('0');
+    CompletionDomainHandler domainHandler = new CompletionDomainHandler(server);
+    handler = domainHandler;
+
+    AnalysisContext context = server.getAnalysisContext(params.file);
+    Source source = server.getSource(params.file);
+    DartCompletionManager completionManager = new DartCompletionManager(
+        context,
+        server.searchEngine,
+        source,
+        new DartCompletionCache(context, source),
+        null,
+        new CommonUsageComputer(selectorRelevance));
+
+    Response response =
+        domainHandler.processRequest(request, completionManager);
+    expect(response, isResponseSuccess('0'));
+    completionId = response.id;
+    assertValidId(completionId);
+    await pumpEventQueue();
+    expect(suggestionsDone, isTrue);
+  }
+
+  void processNotification(Notification notification) {
+    if (notification.event == COMPLETION_RESULTS) {
+      var params = new CompletionResultsParams.fromNotification(notification);
+      String id = params.id;
+      assertValidId(id);
+      if (id == completionId) {
+        expect(suggestionsDone, isFalse);
+        replacementOffset = params.replacementOffset;
+        replacementLength = params.replacementLength;
+        suggestionsDone = params.isLast;
+        expect(suggestionsDone, isNotNull);
+        suggestions = params.results;
+      }
+    }
+  }
+
+  @override
+  void setUp() {
+    super.setUp();
+    createProject();
+  }
+
+  test_ConstructorName() async {
+    // SimpleIdentifier  ConstructorName  InstanceCreationExpression
+    addTestFile('import "dart:async"; class A {x() {new Future.^}}');
+    await getSuggestions({
+      'dart.async.Future': ['value', 'wait']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(CompletionSuggestionKind.INVOCATION, 'delayed');
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        'value',
+        DART_RELEVANCE_COMMON_USAGE);
+    assertNoResult('Future');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+
+  test_PrefixedIdentifier_field() async {
+    // SimpleIdentifier  PrefixedIdentifeir  ExpressionStatement
+    addTestFile('class A {static int s1; static int s2; x() {A.^}}');
+    await getSuggestions({
+      '.A': ['s2']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(CompletionSuggestionKind.INVOCATION, 's1');
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        's2',
+        DART_RELEVANCE_COMMON_USAGE);
+    assertNoResult('Future');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+
+  test_PrefixedIdentifier_getter() async {
+    // SimpleIdentifier  PrefixedIdentifeir  ExpressionStatement
+    addTestFile('class A {int get g1 => 1; int get g2 => 2; x() {new A().^}}');
+    await getSuggestions({
+      '.A': ['g2']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(CompletionSuggestionKind.INVOCATION, 'g1');
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        'g2',
+        DART_RELEVANCE_COMMON_USAGE);
+    assertNoResult('Future');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+
+  test_PrefixedIdentifier_setter() async {
+    // SimpleIdentifier  PrefixedIdentifeir  ExpressionStatement
+    addTestFile('class A {set s1(v) {}; set s2(v) {}; x() {new A().^}}');
+    await getSuggestions({
+      '.A': ['s2']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(CompletionSuggestionKind.INVOCATION, 's1');
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        's2',
+        DART_RELEVANCE_COMMON_USAGE);
+    assertNoResult('Future');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+
+  test_PrefixedIdentifier_static_method() async {
+    // SimpleIdentifier  PrefixedIdentifeir  ExpressionStatement
+    addTestFile('import "dart:async"; class A {x() {Future.^}}');
+    await getSuggestions({
+      'dart.async.Future': ['value', 'wait']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        'wait',
+        DART_RELEVANCE_COMMON_USAGE - 1);
+    assertNoResult('Future');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+
+  test_PropertyAccess() async {
+    // SimpleIdentifier  PropertyAccess  ExpressionStatement
+    addTestFile('import "dart:math"; class A {x() {new Random().^}}');
+    await getSuggestions({
+      'dart.math.Random': ['nextInt', 'nextDouble']
+    });
+    expect(replacementOffset, equals(completionOffset));
+    expect(replacementLength, equals(0));
+    assertHasResult(CompletionSuggestionKind.INVOCATION, 'nextBool');
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        'nextDouble',
+        DART_RELEVANCE_COMMON_USAGE - 1);
+    assertHasResult(
+        CompletionSuggestionKind.INVOCATION,
+        'nextInt',
+        DART_RELEVANCE_COMMON_USAGE);
+    assertNoResult('Random');
+    assertNoResult('Object');
+    assertNoResult('A');
+  }
+}
diff --git a/pkg/analysis_server/test/services/completion/completion_test_util.dart b/pkg/analysis_server/test/services/completion/completion_test_util.dart
index 67a7a06..573f604 100644
--- a/pkg/analysis_server/test/services/completion/completion_test_util.dart
+++ b/pkg/analysis_server/test/services/completion/completion_test_util.dart
@@ -9,6 +9,7 @@
 import 'package:analysis_server/src/protocol.dart' as protocol show Element,
     ElementKind;
 import 'package:analysis_server/src/protocol.dart' hide Element, ElementKind;
+import 'package:analysis_server/src/services/completion/common_usage_computer.dart';
 import 'package:analysis_server/src/services/completion/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart_completion_cache.dart';
 import 'package:analysis_server/src/services/completion/dart_completion_manager.dart';
@@ -450,7 +451,8 @@
         searchEngine,
         testSource,
         cache,
-        [computer]);
+        [computer],
+        new CommonUsageComputer({}));
     var result = _completionManager.computeFast(request);
     expect(request.replacementOffset, isNotNull);
     expect(request.replacementLength, isNotNull);
@@ -2496,6 +2498,19 @@
     });
   }
 
+  test_new_instance() {
+    addTestSource('import "dart:math"; class A {x() {new Random().^}}');
+    computeFast();
+    return computeFull((bool result) {
+      assertSuggestInvocationMethod('nextBool', 'Random', 'bool');
+      assertSuggestInvocationMethod('nextDouble', 'Random', 'double');
+      assertSuggestInvocationMethod('nextInt', 'Random', 'int');
+      assertNotSuggested('Random');
+      assertNotSuggested('Object');
+      assertNotSuggested('A');
+    });
+  }
+
   test_partFile_TypeName() {
     // SimpleIdentifier  TypeName  ConstructorName
     addSource('/testB.dart', '''
diff --git a/pkg/analysis_server/test/services/completion/test_all.dart b/pkg/analysis_server/test/services/completion/test_all.dart
index ef908ff..269e8c0 100644
--- a/pkg/analysis_server/test/services/completion/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/test_all.dart
@@ -8,6 +8,7 @@
 
 import 'arglist_computer_test.dart' as arglist_test;
 import 'combinator_computer_test.dart' as combinator_test;
+import 'common_usage_computer_test.dart' as common_usage_computer_test;
 import 'completion_computer_test.dart' as completion_computer_test;
 import 'completion_manager_test.dart' as completion_manager_test;
 import 'completion_target_test.dart' as completion_target_test;
@@ -23,6 +24,7 @@
   group('completion', () {
     arglist_test.main();
     combinator_test.main();
+    common_usage_computer_test.main();
     completion_computer_test.main();
     completion_manager_test.main();
     completion_target_test.main();
diff --git a/tools/VERSION b/tools/VERSION
index d21d407..9130654 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 9
 PATCH 0
 PRERELEASE 8
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2