Repository: DmitrySoshnikov/regexp-tree (js)
Issue: Optimizer: char-escape-unescape-transform must handle class ranges
The `char-escape-unescape` transform calls `preservesEscape()` instead of `preservesInCharClass()` for chars inside a class range, because their parent is a ClassRange, not a CharacterClass.

This leads to `/[+-\?]/` not being optimized as `/[+-?]/`.
Pull request description: Optimizer: Unescape ] and fix unescape in class ranges
Fixes #128 and #129

Patch:
diff --git a/src/optimizer/transforms/char-escape-unescape-transform.js b/src/optimizer/transforms/char-escape-unescape-transform.js
index 75f7e03..33d3d0f 100644
--- a/src/optimizer/transforms/char-escape-unescape-transform.js
+++ b/src/optimizer/transforms/char-escape-unescape-transform.js
@@ -30,7 +30,7 @@ function shouldUnescape(path) {
   const {node: {value}, index, parent} = path;
 
   // In char class (, etc are allowed.
-  if (parent.type !== 'CharacterClass') {
+  if (parent.type !== 'CharacterClass' && parent.type !== 'ClassRange') {
     return !preservesEscape(value, index, parent);
   }
 
@@ -61,7 +61,7 @@ function preservesEscape(value, index, parent) {
     return preservesClosingCurlyBraceEscape(index, parent);
   }
 
-  return /[*[\]()+?^$./\\|]/.test(value);
+  return /[*[()+?^$./\\|]/.test(value);
 }
 
 function consumeNumbers(startIndex, parent, rtl) {


Test patch:
diff --git a/src/optimizer/__tests__/optimizer-integration-test.js b/src/optimizer/__tests__/optimizer-integration-test.js
index d7021fd..1c1219b 100644
--- a/src/optimizer/__tests__/optimizer-integration-test.js
+++ b/src/optimizer/__tests__/optimizer-integration-test.js
@@ -26,8 +26,8 @@ describe('optimizer-integration-test', () => {
   });
 
   it('preserve escape', () => {
-    const original = /^[\^\*\$\(\)]\^\*\$\(\)\.\[\]\/\\$/;
-    const optimized = /^[$()*^]\^\*\$\(\)\.\[\]\/\\$/;
+    const original = /^[\^\*\$\(\)]\^\*\$\(\)\.\[\/\\$/;
+    const optimized = /^[$()*^]\^\*\$\(\)\.\[\/\\$/;
 
     expect(optimizer.optimize(original).toString())
       .toBe(optimized.toString());
diff --git a/src/optimizer/transforms/__tests__/char-escape-unescape-transform-test.js b/src/optimizer/transforms/__tests__/char-escape-unescape-transform-test.js
index b2d398b..0512552 100644
--- a/src/optimizer/transforms/__tests__/char-escape-unescape-transform-test.js
+++ b/src/optimizer/transforms/__tests__/char-escape-unescape-transform-test.js
@@ -11,10 +11,10 @@ const charUnescape = require('../char-escape-unescape-transform');
 describe('\e -> e', () => {
 
   it('simple chars', () => {
-    const re = transform(/\e\*/, [
+    const re = transform(/\e\*\]/, [
       charUnescape,
     ]);
-    expect(re.toString()).toBe(/e\*/.toString());
+    expect(re.toString()).toBe(/e\*]/.toString());
   });
 
   it('preserve escape', () => {
@@ -66,10 +66,12 @@ describe('\e -> e', () => {
   });
 
   it('char class', () => {
-    const re = transform(/[\e\*\(\]\ \^\$\-]\(\n/, [
+    const re = transform(/[\e\*\(\]\ \^\$\/-\?\-]\(\n/, [
       charUnescape,
     ]);
-    expect(re.toString()).toBe(/[e*(\] ^$-]\(\n/.toString());
+    // Can't use native toString() conversion on a regexp here
+    // because Node 6 stringifies /[/]/ as /[\/]/
+    expect(re.toString()).toBe('/[e*(\\] ^$/-?-]\\(\\n/');
   });
 
   it('does not unescape \^ in char class when in first position', () => {