| OLD | NEW | 
|    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |    1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |    2 // for details. All rights reserved. Use of this source code is governed by a | 
|    3 // BSD-style license that can be found in the LICENSE file. |    3 // BSD-style license that can be found in the LICENSE file. | 
|    4  |    4  | 
|    5 library analyze_helper; |    5 library analyze_helper; | 
|    6  |    6  | 
|    7 import 'dart:async'; |    7 import 'dart:async'; | 
|    8 import 'dart:io'; |    8 import 'dart:io'; | 
|    9 import 'package:compiler/compiler.dart' as api; |    9 import 'package:compiler/compiler.dart' as api; | 
|   10 import 'package:compiler/src/apiimpl.dart'; |   10 import 'package:compiler/src/apiimpl.dart'; | 
|   11 import 'package:compiler/src/commandline_options.dart'; |   11 import 'package:compiler/src/commandline_options.dart'; | 
|   12 import 'package:compiler/src/diagnostics/messages.dart' show |   12 import 'package:compiler/src/diagnostics/messages.dart' show | 
|   13     Message; |   13     Message, | 
 |   14     MessageKind; | 
|   14 import 'package:compiler/src/filenames.dart'; |   15 import 'package:compiler/src/filenames.dart'; | 
|   15 import 'package:compiler/src/source_file_provider.dart'; |   16 import 'package:compiler/src/source_file_provider.dart'; | 
|   16 import 'package:compiler/src/util/uri_extras.dart'; |   17 import 'package:compiler/src/util/uri_extras.dart'; | 
|   17  |   18  | 
|   18 /** |   19 /** | 
|   19  * Map of whitelisted warnings and errors. |   20  * Map of whitelisted warnings and errors. | 
|   20  * |   21  * | 
|   21  * Only add a whitelisting together with a bug report to dartbug.com and add |   22  * Only add a whitelisting together with a bug report to dartbug.com and add | 
|   22  * the bug issue number as a comment on the whitelisting. |   23  * the bug issue number as a comment on the whitelisting. | 
|   23  * |   24  * | 
|   24  * Use an identifiable suffix of the file uri as key. Use a fixed substring of |   25  * Use an identifiable suffix of the file uri as key. Use a fixed substring of | 
|   25  * the error/warning message in the list of whitelistings for each file. |   26  * the error/warning message in the list of whitelistings for each file. | 
|   26  */ |   27  */ | 
|   27 // TODO(johnniwinther): Support canonical URIs as keys and message kinds as |   28 // TODO(johnniwinther): Support canonical URIs as keys and message kinds as | 
|   28 // values. |   29 // values. | 
|   29  |   30  | 
|   30 class CollectingDiagnosticHandler extends FormattingDiagnosticHandler { |   31 class CollectingDiagnosticHandler extends FormattingDiagnosticHandler { | 
|   31   bool hasWarnings = false; |   32   bool hasWarnings = false; | 
|   32   bool hasHint = false; |   33   bool hasHint = false; | 
|   33   bool hasErrors = false; |   34   bool hasErrors = false; | 
|   34   bool lastWasWhitelisted = false; |   35   bool lastWasWhitelisted = false; | 
|   35  |   36  | 
|   36   Map<String, Map<String, int>> whiteListMap |   37   Map<String, Map<dynamic/*String|MessageKind*/, int>> whiteListMap | 
|   37       = new Map<String, Map<String, int>>(); |   38       = new Map<String, Map<dynamic/*String|MessageKind*/, int>>(); | 
|   38  |   39  | 
|   39   CollectingDiagnosticHandler(Map<String, List<String>> whiteList, |   40   CollectingDiagnosticHandler( | 
|   40                               SourceFileProvider provider) |   41       Map<String, List/*<String|MessageKind>*/> whiteList, | 
 |   42       SourceFileProvider provider) | 
|   41       : super(provider) { |   43       : super(provider) { | 
|   42     whiteList.forEach((String file, List<String> messageParts) { |   44     whiteList.forEach((String file, List/*<String|MessageKind>*/ messageParts) { | 
|   43       var useMap = new Map<String,int>(); |   45       var useMap = new Map<dynamic/*String|MessageKind*/, int>(); | 
|   44       for (String messagePart in messageParts) { |   46       for (var messagePart in messageParts) { | 
|   45         useMap[messagePart] = 0; |   47         useMap[messagePart] = 0; | 
|   46       } |   48       } | 
|   47       whiteListMap[file] = useMap; |   49       whiteListMap[file] = useMap; | 
|   48     }); |   50     }); | 
|   49   } |   51   } | 
|   50  |   52  | 
|   51   bool checkResults() { |   53   bool checkResults() { | 
|   52     bool validWhiteListUse = checkWhiteListUse(); |   54     bool validWhiteListUse = checkWhiteListUse(); | 
|   53     reportWhiteListUse(); |   55     reportWhiteListUse(); | 
|   54     return !hasWarnings && !hasHint && !hasErrors && validWhiteListUse; |   56     return !hasWarnings && !hasHint && !hasErrors && validWhiteListUse; | 
|   55   } |   57   } | 
|   56  |   58  | 
|   57   bool checkWhiteListUse() { |   59   bool checkWhiteListUse() { | 
|   58     bool allUsed = true; |   60     bool allUsed = true; | 
|   59     for (String file in whiteListMap.keys) { |   61     for (String file in whiteListMap.keys) { | 
|   60       for (String messagePart in whiteListMap[file].keys) { |   62       for (var messagePart in whiteListMap[file].keys) { | 
|   61         if (whiteListMap[file][messagePart] == 0) { |   63         if (whiteListMap[file][messagePart] == 0) { | 
|   62           print("Whitelisting '$messagePart' is unused in '$file'. " |   64           print("Whitelisting '$messagePart' is unused in '$file'. " | 
|   63                 "Remove the whitelisting from the whitelist map."); |   65                 "Remove the whitelisting from the whitelist map."); | 
|   64           allUsed = false; |   66           allUsed = false; | 
|   65         } |   67         } | 
|   66       } |   68       } | 
|   67     } |   69     } | 
|   68     return allUsed; |   70     return allUsed; | 
|   69   } |   71   } | 
|   70  |   72  | 
|   71   void reportWhiteListUse() { |   73   void reportWhiteListUse() { | 
|   72     for (String file in whiteListMap.keys) { |   74     for (String file in whiteListMap.keys) { | 
|   73       for (String messagePart in whiteListMap[file].keys) { |   75       for (var messagePart in whiteListMap[file].keys) { | 
|   74         int useCount = whiteListMap[file][messagePart]; |   76         int useCount = whiteListMap[file][messagePart]; | 
|   75         print("Whitelisted message '$messagePart' suppressed $useCount " |   77         print("Whitelisted message '$messagePart' suppressed $useCount " | 
|   76               "time(s) in '$file'."); |   78               "time(s) in '$file'."); | 
|   77       } |   79       } | 
|   78     } |   80     } | 
|   79   } |   81   } | 
|   80  |   82  | 
|   81   bool checkWhiteList(Uri uri, String message) { |   83   bool checkWhiteList(Uri uri, Message message, String text) { | 
|   82     if (uri == null) { |   84     if (uri == null) { | 
|   83       return false; |   85       return false; | 
|   84     } |   86     } | 
|   85     String path = uri.path; |   87     String path = uri.path; | 
|   86     for (String file in whiteListMap.keys) { |   88     for (String file in whiteListMap.keys) { | 
|   87       if (path.contains(file)) { |   89       if (path.contains(file)) { | 
|   88         for (String messagePart in whiteListMap[file].keys) { |   90         for (var messagePart in whiteListMap[file].keys) { | 
|   89           if (message.contains(messagePart)) { |   91           bool found = false; | 
 |   92           if (messagePart is String) { | 
 |   93             found = text.contains(messagePart); | 
 |   94           } else { | 
 |   95             assert(messagePart is MessageKind); | 
 |   96             found = message.kind == messagePart; | 
 |   97           } | 
 |   98           if (found) { | 
|   90             whiteListMap[file][messagePart]++; |   99             whiteListMap[file][messagePart]++; | 
|   91             return true; |  100             return true; | 
|   92           } |  101           } | 
|   93         } |  102         } | 
|   94       } |  103       } | 
|   95     } |  104     } | 
|   96     return false; |  105     return false; | 
|   97   } |  106   } | 
|   98  |  107  | 
|   99   @override |  108   @override | 
|  100   void report(Message message, Uri uri, int begin, int end, String text, |  109   void report(Message message, Uri uri, int begin, int end, String text, | 
|  101               api.Diagnostic kind) { |  110               api.Diagnostic kind) { | 
|  102     if (kind == api.Diagnostic.WARNING) { |  111     if (kind == api.Diagnostic.WARNING) { | 
|  103       if (checkWhiteList(uri, text)) { |  112       if (checkWhiteList(uri, message, text)) { | 
|  104         // Suppress whitelisted warnings. |  113         // Suppress whitelisted warnings. | 
|  105         lastWasWhitelisted = true; |  114         lastWasWhitelisted = true; | 
|  106         return; |  115         return; | 
|  107       } |  116       } | 
|  108       hasWarnings = true; |  117       hasWarnings = true; | 
|  109     } |  118     } | 
|  110     if (kind == api.Diagnostic.HINT) { |  119     if (kind == api.Diagnostic.HINT) { | 
|  111       if (checkWhiteList(uri, text)) { |  120       if (checkWhiteList(uri, message, text)) { | 
|  112         // Suppress whitelisted hints. |  121         // Suppress whitelisted hints. | 
|  113         lastWasWhitelisted = true; |  122         lastWasWhitelisted = true; | 
|  114         return; |  123         return; | 
|  115       } |  124       } | 
|  116       hasHint = true; |  125       hasHint = true; | 
|  117     } |  126     } | 
|  118     if (kind == api.Diagnostic.ERROR) { |  127     if (kind == api.Diagnostic.ERROR) { | 
|  119       if (checkWhiteList(uri, text)) { |  128       if (checkWhiteList(uri, message, text)) { | 
|  120         // Suppress whitelisted errors. |  129         // Suppress whitelisted errors. | 
|  121         lastWasWhitelisted = true; |  130         lastWasWhitelisted = true; | 
|  122         return; |  131         return; | 
|  123       } |  132       } | 
|  124       hasErrors = true; |  133       hasErrors = true; | 
|  125     } |  134     } | 
|  126     if (kind == api.Diagnostic.INFO && lastWasWhitelisted) { |  135     if (kind == api.Diagnostic.INFO && lastWasWhitelisted) { | 
|  127       return; |  136       return; | 
|  128     } |  137     } | 
|  129     lastWasWhitelisted = false; |  138     lastWasWhitelisted = false; | 
|  130     super.report(message, uri, begin, end, text, kind); |  139     super.report(message, uri, begin, end, text, kind); | 
|  131   } |  140   } | 
|  132 } |  141 } | 
|  133  |  142  | 
|  134 typedef bool CheckResults(CompilerImpl compiler, |  143 typedef bool CheckResults(CompilerImpl compiler, | 
|  135                           CollectingDiagnosticHandler handler); |  144                           CollectingDiagnosticHandler handler); | 
|  136  |  145  | 
 |  146 enum AnalysisMode { | 
 |  147   /// Analyze all declarations in all libraries in one go. | 
 |  148   ALL, | 
 |  149   /// Analyze all declarations in the main library. | 
 |  150   MAIN, | 
 |  151   /// Analyze all declarations in the given URIs one at a time. This mode can | 
 |  152   /// handle URIs for parts (i.e. skips these). | 
 |  153   URI, | 
 |  154   /// Analyze all declarations reachable from the entry point. | 
 |  155   TREE_SHAKING, | 
 |  156 } | 
 |  157  | 
