Allow ClassName_methodName for a message name, easier to disambiguate duplicates

BUG=
R=kevmoo@google.com

Review URL: https://codereview.chromium.org//1244843002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24bd4ff..e52d6ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.12.4+1
+  * Allow the name of an Intl.message to be "ClassName_methodName", as
+    well as "functionName". This makes it easier to disambiguate
+    messages with the same name but in different classes.
+
 ## 0.12.4
   * Handle spaces in ARB files where we didn't handle them before, and
   where Google translation toolkit is now putting them.
diff --git a/README.md b/README.md
index ec71975..e1fcfc0 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,8 @@
 This provides, in addition to the basic message string, a name, a
 description for translators, the arguments used in the message, and
 examples. The `name` and `args` parameters are required, and must
-match the name and arguments list of the function.  In the future we
+match the name (or ClassName_methodName) and arguments list of the
+function respectively.  In the future we
 hope to have these provided automatically.
 
 This can be run in the program before any translation has been done,
diff --git a/lib/intl.dart b/lib/intl.dart
index 50ba431..d952691 100644
--- a/lib/intl.dart
+++ b/lib/intl.dart
@@ -138,12 +138,16 @@
    * returns the value of this call and provides a scope for the variables that
    * will be substituted in the message.
    *
-   * The parameters are a
-   * [message_str] to be translated, which may be interpolated
-   * based on one or more variables, the [name] of the message, which should
-   * match the enclosing function name, the [args] of the enclosing
-   * function, a [desc] providing a description of usage
-   * and a map of [examples] for each interpolated variable. For example
+   * The [message_str] is the string to be translated, which may be interpolated
+   * based on one or more variables. The [name] of the message must 
+   * match the enclosing function name. For methods, it can also be 
+   * className_methodName. So for a method hello in class Simple, the name
+   * can be either "hello" or "Simple_hello". The name must also be globally
+   * unique in the program, so the second form can make it easier to distinguish
+   * messages with the same name but in different classes.
+   * The [args] repeats the arguments of the enclosing
+   * function, [desc] provides a description of usage, 
+   * [examples] is a Map of exmaples for each interpolated variable. For example
    *       hello(yourName) => Intl.message(
    *         "Hello, $yourName",
    *         name: "hello",
diff --git a/lib/src/intl_message.dart b/lib/src/intl_message.dart
index 28a118a..3df4a61 100644
--- a/lib/src/intl_message.dart
+++ b/lib/src/intl_message.dart
@@ -93,10 +93,23 @@
       return "The 'name' argument for Intl.message must be a simple string "
           "literal.";
     }
-    if (outerName != null && outerName != messageName.expression.value) {
-      return "The 'name' argument for Intl.message must match "
-          "the name of the containing function ("
-          "'${messageName.expression.value}' vs. '$outerName')";
+    var hasOuterName = outerName != null;
+    var givenName = messageName.expression.value;
+    var simpleMatch = outerName == givenName;
+    ClassDeclaration classNode(n) {
+      if (n == null) return null;
+      if (n is ClassDeclaration) return n;
+      return classNode(n.parent);
+    }
+    var classDeclaration = classNode(node);
+    var classPlusMethod = classDeclaration == null
+        ? null
+        : "${classDeclaration.name.token.toString()}_$outerName";
+    var classMatch = classPlusMethod != null && (givenName == classPlusMethod);
+    if (!(hasOuterName && (simpleMatch || classMatch))) {
+      return "The 'name' argument for Intl.message must match either"
+          "the name of the containing function or <className>_<methodName> ("
+          "'$givenName' vs. '$outerName')";
     }
     var simpleArguments = arguments.where((each) => each is NamedExpression &&
         ["desc", "name"].contains(each.name.label.name));
diff --git a/pubspec.yaml b/pubspec.yaml
index cd7e41a..071c5ba 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: intl
-version: 0.12.4
+version: 0.12.4+1
 author: Dart Team <misc@dartlang.org>
 description: Contains code to deal with internationalized/localized messages, date and number formatting and parsing, bi-directional text, and other internationalization issues.
 homepage: https://github.com/dart-lang/intl
diff --git a/test/message_extraction/make_hardcoded_translation.dart b/test/message_extraction/make_hardcoded_translation.dart
index 4143fe8..90bcfc4 100644
--- a/test/message_extraction/make_hardcoded_translation.dart
+++ b/test/message_extraction/make_hardcoded_translation.dart
@@ -29,7 +29,7 @@
       "dollars \${ (les accolades sont ok), et xml/html réservés <& et "
       "des citations \" "
       "avec quelques paramètres ainsi {a}, {b}, et {c}",
-  "method": "Cela vient d'une méthode",
+  "YouveGotMessages_method": "Cela vient d'une méthode",
   "nonLambda": "Cette méthode n'est pas un lambda",
   "staticMessage": "Cela vient d'une méthode statique",
   "notAlwaysTranslated": "Ce manque certaines traductions",
@@ -89,7 +89,7 @@
   "message3": "Zeichen, die Flucht benötigen, zB Schrägstriche \\ Dollar "
       "\${ (geschweiften Klammern sind ok) und xml reservierte Zeichen <& und "
       "Zitate \" Parameter {a}, {b} und {c}",
-  "method": "Dies ergibt sich aus einer Methode",
+  "YouveGotMessages_method": "Dies ergibt sich aus einer Methode",
   "nonLambda": "Diese Methode ist nicht eine Lambda",
   "staticMessage": "Dies ergibt sich aus einer statischen Methode",
   "thisNameIsNotInTheOriginal": "Could this lead to something malicious?",
diff --git a/test/message_extraction/part_of_sample_with_messages.dart b/test/message_extraction/part_of_sample_with_messages.dart
index dc52d21..31cddcc 100644
--- a/test/message_extraction/part_of_sample_with_messages.dart
+++ b/test/message_extraction/part_of_sample_with_messages.dart
@@ -18,7 +18,7 @@
 
   // An instance method, rather than a standalone function.
   method() => Intl.message("This comes from a method",
-      name: 'method', desc: 'This is a method with a '
+      name: 'YouveGotMessages_method', desc: 'This is a method with a '
       'long description which spans '
       'multiple lines.');