Fix source spans hashcode implementation

R=justinfagnani@google.com

Review URL: https://codereview.chromium.org//20600002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/source_maps@25514 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/span.dart b/lib/span.dart
index 1d2152d..e5057fc 100644
--- a/lib/span.dart
+++ b/lib/span.dart
@@ -69,7 +69,7 @@
   bool operator ==(Span other) =>
     sourceUrl == other.sourceUrl && start == other.start && end == other.end;
 
-  int get hashCode => sourceUrl.hashCode + start + (31 * (end - start));
+  int get hashCode => sourceUrl.hashCode + start.offset + (31 * length);
 
   String toString() => '<$runtimeType: $start $end $formatLocation $text>';
 }
@@ -99,6 +99,11 @@
     return offset - other.offset;
   }
 
+  bool operator ==(Location other) =>
+      sourceUrl == other.sourceUrl && offset == other.offset;
+
+  int get hashCode => sourceUrl.hashCode + offset;
+
   String toString() => '(Location $offset)';
   String get formatString => '$sourceUrl:${line + 1}:${column + 1}';
 }
diff --git a/test/span_test.dart b/test/span_test.dart
index c7b9cde..da3e063 100644
--- a/test/span_test.dart
+++ b/test/span_test.dart
@@ -316,6 +316,16 @@
     expect(file.span(8, 9, null).isIdentifier, false);
     expect(new FixedSpan('', 8, 1, 8, isIdentifier: null).isIdentifier, false);
   });
+
+  test('span/location implement == and hashCode', () {
+    expect(identical(span(10, 14), span(10, 14)), isFalse);
+    expect(span(10, 14), equals(span(10, 14)));
+    expect(span(10, 14).hashCode, span(10, 14).hashCode);
+
+    expect(identical(loc(13), loc(13)), isFalse);
+    expect(loc(13), equals(loc(13)));
+    expect(loc(13).hashCode, loc(13).hashCode);
+  });
 }
 
 class TestSpan extends Span {