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

Side by Side Diff: pkg/observe/lib/transformer.dart

Issue 513023002: Step one towards stable error messages with details: (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 3 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 | « pkg/observe/lib/src/messages.dart ('k') | pkg/observe/pubspec.yaml » ('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 /// Code transform for @observable. The core transformation is relatively 5 /// Code transform for @observable. The core transformation is relatively
6 /// straightforward, and essentially like an editor refactoring. 6 /// straightforward, and essentially like an editor refactoring.
7 library observe.transformer; 7 library observe.transformer;
8 8
9 import 'dart:async'; 9 import 'dart:async';
10 10
11 import 'package:analyzer/analyzer.dart'; 11 import 'package:analyzer/analyzer.dart';
12 import 'package:analyzer/src/generated/ast.dart'; 12 import 'package:analyzer/src/generated/ast.dart';
13 import 'package:analyzer/src/generated/error.dart'; 13 import 'package:analyzer/src/generated/error.dart';
14 import 'package:analyzer/src/generated/parser.dart'; 14 import 'package:analyzer/src/generated/parser.dart';
15 import 'package:analyzer/src/generated/scanner.dart'; 15 import 'package:analyzer/src/generated/scanner.dart';
16 import 'package:barback/barback.dart'; 16 import 'package:barback/barback.dart';
17 import 'package:code_transformers/messages/build_logger.dart';
17 import 'package:source_maps/refactor.dart'; 18 import 'package:source_maps/refactor.dart';
18 import 'package:source_span/source_span.dart'; 19 import 'package:source_span/source_span.dart';
19 20
21 import 'src/messages.dart';
22
20 /// A [Transformer] that replaces observables based on dirty-checking with an 23 /// A [Transformer] that replaces observables based on dirty-checking with an
21 /// implementation based on change notifications. 24 /// implementation based on change notifications.
22 /// 25 ///
23 /// The transformation adds hooks for field setters and notifies the observation 26 /// The transformation adds hooks for field setters and notifies the observation
24 /// system of the change. 27 /// system of the change.
25 class ObservableTransformer extends Transformer { 28 class ObservableTransformer extends Transformer {
26 29
30 final bool releaseMode;
27 final List<String> _files; 31 final List<String> _files;
28 ObservableTransformer([List<String> files]) : _files = files; 32 ObservableTransformer([List<String> files, bool releaseMode])
33 : _files = files, releaseMode = releaseMode == true;
29 ObservableTransformer.asPlugin(BarbackSettings settings) 34 ObservableTransformer.asPlugin(BarbackSettings settings)
30 : _files = _readFiles(settings.configuration['files']); 35 : _files = _readFiles(settings.configuration['files']),
36 releaseMode = settings.mode == BarbackMode.RELEASE;
31 37
32 static List<String> _readFiles(value) { 38 static List<String> _readFiles(value) {
33 if (value == null) return null; 39 if (value == null) return null;
34 var files = []; 40 var files = [];
35 bool error; 41 bool error;
36 if (value is List) { 42 if (value is List) {
37 files = value; 43 files = value;
38 error = value.any((e) => e is! String); 44 error = value.any((e) => e is! String);
39 } else if (value is String) { 45 } else if (value is String) {
40 files = [value]; 46 files = [value];
(...skipping 11 matching lines...) Expand all
52 var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id; 58 var id = idOrAsset is AssetId ? idOrAsset : idOrAsset.id;
53 return new Future.value(id.extension == '.dart' && 59 return new Future.value(id.extension == '.dart' &&
54 (_files == null || _files.contains(id.path))); 60 (_files == null || _files.contains(id.path)));
55 } 61 }
56 62
57 Future apply(Transform transform) { 63 Future apply(Transform transform) {
58 return transform.primaryInput.readAsString().then((content) { 64 return transform.primaryInput.readAsString().then((content) {
59 // Do a quick string check to determine if this is this file even 65 // Do a quick string check to determine if this is this file even
60 // plausibly might need to be transformed. If not, we can avoid an 66 // plausibly might need to be transformed. If not, we can avoid an
61 // expensive parse. 67 // expensive parse.
62 if (!observableMatcher.hasMatch(content)) return; 68 if (!observableMatcher.hasMatch(content)) return null;
63 69
64 var id = transform.primaryInput.id; 70 var id = transform.primaryInput.id;
65 // TODO(sigmund): improve how we compute this url 71 // TODO(sigmund): improve how we compute this url
66 var url = id.path.startsWith('lib/') 72 var url = id.path.startsWith('lib/')
67 ? 'package:${id.package}/${id.path.substring(4)}' : id.path; 73 ? 'package:${id.package}/${id.path.substring(4)}' : id.path;
68 var sourceFile = new SourceFile(content, url: url); 74 var sourceFile = new SourceFile(content, url: url);
75 var logger = new BuildLogger(transform,
76 convertErrorsToWarnings: !releaseMode);
69 var transaction = _transformCompilationUnit( 77 var transaction = _transformCompilationUnit(
70 content, sourceFile, transform.logger); 78 content, sourceFile, logger);
71 if (!transaction.hasEdits) { 79 if (!transaction.hasEdits) {
72 transform.addOutput(transform.primaryInput); 80 transform.addOutput(transform.primaryInput);
73 return; 81 } else {
82 var printer = transaction.commit();
83 // TODO(sigmund): emit source maps when barback supports it (see
84 // dartbug.com/12340)
85 printer.build(url);
86 transform.addOutput(new Asset.fromString(id, printer.text));
74 } 87 }
75 var printer = transaction.commit(); 88 return logger.writeOutput();
76 // TODO(sigmund): emit source maps when barback supports it (see
77 // dartbug.com/12340)
78 printer.build(url);
79 transform.addOutput(new Asset.fromString(id, printer.text));
80 }); 89 });
81 } 90 }
82 } 91 }
83 92
84 TextEditTransaction _transformCompilationUnit( 93 TextEditTransaction _transformCompilationUnit(
85 String inputCode, SourceFile sourceFile, TransformLogger logger) { 94 String inputCode, SourceFile sourceFile, BuildLogger logger) {
86 var unit = parseCompilationUnit(inputCode, suppressErrors: true); 95 var unit = parseCompilationUnit(inputCode, suppressErrors: true);
87 var code = new TextEditTransaction(inputCode, sourceFile); 96 var code = new TextEditTransaction(inputCode, sourceFile);
88 for (var directive in unit.directives) { 97 for (var directive in unit.directives) {
89 if (directive is LibraryDirective && _hasObservable(directive)) { 98 if (directive is LibraryDirective && _hasObservable(directive)) {
90 logger.warning('@observable on a library no longer has any effect. ' 99 logger.warning(NO_OBSERVABLE_ON_LIBRARY,
91 'It should be placed on individual fields.',
92 span: _getSpan(sourceFile, directive)); 100 span: _getSpan(sourceFile, directive));
93 break; 101 break;
94 } 102 }
95 } 103 }
96 104
97 for (var declaration in unit.declarations) { 105 for (var declaration in unit.declarations) {
98 if (declaration is ClassDeclaration) { 106 if (declaration is ClassDeclaration) {
99 _transformClass(declaration, code, sourceFile, logger); 107 _transformClass(declaration, code, sourceFile, logger);
100 } else if (declaration is TopLevelVariableDeclaration) { 108 } else if (declaration is TopLevelVariableDeclaration) {
101 if (_hasObservable(declaration)) { 109 if (_hasObservable(declaration)) {
102 logger.warning('Top-level fields can no longer be observable. ' 110 logger.warning(NO_OBSERVABLE_ON_TOP_LEVEL,
103 'Observable fields should be put in an observable objects.',
104 span: _getSpan(sourceFile, declaration)); 111 span: _getSpan(sourceFile, declaration));
105 } 112 }
106 } 113 }
107 } 114 }
108 return code; 115 return code;
109 } 116 }
110 117
111 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end); 118 _getSpan(SourceFile file, AstNode node) => file.span(node.offset, node.end);
112 119
113 /// True if the node has the `@observable` or `@published` annotation. 120 /// True if the node has the `@observable` or `@published` annotation.
114 // TODO(jmesserly): it is not good to be hard coding Polymer support here. 121 // TODO(jmesserly): it is not good to be hard coding Polymer support here.
115 bool _hasObservable(AnnotatedNode node) => 122 bool _hasObservable(AnnotatedNode node) =>
116 node.metadata.any(_isObservableAnnotation); 123 node.metadata.any(_isObservableAnnotation);
117 124
118 // TODO(jmesserly): this isn't correct if the annotation has been imported 125 // TODO(jmesserly): this isn't correct if the annotation has been imported
119 // with a prefix, or cases like that. We should technically be resolving, but 126 // with a prefix, or cases like that. We should technically be resolving, but
120 // that is expensive in analyzer, so it isn't feasible yet. 127 // that is expensive in analyzer, so it isn't feasible yet.
121 bool _isObservableAnnotation(Annotation node) => 128 bool _isObservableAnnotation(Annotation node) =>
122 _isAnnotationContant(node, 'observable') || 129 _isAnnotationContant(node, 'observable') ||
123 _isAnnotationContant(node, 'published') || 130 _isAnnotationContant(node, 'published') ||
124 _isAnnotationType(node, 'ObservableProperty') || 131 _isAnnotationType(node, 'ObservableProperty') ||
125 _isAnnotationType(node, 'PublishedProperty'); 132 _isAnnotationType(node, 'PublishedProperty');
126 133
127 bool _isAnnotationContant(Annotation m, String name) => 134 bool _isAnnotationContant(Annotation m, String name) =>
128 m.name.name == name && m.constructorName == null && m.arguments == null; 135 m.name.name == name && m.constructorName == null && m.arguments == null;
129 136
130 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; 137 bool _isAnnotationType(Annotation m, String name) => m.name.name == name;
131 138
132 void _transformClass(ClassDeclaration cls, TextEditTransaction code, 139 void _transformClass(ClassDeclaration cls, TextEditTransaction code,
133 SourceFile file, TransformLogger logger) { 140 SourceFile file, BuildLogger logger) {
134 141
135 if (_hasObservable(cls)) { 142 if (_hasObservable(cls)) {
136 logger.warning('@observable on a class no longer has any effect. ' 143 logger.warning(NO_OBSERVABLE_ON_CLASS, span: _getSpan(file, cls));
137 'It should be placed on individual fields.',
138 span: _getSpan(file, cls));
139 } 144 }
140 145
141 // We'd like to track whether observable was declared explicitly, otherwise 146 // We'd like to track whether observable was declared explicitly, otherwise
142 // report a warning later below. Because we don't have type analysis (only 147 // report a warning later below. Because we don't have type analysis (only
143 // syntactic understanding of the code), we only report warnings that are 148 // syntactic understanding of the code), we only report warnings that are
144 // known to be true. 149 // known to be true.
145 var explicitObservable = false; 150 var explicitObservable = false;
146 var implicitObservable = false; 151 var implicitObservable = false;
147 if (cls.extendsClause != null) { 152 if (cls.extendsClause != null) {
148 var id = _getSimpleIdentifier(cls.extendsClause.superclass.name); 153 var id = _getSimpleIdentifier(cls.extendsClause.superclass.name);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 190
186 var declaresObservable = explicitObservable || implicitObservable; 191 var declaresObservable = explicitObservable || implicitObservable;
187 192
188 // Track fields that were transformed. 193 // Track fields that were transformed.
189 var instanceFields = new Set<String>(); 194 var instanceFields = new Set<String>();
190 195
191 for (var member in cls.members) { 196 for (var member in cls.members) {
192 if (member is FieldDeclaration) { 197 if (member is FieldDeclaration) {
193 if (member.isStatic) { 198 if (member.isStatic) {
194 if (_hasObservable(member)){ 199 if (_hasObservable(member)){
195 logger.warning('Static fields can no longer be observable. ' 200 logger.warning(NO_OBSERVABLE_ON_STATIC_FIELD,
196 'Observable fields should be put in an observable objects.',
197 span: _getSpan(file, member)); 201 span: _getSpan(file, member));
198 } 202 }
199 continue; 203 continue;
200 } 204 }
201 if (_hasObservable(member)) { 205 if (_hasObservable(member)) {
202 if (!declaresObservable) { 206 if (!declaresObservable) {
203 logger.warning('Observable fields should be put in an observable ' 207 logger.warning(REQUIRE_OBSERVABLE_INTERFACE,
204 'objects. Please declare that this class extends from '
205 'Observable, includes Observable, or implements '
206 'Observable.',
207 span: _getSpan(file, member)); 208 span: _getSpan(file, member));
208 } 209 }
209 _transformFields(file, member, code, logger); 210 _transformFields(file, member, code, logger);
210 211
211 var names = member.fields.variables.map((v) => v.name.name); 212 var names = member.fields.variables.map((v) => v.name.name);
212 213
213 if (!_isReadOnly(member.fields)) instanceFields.addAll(names); 214 if (!_isReadOnly(member.fields)) instanceFields.addAll(names);
214 } 215 }
215 } 216 }
216 } 217 }
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 306
306 code.edit(offset, offset, inserted); 307 code.edit(offset, offset, inserted);
307 } 308 }
308 309
309 bool _isReadOnly(VariableDeclarationList fields) { 310 bool _isReadOnly(VariableDeclarationList fields) {
310 return _hasKeyword(fields.keyword, Keyword.CONST) || 311 return _hasKeyword(fields.keyword, Keyword.CONST) ||
311 _hasKeyword(fields.keyword, Keyword.FINAL); 312 _hasKeyword(fields.keyword, Keyword.FINAL);
312 } 313 }
313 314
314 void _transformFields(SourceFile file, FieldDeclaration member, 315 void _transformFields(SourceFile file, FieldDeclaration member,
315 TextEditTransaction code, TransformLogger logger) { 316 TextEditTransaction code, BuildLogger logger) {
316 317
317 final fields = member.fields; 318 final fields = member.fields;
318 if (_isReadOnly(fields)) return; 319 if (_isReadOnly(fields)) return;
319 320
320 // Private fields aren't supported: 321 // Private fields aren't supported:
321 for (var field in fields.variables) { 322 for (var field in fields.variables) {
322 final name = field.name.name; 323 final name = field.name.name;
323 if (Identifier.isPrivateName(name)) { 324 if (Identifier.isPrivateName(name)) {
324 logger.warning('Cannot make private field $name observable.', 325 logger.warning('Cannot make private field $name observable.',
325 span: _getSpan(file, field)); 326 span: _getSpan(file, field));
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 token = token.next; 403 token = token.next;
403 } 404 }
404 return token; 405 return token;
405 } 406 }
406 407
407 // TODO(sigmund): remove hard coded Polymer support (@published). The proper way 408 // TODO(sigmund): remove hard coded Polymer support (@published). The proper way
408 // to do this would be to switch to use the analyzer to resolve whether 409 // to do this would be to switch to use the analyzer to resolve whether
409 // annotations are subtypes of ObservableProperty. 410 // annotations are subtypes of ObservableProperty.
410 final observableMatcher = 411 final observableMatcher =
411 new RegExp("@(published|observable|PublishedProperty|ObservableProperty)"); 412 new RegExp("@(published|observable|PublishedProperty|ObservableProperty)");
OLDNEW
« no previous file with comments | « pkg/observe/lib/src/messages.dart ('k') | pkg/observe/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698