Wednesday, May 7, 2014

Dart Annotations Are No Longer Structured

I am down to the very last unaccounted code analysis warnings in the Dart for Hipsters source.

Similar to the problems of the past few days, these manifest as unused imports in my Dart code:
Analyzing test.dart...
[hint] Unused import (/home/chris/repos/csdart/Book/code/classes/private.dart, line 4, col 8)
[hint] Unused import (/home/chris/repos/csdart/Book/code/primitives/types.dart, line 5, col 8)
Both of these two warnings come from custom-coded annotations:
library private_snippet;

import 'package:unittest/unittest.dart';
import '../annotations.dart';
// ...
library types_snippet;

import 'package:unittest/unittest.dart';
import 'dart:collection';
import '../annotations.dart';
// ...
I have already removed some instances of importing this annotations.dart library where it was not being used, but in both of these last two files, the annotations are being used:
      @IntentionalError('Demonstrating no compiler or run error')
      String muppet = 'Piggy';
      muppet = 42;
So what's the deal Dart? I am using these annotations, darn it.

In the annotations.dart file, I define the @IntentionalError annotation as optionally accepting a note:
/// Use when intentionally causing an error.
///     @IntentionalError('Demo when semi-colon is omitted')
///     var foo = "bar"
class IntentionalError {
  final String note;
  const IntentionalError([this.note]);
I originally introduced annotations in the book's source code because dartanalyzer would include them in its output:
➜  classes git:(master) ✗ dart_analyzer test.dart
file:/home/chris/repos/csdart/Book/code/classes/private.dart:26:31: Field 'collection' has no setter
    25:         @IntentionalError('Demonstrate non-existent setter call')
    26:         var collection = view.collection = new ComicsCollection();
Unfortunately this is no longer the case—dartanalyzer currently includes only the line number and error generated. Still, I like the idea of including structured annotations as a means of consistently annotating my code in a way that unstructured code comments cannot. Except even that no longer seems to apply. If I intentionally use an incorrect format for my @IntentionalError annotation:
      @IntentionalError(1, 2, 3, [4, 5], 'Demonstrating no compiler or run error')
      String muppet = 'Piggy';
      muppet = 42;
Then dartanalyzer proceeds blissfully unaware of a problem. Even if I remove the import of the annotations.dart file and include the annotations class directly in my source file it has no effect. Annotation classes now seem to serve as little more than documentation for a code documentation tool.

I am already grep-ignoring these intentional errors in my Bash script, so this is not a huge loss. The unused library warnings annoy me though, so I will comment out the import for the time being. If nothing else, the commented annotations.dart will serve to point me in the direction of documentation for my annotations—even if nothing is enforcing the format.

With that, I have all of my Dart for Hipsters tests passing, am ignoring only intentional and/or expected type errors, and have everything passing:
➜  code git:(master) ✗ ./
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: [skeleton] the skeleton app returns OK
CONSOLE MESSAGE: PASS: [main] the app returns OK
CONSOLE MESSAGE: PASS: [main] populates the list
CONSOLE MESSAGE: PASS: [numbers] 2 + 2 = 4
CONSOLE MESSAGE: PASS: [canvas] player is drawn
CONSOLE MESSAGE: All 163 tests passed.
CONSOLE MESSAGE: unittest-suite-success

Static type analysis...
Analyzing test.dart... 

Looks good!
Satisfied, I call it a night. I may still investigate structured annotations in Dart in the context of code reflection (they are used to great effect in Angular.dart). Tomorrow.

Update: Annotation imports (and annotations parameter signatures) DO work if the annotations appear before classes or methods/functions. In the above examples, I am using annotations in variable declarations. I think that should still work—that is, dartanalyzer should see those as legitimate annotations and recognize that they have been imported and/or have invalid parameters.

Actually, this may be more a question of where annotations are allowed to be in Dart code (they were definitely allowed above variable declarations at one point) and what, if anything dartanalyzer should do when annotations are placed in invalid locations.

Day #57