Skip to content

Commit 4119e41

Browse files
authored
specify the augmentation library structure and ordering of augmentations (#3084)
Closes #2158
1 parent 6b15dcf commit 4119e41

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

working/macros/feature-specification.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,118 @@ is not user visible. For example, if two macros are applied to two methods in
279279
the same class, there is no way for those macros to interfere with each other
280280
such that the application order can be detected.
281281
282+
### Augmentation library structure and ordering
283+
284+
It is important that applying macros in a given library always results in a
285+
consistent augmentation library. In particular, multiple tools should be able to
286+
run the same macros with the same inputs, and get the same augmentation library.
287+
This allows debugging and stack traces to work consistently, and be meaningful
288+
and useful.
289+
290+
It is also important that if a declaration is added in Phase 1, the source
291+
offsets to that declaration should not change after Phase 2 or 3 run. This means
292+
that tools don't need to update source offsets after each phase.
293+
294+
Another important consideration is that in Phase 2, the ordering of macros is
295+
user perceptable, and so augmentation results should be serializable to disk -
296+
with stable offsets - at multiple points throughout the process.
297+
298+
We have several rules based around maintaining this stability of source offsets
299+
and consistency of generated output across tools.
300+
301+
#### Rule #1: Each macro application appends a new independent augmentation
302+
303+
While augmentations on a given type declaration can be grouped together, they
304+
are not required to be. You can have as many `augment class A {}` declarations
305+
as you want in a given augmentation library. We take advantage of that fact, and
306+
require that every macro application creates its own augmentation.
307+
308+
This means each macro application is only appending new top level augmentation
309+
declarations to the augmentation library. It only grows over time, and previous
310+
lines are never altered.
311+
312+
#### Rule #2: Augmentations are added in application order, then source order
313+
314+
Where an application order is explicitly defined, the augmentations are appended
315+
in that same order as the primary sort.
316+
317+
If no order is defined between two macro applications, then their augmentations
318+
are sorted based on the source offset of the macro application.
319+
320+
Note that when multiple applications are on the same declaration, there is a
321+
defined order, which is the reverse source offset order.
322+
323+
#### Rule #3: Each augmentation should be separated by one empty line
324+
325+
We need to ensure consistent whitespace across tools, and this follows standard
326+
Dart style, which is to separate declarations with one empty line.
327+
328+
Note that if a given augmentation provides its own empty lines at the start,
329+
these should not be trimmed, and so you may end up with more than one empty line
330+
separating declarations.
331+
332+
In the future, we may decide to run `dart format` or some other lighter weight
333+
formatter on augmentations which would also enforce consistent whitespace.
334+
335+
#### Ordering example
336+
337+
Consider the complicated situation below, and assume all these macros are
338+
applied in all 3 phases:
339+
340+
```dart
341+
@TypeMacroOnB()
342+
class B extends A with C implements D {
343+
344+
}
345+
346+
@TypeMacroOnA()
347+
class A implements C {}
348+
349+
@TypeMacroOnC
350+
mixin C {
351+
@MemberMacroOnC()
352+
int get c;
353+
}
354+
355+
@TypeMacroOnD1()
356+
@TypeMacroOnD2()
357+
interface class D {}
358+
```
359+
360+
The augmentations would appear in the following order:
361+
362+
```dart
363+
// PHASE 1 augmentations order:
364+
//
365+
// TypeMacroOnB
366+
// TypeMacroOnA
367+
// MemberMacroOnC
368+
// TypeMacroOnC
369+
// TypeMacroOnD2
370+
// TypeMacroOnD1
371+
372+
// PHASE 2 augmentations order:
373+
//
374+
// MemberMacroOnC
375+
// TypeMacroOnC
376+
// TypeMacroOnA
377+
// TypeMacroOnD2
378+
// TypeMacroOnD1
379+
// TypeMacroOnB
380+
381+
// PHASE 3 augmentations order (same as phase 1):
382+
//
383+
// TypeMacroOnB
384+
// TypeMacroOnA
385+
// MemberMacroOnC
386+
// TypeMacroOnC
387+
// TypeMacroOnD2
388+
// TypeMacroOnD1
389+
```
390+
391+
Remember that each of these would have their own `augment class` declarations
392+
where applicable (following the first rule).
393+
282394
## Phases
283395

284396
Before we can get into how macro authors create macros, there is another

0 commit comments

Comments
 (0)