|  137 Future analyze(List<Uri> uriList, |  158 Future analyze(List<Uri> uriList, | 
|  138                Map<String, List<String>> whiteList, |  159                Map<String, List/*<String|MessageKind>*/> whiteList, | 
|  139                {bool analyzeAll: true, |  160                {AnalysisMode mode: AnalysisMode.ALL, | 
|  140                 bool analyzeMain: false, |  161                 CheckResults checkResults}) async { | 
|  141                 CheckResults checkResults}) { |  | 
|  142   String testFileName = |  162   String testFileName = | 
|  143       relativize(Uri.base, Platform.script, Platform.isWindows); |  163       relativize(Uri.base, Platform.script, Platform.isWindows); | 
|  144  |  164  | 
|  145   print(""" |  165   print(""" | 
|  146  |  166  | 
|  147  |  167  | 
|  148 === |  168 === | 
|  149 === NOTE: If this test fails, update [WHITE_LIST] in $testFileName |  169 === NOTE: If this test fails, update [WHITE_LIST] in $testFileName | 
|  150 === |  170 === | 
|  151  |  171  | 
|  152  |  172  | 
|  153 """); |  173 """); | 
|  154  |  174  | 
|  155   var libraryRoot = currentDirectory.resolve('sdk/'); |  175   var libraryRoot = currentDirectory.resolve('sdk/'); | 
|  156   var packageRoot = |  176   var packageRoot = | 
|  157       currentDirectory.resolve(Platform.packageRoot); |  177       currentDirectory.resolve(Platform.packageRoot); | 
|  158   var provider = new CompilerSourceFileProvider(); |  178   var provider = new CompilerSourceFileProvider(); | 
|  159   var handler = new CollectingDiagnosticHandler(whiteList, provider); |  179   var handler = new CollectingDiagnosticHandler(whiteList, provider); | 
|  160   var options = <String>[Flags.analyzeOnly, '--categories=Client,Server', |  180   var options = <String>[Flags.analyzeOnly, '--categories=Client,Server', | 
|  161       Flags.showPackageWarnings]; |  181       Flags.showPackageWarnings]; | 
|  162   if (analyzeAll) options.add(Flags.analyzeAll); |  182   switch (mode) { | 
|  163   if (analyzeMain) options.add(Flags.analyzeMain); |  183     case AnalysisMode.URI: | 
 |  184     case AnalysisMode.MAIN: | 
 |  185       options.add(Flags.analyzeMain); | 
 |  186       break; | 
 |  187     case AnalysisMode.ALL: | 
 |  188       options.add(Flags.analyzeAll); | 
 |  189       break; | 
 |  190     case AnalysisMode.TREE_SHAKING: | 
 |  191       break; | 
 |  192   } | 
|  164   var compiler = new CompilerImpl( |  193   var compiler = new CompilerImpl( | 
|  165       provider, |  194       provider, | 
|  166       null, |  195       null, | 
|  167       handler, |  196       handler, | 
|  168       libraryRoot, |  197       libraryRoot, | 
|  169       packageRoot, |  198       packageRoot, | 
|  170       options, |  199       options, | 
|  171       {}); |  200       {}); | 
|  172   String MESSAGE = """ |  201   String MESSAGE = """ | 
|  173  |  202  | 
|  174  |  203  | 
|  175 === |  204 === | 
|  176 === ERROR: Unexpected result of analysis. |  205 === ERROR: Unexpected result of analysis. | 
|  177 === |  206 === | 
|  178 === Please update [WHITE_LIST] in $testFileName |  207 === Please update [WHITE_LIST] in $testFileName | 
|  179 === |  208 === | 
|  180 """; |  209 """; | 
|  181  |  210  | 
|  182   void onCompletion(_) { |  211   if (mode == AnalysisMode.URI) { | 
|  183     bool result; |  212     for (Uri uri in uriList) { | 
|  184     if (checkResults != null) { |  213       await compiler.analyzeUri(uri); | 
|  185       result = checkResults(compiler, handler); |  | 
|  186     } else { |  | 
|  187       result = handler.checkResults(); |  | 
|  188     } |  214     } | 
|  189     if (!result) { |  215   } else if (mode != AnalysisMode.TREE_SHAKING) { | 
|  190       print(MESSAGE); |  216     compiler.librariesToAnalyzeWhenRun = uriList; | 
|  191       exit(1); |  217     await compiler.run(null); | 
|  192     } |  218   } else { | 
 |  219     await compiler.run(uriList.single); | 
|  193   } |  220   } | 
|  194   if (analyzeAll || analyzeMain) { |  221  | 
|  195     compiler.librariesToAnalyzeWhenRun = uriList; |  222   bool result; | 
|  196     return compiler.run(null).then(onCompletion); |  223   if (checkResults != null) { | 
 |  224     result = checkResults(compiler, handler); | 
|  197   } else { |  225   } else { | 
|  198     return compiler.run(uriList.single).then(onCompletion); |  226     result = handler.checkResults(); | 
 |  227   } | 
 |  228   if (!result) { | 
 |  229     print(MESSAGE); | 
 |  230     exit(1); | 
|  199   } |  231   } | 
|  200 } |  232 } | 
| OLD | NEW |