Repository: wemake-services/wemake-python-styleguide

Issue:
False positive WPS226 in f strings
### What's wrong

line
f'query:"{query}"'
in ast node generate node with string value " (double qoute)

so if we have several lines like with we get tricky and false positive violation of WPS226

 WPS226 Found string literal over-use: " > 3


### How it should be

f string should be considered as single node.

### Flake8 version and plugins

{
  "dependencies": [],
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.9.9",
    "system": "Linux"
  },
  "plugins": [
    {
      "is_local": false,
      "plugin": "flake8-bandit",
      "version": "3.0.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-broken-line",
      "version": "0.4.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-bugbear",
      "version": "22.10.27"
    },
    {
      "is_local": false,
      "plugin": "flake8-comprehensions",
      "version": "3.10.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-darglint",
      "version": "1.8.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-debugger",
      "version": "4.1.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-docstrings",
      "version": "1.6.0, pydocstyle: 6.1.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-eradicate",
      "version": "1.4.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-string-format",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_commas",
      "version": "2.1.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_isort",
      "version": "4.2.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_quotes",
      "version": "3.3.1"
    },
    {
      "is_local": false,
      "plugin": "mccabe",
      "version": "0.6.1"
    },
    {
      "is_local": false,
      "plugin": "naming",
      "version": "0.12.1"
    },
    {
      "is_local": false,
      "plugin": "pycodestyle",
      "version": "2.8.0"
    },
    {
      "is_local": false,
      "plugin": "pyflakes",
      "version": "2.4.0"
    },
    {
      "is_local": false,
      "plugin": "rst-docstrings",
      "version": "0.2.7"
    },
    {
      "is_local": false,
      "plugin": "wemake_python_styleguide",
      "version": "0.16.1"
    }
  ],
  "version": "4.0.1"
}


### pip information

