4
4
5
5
import 'dart:convert' ;
6
6
import 'dart:io' ;
7
+ import 'dart:math' as math;
7
8
8
9
import 'package:analyzer/dart/analysis/features.dart' ;
9
10
import 'package:analyzer/dart/analysis/utilities.dart' ;
10
11
import 'package:analyzer/dart/ast/ast.dart' ;
11
12
import 'package:analyzer/dart/ast/token.dart' ;
12
13
import 'package:analyzer/dart/ast/visitor.dart' ;
14
+ import 'package:collection/collection.dart' ;
13
15
import 'package:crypto/crypto.dart' ;
14
16
import 'package:dart_style/dart_style.dart' ;
15
17
import 'package:grinder/grinder.dart' ;
16
18
import 'package:path/path.dart' as p;
19
+ import 'package:source_span/source_span.dart' ;
17
20
18
21
import 'package:sass/src/util/nullable.dart' ;
19
22
@@ -69,37 +72,46 @@ class _Visitor extends RecursiveAstVisitor<void> {
69
72
/// The source of the original asynchronous file.
70
73
final String _source;
71
74
75
+ /// The path from which [_source] was loaded.
76
+ final String _path;
77
+
72
78
/// The current position in [_source] .
73
79
var _position = 0 ;
74
80
75
81
/// The buffer in which the text of the synchronous file is built up.
76
82
final _buffer = StringBuffer ();
77
83
84
+ /// Returns the [SourceFile] which is being rewritten.
85
+ ///
86
+ /// This is only used for debugging and error reporting.
87
+ SourceFile get _sourceFile =>
88
+ SourceFile .fromString (_source, url: p.toUri (_path));
89
+
78
90
/// The synchronous text.
79
91
String get result {
80
92
_buffer.write (_source.substring (_position));
81
93
_position = _source.length;
82
94
return _buffer.toString ();
83
95
}
84
96
85
- _Visitor (this ._source, String path ) {
97
+ _Visitor (this ._source, this ._path ) {
86
98
var afterHeader = "\n " .allMatches (_source).skip (3 ).first.end;
87
99
_buffer.writeln (_source.substring (0 , afterHeader));
88
100
_buffer.writeln ("""
89
- // DO NOT EDIT. This file was generated from ${p .basename (path )}.
101
+ // DO NOT EDIT. This file was generated from ${p .basename (_path )}.
90
102
// See tool/grind/synchronize.dart for details.
91
103
//
92
104
// Checksum: ${sha1 .convert (utf8 .encode (_source ))}
93
105
//
94
106
// ignore_for_file: unused_import
95
107
""" );
96
108
97
- if (p.basename (path ) == 'async_evaluate.dart' ) {
109
+ if (p.basename (_path ) == 'async_evaluate.dart' ) {
98
110
_buffer.writeln ();
99
111
_buffer.writeln ("import 'async_evaluate.dart' show EvaluateResult;" );
100
112
_buffer.writeln ("export 'async_evaluate.dart' show EvaluateResult;" );
101
113
_buffer.writeln ();
102
- } else if (p.basename (path ) == 'async_compile.dart' ) {
114
+ } else if (p.basename (_path ) == 'async_compile.dart' ) {
103
115
_buffer.writeln ();
104
116
_buffer.writeln ("export 'async_compile.dart';" );
105
117
_buffer.writeln ();
@@ -132,10 +144,19 @@ class _Visitor extends RecursiveAstVisitor<void> {
132
144
}
133
145
134
146
void visitClassDeclaration (ClassDeclaration node) {
135
- if (_sharedClasses.contains (node.name2 .lexeme)) {
147
+ if (_sharedClasses.contains (node.name .lexeme)) {
136
148
_skipNode (node);
137
149
} else {
138
- super .visitClassDeclaration (node);
150
+ for (var child in node.sortedCommentAndAnnotations) {
151
+ child.accept (this );
152
+ }
153
+ _rename (node.name);
154
+ node.typeParameters? .accept (this );
155
+ node.extendsClause? .accept (this );
156
+ node.withClause? .accept (this );
157
+ node.implementsClause? .accept (this );
158
+ node.nativeClause? .accept (this );
159
+ node.members.accept (this );
139
160
}
140
161
}
141
162
@@ -144,8 +165,17 @@ class _Visitor extends RecursiveAstVisitor<void> {
144
165
node.visitChildren (this );
145
166
}
146
167
168
+ void visitFunctionDeclaration (FunctionDeclaration node) {
169
+ for (var child in node.sortedCommentAndAnnotations) {
170
+ child.accept (this );
171
+ }
172
+ node.returnType? .accept (this );
173
+ _rename (node.name);
174
+ node.functionExpression.accept (this );
175
+ }
176
+
147
177
void visitMethodDeclaration (MethodDeclaration node) {
148
- if (_synchronizeName (node.name2 .lexeme) != node.name2 .lexeme) {
178
+ if (_synchronizeName (node.name .lexeme) != node.name .lexeme) {
149
179
// If the file defines any asynchronous versions of synchronous functions,
150
180
// remove them.
151
181
_skipNode (node);
@@ -189,8 +219,8 @@ class _Visitor extends RecursiveAstVisitor<void> {
189
219
}
190
220
191
221
void visitNamedType (NamedType node) {
192
- if (["Future" , "FutureOr" ].contains (node.name.name )) {
193
- _skip (node.name.beginToken );
222
+ if (["Future" , "FutureOr" ].contains (node.name2.lexeme )) {
223
+ _skip (node.name2 );
194
224
var typeArguments = node.typeArguments;
195
225
if (typeArguments != null ) {
196
226
_skip (typeArguments.leftBracket);
@@ -199,18 +229,30 @@ class _Visitor extends RecursiveAstVisitor<void> {
199
229
} else {
200
230
_buffer.write ("void" );
201
231
}
202
- } else if (node.name.name == "Module" ) {
232
+ } else if (node.name2.lexeme == "Module" ) {
203
233
_skipNode (node);
204
234
_buffer.write ("Module<Callable>" );
205
235
} else {
206
236
super .visitNamedType (node);
207
237
}
208
238
}
209
239
240
+ /// Writes through [node] 's (synchronized) name.
241
+ ///
242
+ /// Assumes [node] has a name field with type [Token] .
243
+ void _rename (Token token) {
244
+ _skip (token);
245
+ _buffer.write (_synchronizeName (token.lexeme));
246
+ }
247
+
210
248
/// Writes [_source] to [_buffer] up to the beginning of [token] , then puts
211
249
/// [_position] after [token] so it doesn't get written.
212
250
void _skip (Token ? token) {
213
251
if (token == null ) return ;
252
+ if (token.offset < _position) {
253
+ throw _alreadyEmittedException (_spanForToken (token));
254
+ }
255
+
214
256
_buffer.write (_source.substring (_position, token.offset));
215
257
_position = token.end;
216
258
}
@@ -224,6 +266,10 @@ class _Visitor extends RecursiveAstVisitor<void> {
224
266
225
267
/// Writes [_source] to [_buffer] up to the beginning of [node] .
226
268
void _writeTo (AstNode node) {
269
+ if (node.beginToken.offset < _position) {
270
+ throw _alreadyEmittedException (_spanForNode (node));
271
+ }
272
+
227
273
_buffer.write (_source.substring (_position, node.beginToken.offset));
228
274
_position = node.beginToken.offset;
229
275
}
@@ -232,6 +278,10 @@ class _Visitor extends RecursiveAstVisitor<void> {
232
278
///
233
279
/// This leaves [_position] at the end of [node] .
234
280
void _write (AstNode node) {
281
+ if (node.beginToken.offset < _position) {
282
+ throw _alreadyEmittedException (_spanForNode (node));
283
+ }
284
+
235
285
_position = node.beginToken.offset;
236
286
node.accept (this );
237
287
_buffer.write (_source.substring (_position, node.endToken.end));
@@ -248,4 +298,26 @@ class _Visitor extends RecursiveAstVisitor<void> {
248
298
return name;
249
299
}
250
300
}
301
+
302
+ SourceSpanException _alreadyEmittedException (SourceSpan span) {
303
+ var lines = _buffer.toString ().split ("\n " );
304
+ return SourceSpanException (
305
+ "Node was already emitted. Last 3 lines:\n\n " +
306
+ lines
307
+ .slice (math.max (lines.length - 3 , 0 ))
308
+ .map ((line) => " $line " )
309
+ .join ("\n " ) +
310
+ "\n " ,
311
+ span);
312
+ }
313
+
314
+ /// Returns a [FileSpan] that represents [token] 's position in the source
315
+ /// file.
316
+ SourceSpan _spanForToken (Token token) =>
317
+ _sourceFile.span (token.offset, token.end);
318
+
319
+ /// Returns a [FileSpan] that represents [token] 's position in the source
320
+ /// file.
321
+ SourceSpan _spanForNode (AstNode node) =>
322
+ _sourceFile.span (node.beginToken.offset, node.endToken.end);
251
323
}
0 commit comments