Track how many public Flutter members lack docs

* Enable the lint require documentation.

* Track how many public Flutter members lack docs

* Rename the .analysis_options file

This makes Atom happier in the flutter_tools package.
diff --git a/packages/flutter_tools/.analysis_options b/packages/flutter_tools/flutter_analysis_options
similarity index 85%
rename from packages/flutter_tools/.analysis_options
rename to packages/flutter_tools/flutter_analysis_options
index f1976ec..616925d 100644
--- a/packages/flutter_tools/.analysis_options
+++ b/packages/flutter_tools/flutter_analysis_options
@@ -4,6 +4,11 @@
 # to opt-in to all desired lints (https://github.com/dart-lang/sdk/issues/25843).
 # For a list of lints, see: http://dart-lang.github.io/linter/lints/
 
+# This file is the .analyzis_options file used by "flutter analyze".
+# It isn't named that because otherwise editors like Atom would try
+# to use it, and that wouldn't work because it enables things that
+# need to be silenced, in particular, public_member_api_docs.
+
 analyzer:
   language:
     enableSuperMixins: true
@@ -39,7 +44,7 @@
     - package_names
     - package_prefixed_library_names
     - prefer_is_not_empty
-    # - public_member_api_docs # still a lot of work to do before enabling this one
+    - public_member_api_docs
     - slash_for_doc_comments
     - sort_constructors_first
     - sort_unnamed_constructors_first
diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart
index b047a89..1f1d47e 100644
--- a/packages/flutter_tools/lib/src/commands/analyze.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze.dart
@@ -108,6 +108,7 @@
     argParser.addFlag('flutter-repo', help: 'Include all the examples and tests from the Flutter repository.', defaultsTo: false);
     argParser.addFlag('current-directory', help: 'Include all the Dart files in the current directory, if any.', defaultsTo: true);
     argParser.addFlag('current-package', help: 'Include the lib/main.dart file from the current directory, if any.', defaultsTo: true);
+    argParser.addFlag('dartdocs', help: 'List every public member that is lacking documentation. (Only examines files in the Flutter repository.)', defaultsTo: false);
     argParser.addFlag('preamble', help: 'Display the number of files that will be analyzed.', defaultsTo: true);
     argParser.addFlag('congratulate', help: 'Show output even when there are no errors, warnings, hints, or lints.', defaultsTo: true);
     argParser.addFlag('watch', help: 'Run analysis continuously, watching the filesystem for changes.', negatable: false);
@@ -287,7 +288,7 @@
     // save the Dart file and the .packages file to disk
     Directory host = Directory.systemTemp.createTempSync('flutter-analyze-');
     File mainFile = new File(path.join(host.path, 'main.dart'))..writeAsStringSync(mainBody.toString());
-    File optionsFile = new File(path.join(ArtifactStore.flutterRoot, 'packages', 'flutter_tools', '.analysis_options'));
+    File optionsFile = new File(path.join(ArtifactStore.flutterRoot, 'packages', 'flutter_tools', 'flutter_analysis_options'));
     File packagesFile = new File(path.join(host.path, '.packages'))..writeAsStringSync(packagesBody.toString());
 
     List<String> cmd = <String>[
@@ -351,6 +352,7 @@
 
     Set<String> changedFiles = new Set<String>(); // files about which we've complained that they changed
 
+    int membersMissingDocumentation = 0;
     List<String> errorLines = output.toString().split('\n');
     for (String errorLine in errorLines) {
       if (patternsToSkip.every((Pattern pattern) => pattern.allMatches(errorLine).isEmpty)) {
@@ -381,7 +383,14 @@
           if (documentAllMembersPattern.firstMatch(errorMessage) != null) {
             // https://github.com/dart-lang/linter/issues/207
             // https://github.com/dart-lang/linter/issues/208
-            shouldIgnore = !isFlutterLibrary(filename);
+            if (isFlutterLibrary(filename)) {
+              if (!argResults['dartdocs']) {
+                membersMissingDocumentation += 1;
+                shouldIgnore = true;
+              }
+            } else {
+              shouldIgnore = true;
+            }
           } else if (filename == mainFile.path) {
             Match libs = conflictingNamesPattern.firstMatch(errorMessage);
             Match missing = missingFilePattern.firstMatch(errorMessage);
@@ -422,10 +431,18 @@
     if (exitCode < 0 || exitCode > 3) // analyzer exit codes: 0 = nothing, 1 = hints, 2 = warnings, 3 = errors
       return exitCode;
 
-    if (errorCount > 0)
+    if (errorCount > 0) {
+      if (membersMissingDocumentation > 0 && argResults['flutter-repo'])
+        printError('[lint] $membersMissingDocumentation public ${ membersMissingDocumentation == 1 ? "member lacks" : "members lack" } documentation');
       return 1; // we consider any level of error to be an error exit (we don't report different levels)
-    if (argResults['congratulate'])
-      printStatus('No analyzer warnings! (ran in ${elapsed}s)');
+    }
+    if (argResults['congratulate']) {
+      if (membersMissingDocumentation > 0 && argResults['flutter-repo']) {
+        printStatus('No analyzer warnings! (ran in ${elapsed}s; $membersMissingDocumentation public ${ membersMissingDocumentation == 1 ? "member lacks" : "members lack" } documentation)');
+      } else {
+        printStatus('No analyzer warnings! (ran in ${elapsed}s)');
+      }
+    }
     return 0;
   }