Ruff 0.0.264が片仮名の`ノ`を"ambiguous unicode character"と判断する理由について



# 環境

```plain:Python関係
$ python --version
Python 3.11.2

$ ruff --version
ruff 0.0.264
```


```plain:VSCodeのバージョン情報
バージョン: 1.77.3
コミット: 704ed70d4fd1c6bd6342c436f1ede30d1cff4710
 日付: 2023-04-12T09:16:52.732Z
Electron: 19.1.11
Chromium: 102.0.5005.196
Node.js: 16.14.2
V8: 10.2.154.26-electron.0
OS: Linux x64 5.15.0-70-generic
Sandboxed: No
```


```plain:Unicode confusables
# confusables.txt
# Date: 2022-08-26, 16:49:08 GMT
# © 2022 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see https://www.unicode.org/terms_of_use.html
#
# Unicode Security Mechanisms for UTS #39
# Version: 15.0.0
#
# For documentation and usage, see https://www.unicode.org/reports/tr39
```


# 起きたこと
以下のPythonコードを静的解析ツール[Ruff](https://github.com/charliermarsh/ruff)でチェックすると、以下のエラーが発生しました。

```python:sample.py
"""
ａnnotation
アノテーション
"""
```

```
$ ruff check sample.py --select RUF
sample.py:2:1: RUF002 [*] Docstring contains ambiguous unicode character `ａ` (did you mean `a`?)
sample.py:3:2: RUF002 [*] Docstring contains ambiguous unicode character `ノ` (did you mean `/`?)Found 2 errors.
[*] 2 potentially fixable with the --fix option.
```

全角文字`ａ`を半角文字`a`に変えるのは納得できるのですが、カタカナの`ノ`をスラッシュ`/`に変えるとは、どういうことなのでしょうか？


# ambiguous unicode characterとは何か？
よく分かりませんでした。

# "ambiguous unicode character"の判定はどこで実装されているのか？
以下の関数で、confusableな文字かどうかを判定しています。

https://github.com/charliermarsh/ruff/blob/50c4bbc52b1fe3ef3c55500958344078191b301f/src/rules/checks.rs#LL1599C9-L1599C9

定数`CONFUSABLES`には、警告対象の"ambiguous unicode character"と、それに対する提案対象の文字が定義されています。

```typescript
/// See: https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1195
static CONFUSABLES: Lazy<BTreeMap<u32, u32>> = Lazy::new(|| {
    BTreeMap::from([
        (8232, 32),
```

https://github.com/charliermarsh/ruff/blob/50c4bbc52b1fe3ef3c55500958344078191b301f/src/rules/checks.rs#L12-L15

コメントには、https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1195 を参照と書いてあります。
つまりVSCodeのコードを移植したようです。


# Unicode Confusables
Unicodeには視覚的に似ている"confusable"な文字が定義されています。

>The data in [confusables] provide a mechanism for determining when two strings are visually confusable. The data in these files may be refined and extended over time. For information on handling modifications over time, see Section 2.10.1, Backward Compatibility in Unicode Technical Report #36, "Unicode Security Considerations" [UTR36] and the Migration section of this document.

https://www.unicode.org/reports/tr39/#Confusable_Detection

RuffやVSCodeは、"Unicode confusables"を参照しているようです。

https://www.unicode.org/Public/security/latest/confusables.txt

上記のページには、以下の様に記載されていました。"ノ"以外にもスラッシュ`/`に似ている文字はたくさんありました。

```plain:Unicode confusables
1735 ;	002F ;	MA	#* ( ᜵ → / ) PHILIPPINE SINGLE PUNCTUATION → SOLIDUS	# 
2041 ;	002F ;	MA	#* ( ⁁ → / ) CARET INSERTION POINT → SOLIDUS	# 
2215 ;	002F ;	MA	#* ( ∕ → / ) DIVISION SLASH → SOLIDUS	# 
2044 ;	002F ;	MA	#* ( ⁄ → / ) FRACTION SLASH → SOLIDUS	# 
2571 ;	002F ;	MA	#* ( ╱ → / ) BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT → SOLIDUS	# 
27CB ;	002F ;	MA	#* ( ⟋ → / ) MATHEMATICAL RISING DIAGONAL → SOLIDUS	# 
29F8 ;	002F ;	MA	#* ( ⧸ → / ) BIG SOLIDUS → SOLIDUS	# 
1D23A ;	002F ;	MA	#* ( 𝈺 → / ) GREEK INSTRUMENTAL NOTATION SYMBOL-47 → SOLIDUS	# 
31D3 ;	002F ;	MA	#* ( ㇓ → / ) CJK STROKE SP → SOLIDUS	# →⼃→
3033 ;	002F ;	MA	# ( 〳 → / ) VERTICAL KANA REPEAT MARK UPPER HALF → SOLIDUS	# 
2CC6 ;	002F ;	MA	# ( Ⳇ → / ) COPTIC CAPITAL LETTER OLD COPTIC ESH → SOLIDUS	# 
30CE ;	002F ;	MA	# ( ノ → / ) KATAKANA LETTER NO → SOLIDUS	# →⼃→
4E3F ;	002F ;	MA	# ( 丿 → / ) CJK UNIFIED IDEOGRAPH-4E3F → SOLIDUS	# →⼃→
2F03 ;	002F ;	MA	#* ( ⼃ → / ) KANGXI RADICAL SLASH → SOLIDUS	# 
```

ただし、VSCodeは"Unicode confusables"をすべて取り込んでいる訳ではないようです。
"Unicode confusables"には、カタカナの`カ`や平仮名の`く`も定義されています。

```plain:Unicode confusables
30AB ;	529B ;	MA	# ( カ → 力 ) KATAKANA LETTER KA → CJK UNIFIED IDEOGRAPH-529B	# →⼒→
...
304F ;	276C ;	MA	# ( く → ❬ ) HIRAGANA LETTER KU → MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT	# →㇛→→⟨→→〈→
```

しかしRuffは片仮名の`ノ`だけを警告します。

```python:sample2.py
"""
あいうえお
かきくけこ
さしすせそ
たちつてと
なにぬねの
はひふへほ
まみむめも
やゆよ
らりるれろ
わゐゑを
ん
アイウエオ
カキクケコ
サシスセソ
タチツテト
ナニヌネノ
ハヒフヘホ
マミムメモ
ヤユヨ
ラリルレロ
ワヰヱヲ
ン
"""
```


```
$ ruff check sample2.py --select RUF
sample2.py:17:5: RUF002 [*] Docstring contains ambiguous unicode character `ノ` (did you mean `/`?)
Found 1 error.
[*] 1 potentially fixable with the --fix option.
```


# 補足
RuffにIssueを作りました。

https://github.com/charliermarsh/ruff/issues/4268

# 参考サイト

https://util.unicode.org/UnicodeJsps/confusables.jsp

https://www.npmjs.com/package/unicode-confusables