pip 22.3.1
(python 3.9)
absl-py==1.3.0
aiodns==3.0.0
aiohttp==3.8.1
aiosignal==1.3.1
aniso8601==7.0.0
asgiref==3.5.2
astor==0.8.1
astroid==2.12.12
astunparse==1.6.3
async-timeout==4.0.2
attrs==22.1.0
autoflake==1.4
bandit==1.7.4
black==22.3.0
boto3==1.23.4
botocore==1.26.10
Brotli==1.0.9
cachetools==5.0.0
cchardet==2.1.7
certifi==2022.9.24
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==2.0.12
click==8.1.3
ConfigArgParse==1.5.3
coverage==6.5.0
darglint==1.8.1
Deprecated==1.2.13
dill==0.3.6
distlib==0.3.6
dj-database-url==1.0.0
dj-email-url==1.0.6
Django==4.0.6
django-cache-url==3.4.2
django-cors-headers==3.12.0
django-injector==0.2.5
django-stubs==1.13.0
django-stubs-ext==0.7.0
docutils==0.19
environs==9.5.0
eradicate==2.1.0
filelock==3.8.0
flake8==4.0.1
flake8-bandit==3.0.0
flake8-broken-line==0.4.0
flake8-bugbear==22.10.27
flake8-commas==2.1.0
flake8-comprehensions==3.10.1
flake8-debugger==4.1.2
flake8-docstrings==1.6.0
flake8-eradicate==1.4.0
flake8-isort==4.2.0
flake8-polyfill==1.0.2
flake8-quotes==3.3.1
flake8-rst-docstrings==0.2.7
flake8-string-format==0.3.0
Flask==2.2.2
Flask-BasicAuth==0.2.0
Flask-Cors==3.0.10
flatbuffers==1.12
frozenlist==1.3.3
gast==0.4.0
gevent==22.10.2
geventhttpclient==2.0.8
gitdb==4.0.9
GitPython==3.1.29
google-auth==2.14.1
google-auth-oauthlib==0.4.6
google-pasta==0.2.0
graphene==2.1.9
graphene-django==2.15.0
graphql-core==2.3.2
graphql-relay==2.0.1
greenlet==2.0.1
grpcio==1.50.0
gunicorn==20.1.0
h5py==3.7.0
hash-chunker==0.1.9
identify==2.5.8
idna==3.4
importlib-metadata==5.0.0
inflect==5.5.2
iniconfig==1.1.1
injector==0.20.1
isort==5.10.1
itsdangerous==2.1.2
Jinja2==3.1.2
jmespath==1.0.1
joblib==1.2.0
kazoo==2.8.0
keras==2.9.0
Keras-Preprocessing==1.1.2
lazy-object-proxy==1.8.0
libclang==14.0.6
locust==2.9.0
Markdown==3.4.1
MarkupSafe==2.1.1
marshmallow==3.19.0
mccabe==0.6.1
msgpack==1.0.4
multidict==6.0.2
mypy==0.990
mypy-extensions==0.4.3
mysqlclient==2.1.0
nodeenv==1.7.0
numpy==1.23.4
oauthlib==3.2.2
opt-einsum==3.3.0
packaging==21.3
pandas==1.4.2
pathspec==0.10.2
pbr==5.11.0
pep8-naming==0.12.1
platformdirs==2.5.4
pluggy==0.13.1
pre-commit==2.16.0
promise==2.3
protobuf==3.19.6
psutil==5.9.4
py==1.11.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycares==4.2.2
pycodestyle==2.8.0
pycparser==2.21
pydocstyle==6.1.1
pyflakes==2.4.0
Pygments==2.13.0
pylint==2.15.4
pylint-django==2.5.3
pylint-plugin-utils==0.7
PyMySQL==1.0.2
pyparsing==3.0.9
pytest==6.2.4
pytest-cov==2.12.0
pytest-django==4.5.2
pytest-lazy-fixture==0.6.3
python-dateutil==2.8.2
python-dotenv==0.21.0
pytz==2022.6
PyYAML==6.0
pyzmq==22.3.0
redis==4.2.2
requests==2.27.1
requests-oauthlib==1.3.1
restructuredtext-lint==1.4.0
roundrobin==0.0.4
rsa==4.9
Rx==1.6.1
s3transfer==0.5.2
scikit-learn==1.0
scipy==1.9.3
singledispatch==3.7.0
six==1.16.0
smmap==5.0.0
snowballstemmer==2.2.0
SQLAlchemy==1.4.36
sqlparse==0.4.3
stevedore==4.1.1
tenacity==8.0.1
tensorboard==2.9.1
tensorboard-data-server==0.6.1
tensorboard-plugin-wit==1.8.1
tensorflow-cpu==2.9.1
tensorflow-estimator==2.9.0
tensorflow-io-gcs-filesystem==0.27.0
termcolor==2.1.0
text-unidecode==1.3
threadpoolctl==3.1.0
toml==0.10.2
tomli==2.0.1
tomlkit==0.11.6
tqdm==4.64.0
types-cachetools==5.0.1
types-pytz==2022.6.0.1
types-PyYAML==6.0.12.2
types-redis==4.2.5
types-requests==2.27.19
types-urllib3==1.26.25.3
typing_extensions==4.4.0
urllib3==1.26.12
virtualenv==20.16.7
wemake-python-styleguide==0.16.1
Werkzeug==2.2.2
wrapt==1.14.1
yarl==1.8.1
zipp==3.10.0
zope.event==4.5.0
zope.interface==5.5.1


### OS information

Ubuntu 22.04.1 LTS


Discussion:
Hi @whysage 

It's a good catch! Do you want to prepare a PR that adds `'"'` to the [`_ignored_string_constants`](https://github.com/wemake-services/wemake-python-styleguide/blob/52c7c4e55a6e8d9ee032db2c9761c9af39325c27/wemake_python_styleguide/visitors/ast/complexity/overuses.py#L43) and `'"\"\"'` to the [test](https://github.com/wemake-services/wemake-python-styleguide/blob/52c7c4e55a6e8d9ee032db2c9761c9af39325c27/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py#L201) parameters?
sure

Resolving patch:
diff --git a/wemake_python_styleguide/visitors/ast/complexity/overuses.py b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
--- a/wemake_python_styleguide/visitors/ast/complexity/overuses.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
@@ -49,6 +49,10 @@ class StringOveruseVisitor(base.BaseNodeVisitor):
         '\r\n',
         '\t',
         '|',
+        '"',
+        "'",
+        b'"',
+        b"'",
         b' ',
         b'.',
         b',',


Test patch:
diff --git a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
--- a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
@@ -204,6 +204,8 @@ def test_string_type_annotations(
     '""',
     '","',
     '"."',
+    "'\"'",
+    '"\'"',
 ])
 @pytest.mark.parametrize('prefix', [
     'b',