Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: pkg/intl/lib/extract_messages.dart

Issue 22392004: Improvements to warnings on message extraction. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Changes from review Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/intl/test/message_extraction/extract_to_json.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /**
6 * This is for use in extracting messages from a Dart program 6 * This is for use in extracting messages from a Dart program
7 * using the Intl.message() mechanism and writing them to a file for 7 * using the Intl.message() mechanism and writing them to a file for
8 * translation. This provides only the stub of a mechanism, because it 8 * translation. This provides only the stub of a mechanism, because it
9 * doesn't define how the file should be written. It provides an 9 * doesn't define how the file should be written. It provides an
10 * [IntlMessage] class that holds the extracted data and [parseString] 10 * [IntlMessage] class that holds the extracted data and [parseString]
(...skipping 15 matching lines...) Expand all
26 import 'package:analyzer_experimental/analyzer.dart'; 26 import 'package:analyzer_experimental/analyzer.dart';
27 import 'package:intl/src/intl_message.dart'; 27 import 'package:intl/src/intl_message.dart';
28 28
29 /** 29 /**
30 * If this is true, print warnings for skipped messages. Otherwise, warnings 30 * If this is true, print warnings for skipped messages. Otherwise, warnings
31 * are suppressed. 31 * are suppressed.
32 */ 32 */
33 bool suppressWarnings = false; 33 bool suppressWarnings = false;
34 34
35 /** 35 /**
36 * If this is true, then treat all warnings as errors.
37 */
38 bool warningsAreErrors = false;
39
40 /**
41 * This accumulates a list of all warnings/errors we have found. These are
42 * saved as strings right now, so all that can really be done is print and
43 * count them.
44 */
45 List<String> warnings = [];
46
47 /** Were there any warnings or errors in extracting messages. */
48 bool get hasWarnings => warnings.isNotEmpty;
49
50 /**
36 * Parse the source of the Dart program file [file] and return a Map from 51 * Parse the source of the Dart program file [file] and return a Map from
37 * message names to [IntlMessage] instances. 52 * message names to [IntlMessage] instances.
38 */ 53 */
39 Map<String, MainMessage> parseFile(File file) { 54 Map<String, MainMessage> parseFile(File file) {
40 _root = parseDartFile(file.path); 55 _root = parseDartFile(file.path);
41 _origin = file.path; 56 _origin = file.path;
42 var visitor = new MessageFindingVisitor(); 57 var visitor = new MessageFindingVisitor();
43 _root.accept(visitor); 58 _root.accept(visitor);
44 return visitor.messages; 59 return visitor.messages;
45 } 60 }
46 61
47 /** 62 /**
48 * The root of the compilation unit, and the first node we visit. We hold 63 * The root of the compilation unit, and the first node we visit. We hold
49 * on to this for error reporting, as it can give us line numbers of other 64 * on to this for error reporting, as it can give us line numbers of other
50 * nodes. 65 * nodes.
51 */ 66 */
52 CompilationUnit _root; 67 CompilationUnit _root;
53 68
54 /** 69 /**
55 * An arbitrary string describing where the source code came from. Most 70 * An arbitrary string describing where the source code came from. Most
56 * obviously, this could be a file path. We use this when reporting 71 * obviously, this could be a file path. We use this when reporting
57 * invalid messages. 72 * invalid messages.
58 */ 73 */
59 String _origin; 74 String _origin;
60 75
61 void _reportErrorLocation(ASTNode node) { 76 String _reportErrorLocation(ASTNode node) {
62 if (_origin != null) print(" from $_origin"); 77 var result = new StringBuffer();
78 if (_origin != null) result.write(" from $_origin");
63 var info = _root.lineInfo; 79 var info = _root.lineInfo;
64 if (info != null) { 80 if (info != null) {
65 var line = info.getLocation(node.offset); 81 var line = info.getLocation(node.offset);
66 print(" line: ${line.lineNumber}, column: ${line.columnNumber}"); 82 result.write(" line: ${line.lineNumber}, column: ${line.columnNumber}");
67 } 83 }
84 return result.toString();
68 } 85 }
69 86
70 /** 87 /**
71 * This visits the program source nodes looking for Intl.message uses 88 * This visits the program source nodes looking for Intl.message uses
72 * that conform to its pattern and then creating the corresponding 89 * that conform to its pattern and then creating the corresponding
73 * IntlMessage objects. We have to find both the enclosing function, and 90 * IntlMessage objects. We have to find both the enclosing function, and
74 * the Intl.message invocation. 91 * the Intl.message invocation.
75 */ 92 */
76 class MessageFindingVisitor extends GeneralizingASTVisitor { 93 class MessageFindingVisitor extends GeneralizingASTVisitor {
77 94
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 // before doing the tests below. 139 // before doing the tests below.
123 if (!namedArguments.every((each) => each is NamedExpression)) { 140 if (!namedArguments.every((each) => each is NamedExpression)) {
124 return "Message arguments except the message must be named"; 141 return "Message arguments except the message must be named";
125 } 142 }
126 var notArgs = namedArguments.where( 143 var notArgs = namedArguments.where(
127 (each) => each.name.label.name != 'args'); 144 (each) => each.name.label.name != 'args');
128 var values = notArgs.map((each) => each.expression).toList(); 145 var values = notArgs.map((each) => each.expression).toList();
129 if (!values.every((each) => each is SimpleStringLiteral)) { 146 if (!values.every((each) => each is SimpleStringLiteral)) {
130 "Intl.message arguments must be simple string literals"; 147 "Intl.message arguments must be simple string literals";
131 } 148 }
132 if (!notArgs.any((each) => each.name.label.name == 'name')) { 149 var messageName = notArgs.firstWhere(
150 (eachArg) => eachArg.name.label.name == 'name',
151 orElse: () => null);
152 if (messageName == null) {
133 return "The 'name' argument for Intl.message must be specified"; 153 return "The 'name' argument for Intl.message must be specified";
134 } 154 }
155 if ((messageName.expression is! SimpleStringLiteral)
156 || messageName.expression.value != name) {
157 return "The 'name' argument for Intl.message must be a simple string "
158 "literal and match the containing function name.";
159 }
135 var hasArgs = namedArguments.any((each) => each.name.label.name == 'args'); 160 var hasArgs = namedArguments.any((each) => each.name.label.name == 'args');
136 var hasParameters = !parameters.parameters.isEmpty; 161 var hasParameters = !parameters.parameters.isEmpty;
137 if (!hasArgs && hasParameters) { 162 if (!hasArgs && hasParameters) {
138 return "The 'args' argument for Intl.message must be specified"; 163 return "The 'args' argument for Intl.message must be specified";
139 } 164 }
140 return null; 165 return null;
141 } 166 }
142 167
143 /** 168 /**
144 * Record the parameters of the function or method declaration we last 169 * Record the parameters of the function or method declaration we last
145 * encountered before seeing the Intl.message call. 170 * encountered before seeing the Intl.message call.
146 */ 171 */
147 void visitMethodDeclaration(MethodDeclaration node) { 172 void visitMethodDeclaration(MethodDeclaration node) {
148 parameters = node.parameters; 173 parameters = node.parameters;
149 String name = node.name.name; 174 name = node.name.name;
150 super.visitMethodDeclaration(node); 175 super.visitMethodDeclaration(node);
151 } 176 }
152 177
153 /** 178 /**
154 * Record the parameters of the function or method declaration we last 179 * Record the parameters of the function or method declaration we last
155 * encountered before seeing the Intl.message call. 180 * encountered before seeing the Intl.message call.
156 */ 181 */
157 void visitFunctionExpression(FunctionExpression node) {
158 parameters = node.parameters;
159 name = null;
160 super.visitFunctionExpression(node);
161 }
162
163 /**
164 * Record the parameters of the function or method declaration we last
165 * encountered before seeing the Intl.message call.
166 */
167 void visitFunctionDeclaration(FunctionDeclaration node) { 182 void visitFunctionDeclaration(FunctionDeclaration node) {
168 parameters = node.functionExpression.parameters; 183 parameters = node.functionExpression.parameters;
169 name = node.name.name; 184 name = node.name.name;
170 super.visitFunctionDeclaration(node); 185 super.visitFunctionDeclaration(node);
171 } 186 }
172 187
173 /** 188 /**
174 * Examine method invocations to see if they look like calls to Intl.message. 189 * Examine method invocations to see if they look like calls to Intl.message.
175 * If we've found one, stop recursing. This is important because we can have 190 * If we've found one, stop recursing. This is important because we can have
176 * Intl.message(...Intl.plural...) and we don't want to treat the inner 191 * Intl.message(...Intl.plural...) and we don't want to treat the inner
177 * plural as if it was an outermost message. 192 * plural as if it was an outermost message.
178 */ 193 */
179 void visitMethodInvocation(MethodInvocation node) { 194 void visitMethodInvocation(MethodInvocation node) {
180 if (!addIntlMessage(node)) { 195 if (!addIntlMessage(node)) {
181 return super.visitMethodInvocation(node); 196 return super.visitMethodInvocation(node);
182 } 197 }
183 } 198 }
184 199
185 /** 200 /**
186 * Check that the node looks like an Intl.message invocation, and create 201 * Check that the node looks like an Intl.message invocation, and create
187 * the [IntlMessage] object from it and store it in [messages]. Return true 202 * the [IntlMessage] object from it and store it in [messages]. Return true
188 * if we successfully extracted a message and should stop looking. Return 203 * if we successfully extracted a message and should stop looking. Return
189 * false if we didn't, so should continue recursing. 204 * false if we didn't, so should continue recursing.
190 */ 205 */
191 bool addIntlMessage(MethodInvocation node) { 206 bool addIntlMessage(MethodInvocation node) {
192 if (!looksLikeIntlMessage(node)) return false; 207 if (!looksLikeIntlMessage(node)) return false;
193 var reason = checkValidity(node); 208 var reason = checkValidity(node);
194 if (reason != null) { 209 if (reason != null) {
195 if (!suppressWarnings) { 210 if (!suppressWarnings) {
196 print("Skipping invalid Intl.message invocation\n <$node>"); 211 var err = new StringBuffer();
197 print(" reason: $reason"); 212 err.write("Skipping invalid Intl.message invocation\n <$node>\n");
198 _reportErrorLocation(node); 213 err.write(" reason: $reason\n");
214 err.write(_reportErrorLocation(node));
215 warnings.add(err.toString());
216 print(err);
199 } 217 }
200 // We found one, but it's not valid. Stop recursing. 218 // We found one, but it's not valid. Stop recursing.
201 return true; 219 return true;
202 } 220 }
203 var message; 221 var message;
204 if (node.methodName.name == "message") { 222 if (node.methodName.name == "message") {
205 message = messageFromIntlMessageCall(node); 223 message = messageFromIntlMessageCall(node);
206 } else { 224 } else {
207 message = messageFromDirectPluralOrGenderCall(node); 225 message = messageFromDirectPluralOrGenderCall(node);
208 } 226 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 */ 260 */
243 MainMessage messageFromIntlMessageCall(MethodInvocation node) { 261 MainMessage messageFromIntlMessageCall(MethodInvocation node) {
244 262
245 void extractFromIntlCall(MainMessage message, List arguments) { 263 void extractFromIntlCall(MainMessage message, List arguments) {
246 try { 264 try {
247 var interpolation = new InterpolationVisitor(message); 265 var interpolation = new InterpolationVisitor(message);
248 arguments.first.accept(interpolation); 266 arguments.first.accept(interpolation);
249 message.messagePieces.addAll(interpolation.pieces); 267 message.messagePieces.addAll(interpolation.pieces);
250 } on IntlMessageExtractionException catch (e) { 268 } on IntlMessageExtractionException catch (e) {
251 message = null; 269 message = null;
252 print("Error $e"); 270 var err = new StringBuffer();
253 print("Processing <$node>"); 271 err.write("Error $e\n");
254 _reportErrorLocation(node); 272 err.write("Processing <$node>\n");
273 err.write(_reportErrorLocation(node));
274 print(err);
275 warnings.add(err);
255 } 276 }
256 } 277 }
257 278
258 void setValue(MainMessage message, String fieldName, String fieldValue) { 279 void setValue(MainMessage message, String fieldName, String fieldValue) {
259 message[fieldName] = fieldValue; 280 message[fieldName] = fieldValue;
260 } 281 }
261 282
262 return _messageFromNode(node, extractFromIntlCall, setValue); 283 return _messageFromNode(node, extractFromIntlCall, setValue);
263 } 284 }
264 285
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 message.parent = parent; 451 message.parent = parent;
431 452
432 var arguments = node.argumentList.arguments.elements; 453 var arguments = node.argumentList.arguments.elements;
433 for (var arg in arguments.where((each) => each is NamedExpression)) { 454 for (var arg in arguments.where((each) => each is NamedExpression)) {
434 try { 455 try {
435 var interpolation = new InterpolationVisitor(message); 456 var interpolation = new InterpolationVisitor(message);
436 arg.expression.accept(interpolation); 457 arg.expression.accept(interpolation);
437 message[arg.name.label.token.toString()] = interpolation.pieces; 458 message[arg.name.label.token.toString()] = interpolation.pieces;
438 } on IntlMessageExtractionException catch (e) { 459 } on IntlMessageExtractionException catch (e) {
439 message = null; 460 message = null;
440 print("Error $e"); 461 var err = new StringBuffer();
441 print("Processing <$node>"); 462 err.write("Error $e");
442 _reportErrorLocation(node); 463 err.write("Processing <$node>");
464 err.write(_reportErrorLocation(node));
465 print(err);
466 warnings.add(err);
443 } 467 }
444 } 468 }
445 var mainArg = node.argumentList.arguments.elements.firstWhere( 469 var mainArg = node.argumentList.arguments.elements.firstWhere(
446 (each) => each is! NamedExpression); 470 (each) => each is! NamedExpression);
447 if (mainArg is SimpleStringLiteral) { 471 if (mainArg is SimpleStringLiteral) {
448 message.mainArgument = mainArg.toString(); 472 message.mainArgument = mainArg.toString();
449 } else { 473 } else {
450 message.mainArgument = mainArg.name; 474 message.mainArgument = mainArg.name;
451 } 475 }
452 return message; 476 return message;
453 } 477 }
454 } 478 }
455 479
456 /** 480 /**
457 * Exception thrown when we cannot process a message properly. 481 * Exception thrown when we cannot process a message properly.
458 */ 482 */
459 class IntlMessageExtractionException implements Exception { 483 class IntlMessageExtractionException implements Exception {
460 /** 484 /**
461 * A message describing the error. 485 * A message describing the error.
462 */ 486 */
463 final String message; 487 final String message;
464 488
465 /** 489 /**
466 * Creates a new exception with an optional error [message]. 490 * Creates a new exception with an optional error [message].
467 */ 491 */
468 const IntlMessageExtractionException([this.message = ""]); 492 const IntlMessageExtractionException([this.message = ""]);
469 493
470 String toString() => "IntlMessageExtractionException: $message"; 494 String toString() => "IntlMessageExtractionException: $message";
471 } 495 }
OLDNEW
« no previous file with comments | « no previous file | pkg/intl/test/message_extraction/extract_to_json.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698