Fix broken or dangerous backquotes

Co-authored-by: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com>
This commit is contained in:
Fred Tingaud 2023-10-30 10:33:56 +01:00 committed by GitHub
parent 7e46053974
commit d3cfe19d7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
106 changed files with 853 additions and 387 deletions

View File

@ -39,7 +39,7 @@ tooling_tests_task:
PYTHONPATH: . PYTHONPATH: .
install_dependencies_script: install_dependencies_script:
- cd rspec-tools - cd rspec-tools
- pipenv install - pipenv install --dev
- pipenv run pip install pytest pytest-cov - pipenv run pip install pytest pytest-cov
tests_script: tests_script:
- bash ci/fetch_branches.sh - bash ci/fetch_branches.sh

View File

@ -1,105 +0,0 @@
# Validate the asciidoc environment directives in the given file(s).
# Errors are printed to the standard output stream.
#
# "ifdef" commands has to start the line without any leading spaces,
# as per asciidoc format.
#
# Only one form is allowed:
# ifdef::env-github,rspecator-view[]
#
# The closing command is:
# endif::env-github,rspecator-view[]
#
# It must be in the same file.
# Only one such environment is allowed per file.
from pathlib import Path
import sys
VALID_IFDEF = "ifdef::env-github,rspecator-view[]"
VALID_ENDIF = "endif::env-github,rspecator-view[]"
class Checker:
def __init__(self, file: Path):
assert file.exists()
assert file.is_file()
self._file = file
self._is_env_open = False
self._has_env = False
self._is_valid = True
def process(self) -> bool:
content = self._file.read_text(encoding="utf-8")
lines = content.splitlines(keepends=False)
for line_index, line in enumerate(lines):
line_number = line_index + 1
if line.startswith("ifdef::"):
self._process_open(line_number, line)
if line.startswith("endif::"):
self._process_close(line_number, line)
if self._is_env_open:
self._on_error(len(lines), "The ifdef command is not closed.")
return self._is_valid
def _process_open(self, line_number: int, line: str):
if self._has_env:
self._on_error(line_number, "Only one ifdef command is allowed per file.")
if self._is_env_open:
self._on_error(line_number, "The previous ifdef command was not closed.")
self._has_env = True
self._is_env_open = True
# IDEs should be configured to properly display the description,
# not the other way around.
# "env-vscode" was used in the passed. Instead, user should be able to
# toggle the rspecator view based on their needs. Help these users migrate.
if "vscode" in line:
self._on_error(
line_number,
"Configure VS Code to display rspecator-view by setting the asciidoctor attribute.",
)
if line != VALID_IFDEF:
self._on_error(
line_number,
f'Incorrect asciidoc environment. "{VALID_IFDEF}" should be used instead.',
)
def _process_close(self, line_number: int, line: str):
if not self._is_env_open:
self._on_error(line_number, "Unexpected endif command.")
self._is_env_open = False
if line != VALID_ENDIF:
self._on_error(
line_number,
f'Incorrect endif command. "{VALID_ENDIF}" should be used instead.',
)
def _on_error(self, line_number: int, message: str):
print(f"{self._file}:{line_number} {message}")
self._is_valid = False
def main():
files = sys.argv[1:]
if not files:
sys.exit("Missing input files")
valid = True
for file in files:
if not Checker(Path(file)).process():
valid = False
if not valid:
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -72,30 +72,8 @@ do
exit_code=1 exit_code=1
fi fi
else else
# Make sure include:: clauses are always more than one line away from the previous content # Add the full path of all adoc files that were affected for sanitization
# Detect includes stuck to the line before find ~+/"${dir}" -name '*.adoc' >> all_asciidocs
find "$dir" -name "*.adoc" -execdir sh -c 'grep -Pzl "\S[ \t]*\ninclude::" $1 | xargs -r -I@ realpath "$PWD/@"' shell {} \; > stuck
# Detect includes stuck to the line after
find "$dir" -name "*.adoc" -execdir sh -c 'grep -Pzl "include::[^\[]+\[\]\n[ \t]*[^\n]" $1 | xargs -r -I@ realpath "$PWD/@"' shell {} \; >> stuck
if [ -s stuck ]; then
echo "ERROR: These adoc files contain an include that is stuck to other content."
echo "This may result in broken tags and other display issues."
echo "Make sure there is an empty line before and after each include:"
cat stuck
exit_code=1
fi
rm -f stuck
# Validate modified files' ifdef/endif commands.
find "${dir}" -name '*.adoc' \
-exec python3 "./ci/asciidoc_validation/validate_environment.py" '{}' '+' \
>validate_env_commands 2>&1
if [ -s validate_env_commands ]; then
echo "ERROR: Some ifdef/endif commands are misused."
cat validate_env_commands
exit_code=1
fi
rm -f validate_env_commands
for language in "${dir}"/*/ for language in "${dir}"/*/
do do
@ -125,6 +103,18 @@ do
fi fi
done done
cd rspec-tools
cat ../all_asciidocs | xargs pipenv run rspec-tools check-asciidoc >validate_asciidoc 2>&1
if [ -s validate_asciidoc ]; then
echo "ERROR: Invalid asciidoc description:"
cat validate_asciidoc
exit_code=1
fi
rm -f validate_asciidoc ../all_asciidocs
cd ..
# Run asciidoctor and fail if a warning is emitted. # Run asciidoctor and fail if a warning is emitted.
# Use the tmp_SXYZ_language.adoc files (see note above). # Use the tmp_SXYZ_language.adoc files (see note above).
ADOC_COUNT=$(find rules -name "tmp*.adoc" | wc -l) ADOC_COUNT=$(find rules -name "tmp*.adoc" | wc -l)

View File

@ -17,7 +17,8 @@ slackclient = "*"
[dev-packages] [dev-packages]
pytest = ">=6.2.2" pytest = ">=6.2.2"
mypy = ">=0.800" mypy = ">=0.800"
rspec-tools = {editable = true, path = "."} rspec-tools = {file = ".", editable = true}
pytest-snapshot = "*"
[requires] [requires]
python_version = "3.9" python_version = "3.9"

371
rspec-tools/Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "ea9f240f8315f033a9883d447d2feb9b40fbad6bcbc336f404d47ebb6334e4c7" "sha256": "5275bfce06cb7aea2422d149640dee6b7052a74e37d82fb02665df43d223005e"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -106,6 +106,7 @@
"sha256:fc37e9aef10a696a5a4474802930079ccfc14d9f9c10b4662169671ff034b7df", "sha256:fc37e9aef10a696a5a4474802930079ccfc14d9f9c10b4662169671ff034b7df",
"sha256:fdee8405931b0615220e5ddf8cd7edd8592c606a8e4ca2a00704883c396e4479" "sha256:fdee8405931b0615220e5ddf8cd7edd8592c606a8e4ca2a00704883c396e4479"
], ],
"markers": "python_version >= '3.6'",
"version": "==3.8.6" "version": "==3.8.6"
}, },
"aiosignal": { "aiosignal": {
@ -113,6 +114,7 @@
"sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc",
"sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17" "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"
], ],
"markers": "python_version >= '3.7'",
"version": "==1.3.1" "version": "==1.3.1"
}, },
"appdirs": { "appdirs": {
@ -127,6 +129,7 @@
"sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f",
"sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"
], ],
"markers": "python_version >= '3.7'",
"version": "==4.0.3" "version": "==4.0.3"
}, },
"attrs": { "attrs": {
@ -134,6 +137,7 @@
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04",
"sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"
], ],
"markers": "python_version >= '3.7'",
"version": "==23.1.0" "version": "==23.1.0"
}, },
"beautifulsoup4": { "beautifulsoup4": {
@ -141,6 +145,7 @@
"sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da", "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da",
"sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a" "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"
], ],
"markers": "python_full_version >= '3.6.0'",
"version": "==4.12.2" "version": "==4.12.2"
}, },
"bs4": { "bs4": {
@ -155,6 +160,7 @@
"sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082",
"sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"
], ],
"markers": "python_version >= '3.6'",
"version": "==2023.7.22" "version": "==2023.7.22"
}, },
"cffi": { "cffi": {
@ -212,102 +218,104 @@
"sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956",
"sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"
], ],
"markers": "python_version >= '3.8'",
"version": "==1.16.0" "version": "==1.16.0"
}, },
"charset-normalizer": { "charset-normalizer": {
"hashes": [ "hashes": [
"sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843", "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5",
"sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786", "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93",
"sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e", "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a",
"sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8", "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d",
"sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4", "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c",
"sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa", "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1",
"sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d", "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58",
"sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82", "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2",
"sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7", "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557",
"sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895", "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147",
"sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d", "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041",
"sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a", "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2",
"sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382", "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2",
"sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678", "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7",
"sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b", "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296",
"sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e", "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690",
"sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741", "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67",
"sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4", "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57",
"sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596", "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597",
"sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9", "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846",
"sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69", "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b",
"sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c", "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97",
"sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77", "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c",
"sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13", "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62",
"sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459", "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa",
"sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e", "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f",
"sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7", "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e",
"sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908", "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821",
"sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a", "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3",
"sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f", "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4",
"sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8", "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb",
"sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482", "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727",
"sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d", "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514",
"sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d", "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d",
"sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545", "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761",
"sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34", "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55",
"sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86", "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f",
"sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6", "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c",
"sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe", "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034",
"sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e", "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6",
"sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc", "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae",
"sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7", "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1",
"sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd", "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14",
"sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c", "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1",
"sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557", "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228",
"sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a", "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708",
"sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89", "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48",
"sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078", "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f",
"sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e", "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5",
"sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4", "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f",
"sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403", "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4",
"sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0", "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8",
"sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89", "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff",
"sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115", "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61",
"sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9", "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b",
"sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05", "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97",
"sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a", "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b",
"sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec", "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605",
"sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56", "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728",
"sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38", "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d",
"sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479", "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c",
"sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c", "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf",
"sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e", "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673",
"sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd", "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1",
"sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186", "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b",
"sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455", "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41",
"sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c", "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8",
"sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65", "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f",
"sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78", "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4",
"sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287", "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008",
"sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df", "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9",
"sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43", "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5",
"sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1", "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f",
"sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7", "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e",
"sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989", "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273",
"sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a", "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45",
"sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63", "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e",
"sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884", "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656",
"sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649", "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e",
"sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810", "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c",
"sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828", "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2",
"sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4", "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72",
"sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2", "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056",
"sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd", "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397",
"sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5", "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42",
"sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe", "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd",
"sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293", "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3",
"sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e", "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213",
"sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e", "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf",
"sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8" "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"
], ],
"version": "==3.3.0" "markers": "python_full_version >= '3.7.0'",
"version": "==3.3.1"
}, },
"click": { "click": {
"hashes": [ "hashes": [
@ -315,41 +323,43 @@
"sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==8.1.7" "version": "==8.1.7"
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf",
"sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84",
"sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e",
"sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8",
"sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7",
"sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1",
"sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88",
"sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86",
"sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179",
"sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81",
"sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20",
"sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548",
"sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d",
"sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d",
"sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5",
"sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1",
"sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147",
"sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936",
"sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797",
"sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696",
"sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72",
"sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da",
"sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723"
], ],
"version": "==41.0.4" "version": "==41.0.5"
}, },
"deprecated": { "deprecated": {
"hashes": [ "hashes": [
"sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c",
"sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.14" "version": "==1.2.14"
}, },
"frozenlist": { "frozenlist": {
@ -416,6 +426,7 @@
"sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1", "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1",
"sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e" "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"
], ],
"markers": "python_version >= '3.8'",
"version": "==1.4.0" "version": "==1.4.0"
}, },
"fs": { "fs": {
@ -428,24 +439,27 @@
}, },
"gitdb": { "gitdb": {
"hashes": [ "hashes": [
"sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a", "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4",
"sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7" "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"
], ],
"version": "==4.0.10" "markers": "python_version >= '3.7'",
"version": "==4.0.11"
}, },
"gitpython": { "gitpython": {
"hashes": [ "hashes": [
"sha256:5f4c4187de49616d710a77e98ddf17b4782060a1788df441846bddefbb89ab33", "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4",
"sha256:f9b9ddc0761c125d5780eab2d64be4873fc6817c2899cbcb34b02344bdc7bc54" "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.37" "markers": "python_version >= '3.7'",
"version": "==3.1.40"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
], ],
"markers": "python_version >= '3.5'",
"version": "==3.4" "version": "==3.4"
}, },
"jsonschema": { "jsonschema": {
@ -454,6 +468,7 @@
"sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf" "sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==4.19.1" "version": "==4.19.1"
}, },
"jsonschema-specifications": { "jsonschema-specifications": {
@ -461,6 +476,7 @@
"sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1",
"sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb"
], ],
"markers": "python_version >= '3.8'",
"version": "==2023.7.1" "version": "==2023.7.1"
}, },
"multidict": { "multidict": {
@ -540,6 +556,7 @@
"sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d", "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d",
"sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba" "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"
], ],
"markers": "python_version >= '3.7'",
"version": "==6.0.4" "version": "==6.0.4"
}, },
"pycparser": { "pycparser": {
@ -555,13 +572,18 @@
"sha256:ecf12c2809c44147bce63b047b3d2e9dac8a41b63e90fcb263c703f64936b97c" "sha256:ecf12c2809c44147bce63b047b3d2e9dac8a41b63e90fcb263c703f64936b97c"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==2.1.1" "version": "==2.1.1"
}, },
"pyjwt": { "pyjwt": {
"extras": [
"crypto"
],
"hashes": [ "hashes": [
"sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de", "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de",
"sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320" "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"
], ],
"markers": "python_version >= '3.7'",
"version": "==2.8.0" "version": "==2.8.0"
}, },
"pynacl": { "pynacl": {
@ -577,6 +599,7 @@
"sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b",
"sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543" "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"
], ],
"markers": "python_version >= '3.6'",
"version": "==1.5.0" "version": "==1.5.0"
}, },
"python-dateutil": { "python-dateutil": {
@ -584,6 +607,7 @@
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.2" "version": "==2.8.2"
}, },
"referencing": { "referencing": {
@ -591,6 +615,7 @@
"sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf",
"sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0"
], ],
"markers": "python_version >= '3.8'",
"version": "==0.30.2" "version": "==0.30.2"
}, },
"requests": { "requests": {
@ -598,6 +623,7 @@
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
], ],
"markers": "python_version >= '3.7'",
"version": "==2.31.0" "version": "==2.31.0"
}, },
"rpds-py": { "rpds-py": {
@ -702,17 +728,27 @@
"sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d", "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d",
"sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281" "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281"
], ],
"markers": "python_version >= '3.8'",
"version": "==0.10.6" "version": "==0.10.6"
}, },
"rspec-tools": { "rspec-tools": {
"editable": true, "editable": true,
"path": "." "path": "."
}, },
"setuptools": {
"hashes": [
"sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87",
"sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"
],
"markers": "python_version >= '3.8'",
"version": "==68.2.2"
},
"six": { "six": {
"hashes": [ "hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0" "version": "==1.16.0"
}, },
"slackapi": { "slackapi": {
@ -728,6 +764,7 @@
"sha256:ab79fefb5412d0595bc01d2f195a787597f2a617b6766562932ab9ffbe5cb173" "sha256:ab79fefb5412d0595bc01d2f195a787597f2a617b6766562932ab9ffbe5cb173"
], ],
"index": "pypi", "index": "pypi",
"markers": "python_full_version >= '3.6.0'",
"version": "==2.9.4" "version": "==2.9.4"
}, },
"smmap": { "smmap": {
@ -735,6 +772,7 @@
"sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62",
"sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da" "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"
], ],
"markers": "python_version >= '3.7'",
"version": "==5.0.1" "version": "==5.0.1"
}, },
"soupsieve": { "soupsieve": {
@ -742,6 +780,7 @@
"sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690",
"sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"
], ],
"markers": "python_version >= '3.8'",
"version": "==2.5" "version": "==2.5"
}, },
"typing-extensions": { "typing-extensions": {
@ -749,14 +788,16 @@
"sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0",
"sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"
], ],
"markers": "python_version >= '3.8'",
"version": "==4.8.0" "version": "==4.8.0"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2", "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84",
"sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564" "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"
], ],
"version": "==2.0.6" "markers": "python_version >= '3.7'",
"version": "==2.0.7"
}, },
"wrapt": { "wrapt": {
"hashes": [ "hashes": [
@ -836,6 +877,7 @@
"sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559",
"sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.15.0" "version": "==1.15.0"
}, },
"yarl": { "yarl": {
@ -915,6 +957,7 @@
"sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78", "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78",
"sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7" "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"
], ],
"markers": "python_version >= '3.7'",
"version": "==1.9.2" "version": "==1.9.2"
} }
}, },
@ -932,46 +975,49 @@
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
], ],
"markers": "python_version >= '3.7'",
"version": "==2.0.0" "version": "==2.0.0"
}, },
"mypy": { "mypy": {
"hashes": [ "hashes": [
"sha256:091f53ff88cb093dcc33c29eee522c087a438df65eb92acd371161c1f4380ff0", "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7",
"sha256:1a69db3018b87b3e6e9dd28970f983ea6c933800c9edf8c503c3135b3274d5ad", "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e",
"sha256:24f3de8b9e7021cd794ad9dfbf2e9fe3f069ff5e28cb57af6f873ffec1cb0425", "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c",
"sha256:31eba8a7a71f0071f55227a8057468b8d2eb5bf578c8502c7f01abaec8141b2f", "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169",
"sha256:3c8835a07b8442da900db47ccfda76c92c69c3a575872a5b764332c4bacb5a0a", "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208",
"sha256:3df87094028e52766b0a59a3e46481bb98b27986ed6ded6a6cc35ecc75bb9182", "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0",
"sha256:49499cf1e464f533fc45be54d20a6351a312f96ae7892d8e9f1708140e27ce41", "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1",
"sha256:4c192445899c69f07874dabda7e931b0cc811ea055bf82c1ababf358b9b2a72c", "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1",
"sha256:4f3d27537abde1be6d5f2c96c29a454da333a2a271ae7d5bc7110e6d4b7beb3f", "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7",
"sha256:7469545380dddce5719e3656b80bdfbb217cfe8dbb1438532d6abc754b828fed", "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45",
"sha256:7807a2a61e636af9ca247ba8494031fb060a0a744b9fee7de3a54bed8a753323", "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143",
"sha256:856bad61ebc7d21dbc019b719e98303dc6256cec6dcc9ebb0b214b81d6901bd8", "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5",
"sha256:89513ddfda06b5c8ebd64f026d20a61ef264e89125dc82633f3c34eeb50e7d60", "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f",
"sha256:8e0db37ac4ebb2fee7702767dfc1b773c7365731c22787cb99f507285014fcaf", "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd",
"sha256:971104bcb180e4fed0d7bd85504c9036346ab44b7416c75dd93b5c8c6bb7e28f", "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245",
"sha256:9e1589ca150a51d9d00bb839bfeca2f7a04f32cd62fad87a847bc0818e15d7dc", "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f",
"sha256:9f8464ed410ada641c29f5de3e6716cbdd4f460b31cf755b2af52f2d5ea79ead", "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332",
"sha256:ab98b8f6fdf669711f3abe83a745f67f50e3cbaea3998b90e8608d2b459fd566", "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30",
"sha256:b19006055dde8a5425baa5f3b57a19fa79df621606540493e5e893500148c72f", "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183",
"sha256:c69051274762cccd13498b568ed2430f8d22baa4b179911ad0c1577d336ed849", "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f",
"sha256:d2dad072e01764823d4b2f06bc7365bb1d4b6c2f38c4d42fade3c8d45b0b4b67", "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85",
"sha256:dccd850a2e3863891871c9e16c54c742dba5470f5120ffed8152956e9e0a5e13", "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46",
"sha256:e28d7b221898c401494f3b77db3bac78a03ad0a0fff29a950317d87885c655d2", "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71",
"sha256:e4b7a99275a61aa22256bab5839c35fe8a6887781862471df82afb4b445daae6", "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660",
"sha256:eb7ff4007865833c470a601498ba30462b7374342580e2346bf7884557e40531", "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb",
"sha256:f8598307150b5722854f035d2e70a1ad9cc3c72d392c34fffd8c66d888c90f17", "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c",
"sha256:fea451a3125bf0bfe716e5d7ad4b92033c471e4b5b3e154c67525539d14dc15a" "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.6.0" "markers": "python_version >= '3.8'",
"version": "==1.6.1"
}, },
"mypy-extensions": { "mypy-extensions": {
"hashes": [ "hashes": [
"sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
"sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
], ],
"markers": "python_version >= '3.5'",
"version": "==1.0.0" "version": "==1.0.0"
}, },
"packaging": { "packaging": {
@ -979,6 +1025,7 @@
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
], ],
"markers": "python_version >= '3.7'",
"version": "==23.2" "version": "==23.2"
}, },
"pluggy": { "pluggy": {
@ -986,15 +1033,26 @@
"sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12",
"sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"
], ],
"markers": "python_version >= '3.8'",
"version": "==1.3.0" "version": "==1.3.0"
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002", "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac",
"sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069" "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"
], ],
"index": "pypi", "index": "pypi",
"version": "==7.4.2" "markers": "python_version >= '3.7'",
"version": "==7.4.3"
},
"pytest-snapshot": {
"hashes": [
"sha256:4b9fe1c21c868fe53a545e4e3184d36bc1c88946e3f5c1d9dd676962a9b3d4ab",
"sha256:c7013c3abc3e860f9feff899f8b4debe3708650d8d8242a61bf2625ff64db7f3"
],
"index": "pypi",
"markers": "python_version >= '3.5'",
"version": "==0.9.0"
}, },
"rspec-tools": { "rspec-tools": {
"editable": true, "editable": true,
@ -1013,6 +1071,7 @@
"sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0",
"sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"
], ],
"markers": "python_version >= '3.8'",
"version": "==4.8.0" "version": "==4.8.0"
} }
} }

View File

@ -27,7 +27,7 @@ Usage
.See all commands .See all commands
[source,sh] [source,sh]
---- ----
$ rspec-tools --help $ pipenv run rspec-tools --help
---- ----
@ -44,7 +44,7 @@ $ pipenv install --dev -e .
.Run tests .Run tests
[source,sh] [source,sh]
---- ----
$ pytest $ pipenv run pytest
---- ----
.Fixtures .Fixtures

View File

@ -18,6 +18,7 @@ from rspec_tools.validation.description import (validate_subsections,
validate_section_levels, validate_section_levels,
validate_section_names, validate_section_names,
validate_source_language) validate_source_language)
from rspec_tools.validation.sanitize_asciidoc import sanitize_asciidoc
from rspec_tools.validation.metadata import validate_rule_metadata from rspec_tools.validation.metadata import validate_rule_metadata
@ -87,6 +88,17 @@ def validate_rules_metadata(rules):
_fatal_error(f"Validation failed due to {error_counter} errors out of {len(rules)} analyzed rules") _fatal_error(f"Validation failed due to {error_counter} errors out of {len(rules)} analyzed rules")
@cli.command()
@click.argument('files', nargs=-1, required=True)
def check_asciidoc(files):
'''Sanitize the AsciiDoc description.'''
error_counter = 0
for file in files:
error_counter += sanitize_asciidoc(Path(file))
if error_counter > 0:
_fatal_error(f"Validation of the asciidoc description failed due to {error_counter} errors")
VALIDATORS = [validate_subsections, VALIDATORS = [validate_subsections,
validate_section_names, validate_section_names,
validate_section_levels, validate_section_levels,

View File

@ -0,0 +1,254 @@
""" Ensure the asciidoc code for a rule description follows best practices
Checks are:
* "ifdef"/"endif" blocks should be well-formed for RSPEC
* Inline code with backquotes is correctly escaped and balanced
* Include commands are not appended to other code
"""
from pathlib import Path
import re
VALID_IFDEF = "ifdef::env-github,rspecator-view[]"
VALID_ENDIF = "endif::env-github,rspecator-view[]"
VARIABLE_DECL = re.compile(r':\w+: ')
INCLUDE = re.compile(r'include::')
FORMATTING_CHARS = ['_', r'\*', r'\#']
WORD_FORMATTING_CHARS = [r'\~', r'\^']
# If the formatting char is repeated twice, it can go anywhere
UNCONSTRAINED_FORMATTING = '|'.join(x + x for x in FORMATTING_CHARS)
# Single formatting char are dangerous at the beginning of a word
FORMATTING_OPENING = '|'.join(r'(\W|^)' + x + r'\w' for x in FORMATTING_CHARS)
# Single formatting char are dangerous at the end of a word
FORMATTING_CLOSING = '|'.join(r'\w' + x + r'(\W|$)' for x in FORMATTING_CHARS)
# Word formatting is broken by spaces so we look for things like `#word#`
WORD_FORMATTING = "|".join(x + r'\S+' + x for x in WORD_FORMATTING_CHARS)
# We combine all the matchers
NEED_PROTECTION = re.compile('('
f'{UNCONSTRAINED_FORMATTING}|'
f'{FORMATTING_OPENING}|'
f'{FORMATTING_CLOSING}|'
f'{WORD_FORMATTING}'
')')
# There is a regex trick here:
# We want to stop the search if there is a backquote
# We do that by matching backquote OR the closing passthrough
# Then we'll ignore any match of backquote
CLOSE_CONSTRAINED_PASSTHROUGH = re.compile(r'`|((?<!\s)\+(?=`))')
CLOSE_CONSTRAINED_BACKQUOTE = re.compile(r'`(?!\w)')
CLOSE_UNCONSTRAINED_BACKQUOTE = re.compile('``')
PASSTHROUGH_MACRO_TEXT = r'pass:\w*\[(\\\]|[^\]])*\]'
PASSTHROUGH_MACRO = re.compile(PASSTHROUGH_MACRO_TEXT)
# There is a regex trick here:
# We want to skip passthrough macros, to not find pass:[``whatever``]
# We do that by matching
# * EITHER passthrough macros including their ignored backquotes
# * OR backquotes
# Then we'll ignore any match of PASSTHROUGH_MACRO
BACKQUOTE = re.compile(PASSTHROUGH_MACRO_TEXT + r'|(?P<backquote>(``+)|(?<![\\\w])(`)(?!\s))')
def close_passthrough(count, pos, line):
"""Find the end of a passthrough block marked by *count* plus signs"""
while count > 0:
# `+++a++` will display '+a' in case of inbalance, we try to find the biggest closing block
if count == 1:
if not line[pos + count].isspace() and line[pos - 1] == '`':
#constrained '+'. It is a passthrough only if it is directly around text and surrounded by backquotes: `+Some Content+`
close_pattern = CLOSE_CONSTRAINED_PASSTHROUGH
else:
return pos
else:
close_pattern = re.compile('(' + r'\+' * count + ')')
end = close_pattern.search(line, pos + count)
if end and end.group(1):
return end.end()
count -= 1
return pos
def skip_passthrough_macro(line, pos):
'''If this is a passthrough macro, skip to the end'''
if line[pos] == 'p':
pm = PASSTHROUGH_MACRO.match(line, pos)
if pm:
return pm.end()
return pos
def skip_passthrough_plus(line, pos):
'''If this is a passthrough +, skip to the end'''
if line[pos] == '+':
count = 1
while pos + count < len(line) and line[pos + count] == '+':
count += 1
return close_passthrough(count, pos, line)
return pos
def close_inline_block(line: str, pos: int, closing_pattern: re.Pattern[str]):
"""Find the end of an inline block started with *pattern*"""
content = ""
while pos < len(line):
pos = skip_passthrough_macro(line, pos)
pos = skip_passthrough_plus(line, pos)
if closing_pattern.match(line, pos):
return pos, content
content += line[pos]
pos += 1
return -1, content
class Sanitizer:
def __init__(self, file: Path):
assert file.exists()
assert file.is_file()
self._file = file
self._is_env_open = False
self._has_env = False
self._error_count = 0
self._is_inside_code = False
self._empty_line = True
self._previous_line_was_include = False
def process(self) -> bool:
content = self._file.read_text(encoding="utf-8")
lines = content.splitlines(keepends=False)
for line_index, line in enumerate(lines):
if self._is_inside_code:
if line == '----':
self._is_inside_code = False
continue
if line == '----':
self._is_inside_code = True
continue
line_number = line_index + 1
if line.startswith("ifdef::"):
self._process_open_ifdef(line_number, line)
elif line.startswith("endif::"):
self._process_close_ifdef(line_number, line)
elif not line.strip():
self._empty_line = True
else:
self._process_description(line_number, line)
self._empty_line = False
if self._is_env_open:
self._on_error(len(lines), "An ifdef command is opened but never closed.")
return self._error_count
def _process_open_ifdef(self, line_number: int, line: str):
if self._has_env:
message = "Only one ifdef command is allowed per file."
if self._is_env_open:
message += "\nThe previous ifdef command was not closed."
self._on_error(line_number, message)
self._has_env = True
self._is_env_open = True
# IDEs should be configured to properly display the description,
# not the other way around.
# "env-vscode" was used in the passed. Instead, user should be able to
# toggle the rspecator view based on their needs. Help these users migrate.
if "vscode" in line:
self._on_error(
line_number,
"Configure VS Code to display rspecator-view by setting the asciidoctor attribute.",
)
elif line != VALID_IFDEF:
self._on_error(
line_number,
f'Incorrect asciidoc environment. "{VALID_IFDEF}" should be used instead.',
)
def _process_close_ifdef(self, line_number: int, line: str):
if not self._is_env_open:
self._on_error(line_number, "Unexpected endif command.")
self._is_env_open = False
if line != VALID_ENDIF:
self._on_error(
line_number,
f'Incorrect endif command. "{VALID_ENDIF}" should be used instead.',
)
def _process_description(self, line_number: int, line: str):
if VARIABLE_DECL.match(line):
return
if self._previous_line_was_include and not self._empty_line:
self._on_error(line_number - 1, '''An empty line is missing after the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include''')
if INCLUDE.match(line):
self._previous_line_was_include = True
if not self._empty_line:
self._on_error(line_number, '''An empty line is missing before the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include''')
return
else:
self._previous_line_was_include = False
pos = 0
res = BACKQUOTE.search(line, pos)
# We filter out matches for passthrough. See comment near the BACKQUOTE declaration
while res and res.group('backquote'):
pos = self._check_inlined_code(line_number, res.end(), line, res.group('backquote'))
res = BACKQUOTE.search(line, pos)
def _check_inlined_code(self, line_number: int, pos: int, line: str, opening_pattern: str):
if len(opening_pattern) > 2:
# Part of the backquotes are displayed as backquotes.
self._on_error(line_number, 'Use "++" to isolate the backquotes you want to display from the ones that should be interpreted by AsciiDoc.')
return pos
elif len(opening_pattern) == 2:
closing_pattern = CLOSE_UNCONSTRAINED_BACKQUOTE
else:
closing_pattern = CLOSE_CONSTRAINED_BACKQUOTE
content_end, content = close_inline_block(line, pos, closing_pattern)
if content_end < 0:
message='Unbalanced code inlining tags.'
if len(opening_pattern) == 1:
message += '''
If you are trying to write inline code that is glued to text without a space,
you need to use double backquotes:
> Replace all `reference`s.
Will not display correctly. You need to write:
> Replace all ``reference``s.
'''
self._on_error(line_number, message)
return len(line)
pos = content_end + len(opening_pattern)
if NEED_PROTECTION.search(content):
self._on_error (line_number, f'''
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++{content}++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[{content}]``
''')
return pos
return pos
def _on_error(self, line_number: int, message: str):
print(f"{self._file}:{line_number} {message}")
self._error_count += 1
def sanitize_asciidoc(file_path: Path):
"""Called by the CLI"""
return Sanitizer(file_path).process()

View File

@ -20,6 +20,17 @@ def mockinvalidrules():
return Path(__file__).parent.joinpath('resources', 'invalid-rules') return Path(__file__).parent.joinpath('resources', 'invalid-rules')
@pytest.fixture
def mockinvalidasciidoc():
'''Provides a path to test asciidoc resources.'''
return Path(__file__).parent.joinpath('resources', 'invalid-asciidoc')
@pytest.fixture
def mockasciidoc():
'''Provides a path to test asciidoc resources.'''
return Path(__file__).parent.joinpath('resources', 'asciidoc')
@pytest.fixture @pytest.fixture
def git_config(): def git_config():
'''Create a mock git configuration.''' '''Create a mock git configuration.'''

View File

@ -0,0 +1,35 @@
This is a file with `valid inlined code`.
It's using ``unconstrained tag``s.
It has ``++**protected tags**++``
It escapes `++`++` backquotes
it can use `\` or `+`
Plays well with beginning and end of lines:
`++`++`
Mix of passthrough and plusses `+{plus}##+{plus}+`
:var: ``Variables are not interpreted`
{var}
We can use passthrough macros `pass:[__like that__]`
Or we can use passthrough macros `as a pass:[__part__] of the code`.
The pass:[can also be outside `__the backquotes__`]
The pass:[``++Can have __ [escaped brackets\] __ ++``]
[source,python]
----
# We don't care about `in the code
----
We can have a sole ` surrounded by spaces
This file does not exist but we only check that the include is well placed:
include::everything_ok.adoc[]
For `constrained*format` we don't raise an issue unless `+*it's+ at the beginning or end of a +~word~+`.

View File

@ -0,0 +1,5 @@
Some description
Now we try to close and ifdef that was never opened.
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,4 @@
# Header
include::somefile.adoc[]
* content just after

View File

@ -0,0 +1,2 @@
# Header
include::somefile.adoc[]

View File

@ -0,0 +1 @@
$PATH/close_unopened_ifdef.adoc:5 Unexpected endif command.

View File

@ -0,0 +1,3 @@
$PATH/include_stuck_after.adoc:3 An empty line is missing after the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include

View File

@ -0,0 +1,3 @@
$PATH/include_stuck_before.adoc:2 An empty line is missing before the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include

View File

@ -0,0 +1 @@
$PATH/triple_backquotes.adoc:1 Use "++" to isolate the backquotes you want to display from the ones that should be interpreted by AsciiDoc.

View File

@ -0,0 +1 @@
$PATH/two_ifdef.adoc:9 Only one ifdef command is allowed per file.

View File

@ -0,0 +1,2 @@
$PATH/two_ifdef_unclosed.adoc:7 Only one ifdef command is allowed per file.
The previous ifdef command was not closed.

View File

@ -0,0 +1,6 @@
$PATH/two_stuck_includes.adoc:3 An empty line is missing after the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include
$PATH/two_stuck_includes.adoc:4 An empty line is missing before the include.
This may result in broken tags and other display issues.
Make sure there are always empty lines before and after each include

View File

@ -0,0 +1 @@
$PATH/unbalanced_double_backquotes.adoc:2 Unbalanced code inlining tags.

View File

@ -0,0 +1,7 @@
$PATH/unbalanced_single_backquotes.adoc:1 Unbalanced code inlining tags.
If you are trying to write inline code that is glued to text without a space,
you need to use double backquotes:
> Replace all `reference`s.
Will not display correctly. You need to write:
> Replace all ``reference``s.

View File

@ -0,0 +1 @@
$PATH/unclosed_ifdef.adoc:6 An ifdef command is opened but never closed.

View File

@ -0,0 +1,32 @@
$PATH/unprotected_formatting.adoc:1
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++*careful++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[*careful]``
$PATH/unprotected_formatting.adoc:3
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++unpro**tected++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[unpro**tected]``
$PATH/unprotected_formatting.adoc:5
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++~flags~++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[~flags~]``
$PATH/unprotected_formatting.adoc:7
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++_problems_++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[_problems_]``

View File

@ -0,0 +1,8 @@
$PATH/unprotected_formatting_with_plusses.adoc:1
Using backquotes does not protect against asciidoc interpretation. Starting or
ending a word with '*', '#', '_' or having two of them consecutively will
trigger unintended behavior with the rest of the text.
Use ``++This is not + ##protected## +++`` to avoid that.
If you really want to have formatting inside your code, you can write
``pass:n[This is not + ##protected## +]``

View File

@ -0,0 +1,2 @@
$PATH/vscode_ifdef.adoc:3 Configure VS Code to display rspecator-view by setting the asciidoctor attribute.
$PATH/vscode_ifdef.adoc:7 Incorrect endif command. "endif::env-github,rspecator-view[]" should be used instead.

View File

@ -0,0 +1,7 @@
$PATH/wrong_constrained_passthrough.adoc:1 Unbalanced code inlining tags.
If you are trying to write inline code that is glued to text without a space,
you need to use double backquotes:
> Replace all `reference`s.
Will not display correctly. You need to write:
> Replace all ``reference``s.

View File

@ -0,0 +1 @@
$PATH/wrong_endif.adoc:7 Incorrect endif command. "endif::env-github,rspecator-view[]" should be used instead.

View File

@ -0,0 +1 @@
$PATH/wrong_ifdef.adoc:3 Incorrect asciidoc environment. "ifdef::env-github,rspecator-view[]" should be used instead.

View File

@ -0,0 +1,3 @@
We don't like people using three backquotes to display one in monospace ```
Instead they should write `++`++`

View File

@ -0,0 +1,13 @@
Some description
ifdef::env-github,rspecator-view[]
A first ifdef block
endif::env-github,rspecator-view[]
ifdef::env-github,rspecator-view[]
This second ifdef should not be there
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,11 @@
Some description
ifdef::env-github,rspecator-view[]
A first ifdef block
ifdef::env-github,rspecator-view[]
This second ifdef should not be there
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,6 @@
# Header
include::somefile.adoc[]
include::someotherfile.adoc[]
* Other content

View File

@ -0,0 +1,4 @@
This is a test for double
backquotes``next to text
We shouldn't raise on this``double backquote usage``.

View File

@ -0,0 +1,3 @@
Some text with `unbalanced`backquotes
Some other text where ` is used correctly. Shouldn't rais
`inlined code with single quotes`

View File

@ -0,0 +1,6 @@
Some content followed by an ifdef
ifdef::env-github,rspecator-view[]
This ifdef should be valid
But then we don't close the block

View File

@ -0,0 +1,7 @@
we should be `*careful`
of `unpro**tected`
formatting `~flags~`
They can create `_problems_`.

View File

@ -0,0 +1 @@
Some content and `This is not + ##protected## +`

View File

@ -0,0 +1,7 @@
Some content followed by an ifdef
ifdef::env-github,rspecator-view,env-vscode[]
This ifdef is wrong because we add vscode flags in it
endif::env-github,rspecator-view,env-vscode[]

View File

@ -0,0 +1 @@
Constrained pass-through are broken by backquotes: `+`+`

View File

@ -0,0 +1,7 @@
Some content followed by an ifdef
ifdef::env-github,rspecator-view[]
This ifdef is wrong because we don't close it with the expected values
endif::test,rspecator-view[]

View File

@ -0,0 +1,7 @@
Some content followed by an ifdef
ifdef::mytest,rspecator-view[]
This ifdef is wrong because we don't open it with the expected values
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,43 @@
import re
from pathlib import Path
import pytest
from rspec_tools.validation.sanitize_asciidoc import sanitize_asciidoc
def relative_output(capsys, path: Path):
return capsys.readouterr().out.replace(str(path), '$PATH')
@pytest.mark.parametrize('invalid_file,expected_count', [('unbalanced_single_backquotes', 1),
('unbalanced_double_backquotes', 1),
('triple_backquotes', 1),
('unprotected_formatting', 4),
('unprotected_formatting_with_plusses', 1),
('wrong_constrained_passthrough', 1),
('unclosed_ifdef', 1),
('close_unopened_ifdef', 1),
('two_ifdef', 1),
('two_ifdef_unclosed', 1),
('vscode_ifdef', 2),
('wrong_ifdef', 1),
('wrong_endif', 1),
('include_stuck_before', 1),
('include_stuck_after', 1),
('two_stuck_includes', 2)
])
def test_need_sanitation(mockinvalidasciidoc: Path, invalid_file, expected_count, capsys, snapshot):
'''Check that we detect needs for sanitation.'''
name_path = Path(invalid_file)
adoc = mockinvalidasciidoc / name_path.with_suffix('.adoc')
expected = mockinvalidasciidoc / 'snapshots' / name_path.with_suffix('.txt')
assert sanitize_asciidoc(adoc) == expected_count
snapshot.snapshot_dir = mockinvalidasciidoc / 'snapshots'
snapshot.assert_match(relative_output(capsys, mockinvalidasciidoc), expected)
def test_correctly_sanitized(mockasciidoc: Path):
'''Check that we raise no issue on correctly sanitized asciidoc'''
name_path = Path('valid')
adoc = mockasciidoc / name_path.with_suffix('.adoc')
assert sanitize_asciidoc(adoc) == 0

View File

@ -15,7 +15,7 @@ When throwing an exception, it is therefore recommended to throw the most specif
To fix this issue, make sure to throw specific exceptions that are relevant to the context in which they arise. It is recommended to either: To fix this issue, make sure to throw specific exceptions that are relevant to the context in which they arise. It is recommended to either:
* Throw a subtype of `Exception` that already exists in the Standard PHP Library. For instance ``++InvalidArgumentException++`` could be raised when an unexpected argument is provided to a function. * Throw a subtype of `Exception` that already exists in the Standard PHP Library. For instance ``++InvalidArgumentException++`` could be raised when an unexpected argument is provided to a function.
* Define a custom exception type that derives from `++Exception++`` or one of its subclasses. * Define a custom exception type that derives from ``++Exception++`` or one of its subclasses.
=== Code examples === Code examples

View File

@ -17,8 +17,8 @@ global using System.Net.Sockets; // Compliant by exception
Unnecessary `using` directives are ignored in ASP.NET Core projects in the following files: Unnecessary `using` directives are ignored in ASP.NET Core projects in the following files:
* `_Imports.razor` * ``++_Imports.razor++``
* `_ViewImports.cshtml` * ``++_ViewImports.cshtml++``
== How to fix it == How to fix it

View File

@ -127,7 +127,7 @@ include::../message.adoc[]
(visible only on this page) (visible only on this page)
=== on 3 Mar 2020, 17:27:45 Nicolas Harraudeau wrote: === on 3 Mar 2020, 17:27:45 Nicolas Harraudeau wrote:
Note: There is no need to create issues for \``++raise++`` statements because: Note: There is no need to create issues for ``++raise++`` statements because:
* there might be a good reason to raise an exception (Ex: cannot release resource) * there might be a good reason to raise an exception (Ex: cannot release resource)
* python will link the new exception and the old exception automatically: * python will link the new exception and the old exception automatically:

View File

@ -37,13 +37,13 @@ For the parameters of the rule, the following rules are applied:
* `?` matches a single character * `?` matches a single character
* `*` matches zero or more characters * `*` matches zero or more characters
* `**` matches zero or more packages * ``++**++`` matches zero or more packages
Examples: Examples:
* `java.internal.InternalClass` will match only `InternalClass` class. * `java.internal.InternalClass` will match only `InternalClass` class.
* `java.internal.*` will match any member of `java.internal` package. * ``++java.internal.*++`` will match any member of `java.internal` package.
* `java.internal.**` same as above, but including sub-packages. * ``++java.internal.**++`` same as above, but including sub-packages.
=== Code examples === Code examples

View File

@ -4,7 +4,7 @@ include::../description.adoc[]
=== Exceptions === Exceptions
To prevent generating some false-positives, literals having less than 10 characters are excluded as well as literals matching `/^\w*$/`. To prevent generating some false-positives, literals having less than 10 characters are excluded as well as literals matching ``++/^\w*$/++``.
String literals inside import/export statements and JSX attributes are also ignored. String literals inside import/export statements and JSX attributes are also ignored.
The same goes for statement-like string literals, e.g. `'use strict';`. The same goes for statement-like string literals, e.g. `'use strict';`.

View File

@ -11,7 +11,7 @@ Specifically, deallocation should correspond to allocation as per the table belo
|Allocation | Deallocation |Allocation | Deallocation
|`p = new T();` | `delete p;` |`p = new T();` | `delete p;`
|`+p = new T[5];+` | `+delete[] p;+` |`+p = new T[5];+` | `+delete[] p;+`
|`p = malloc(sizeof(int)*5);` | `free(p);` |``++p = malloc(sizeof(int)*5);++`` | `free(p);`
|============================================ |============================================
=== What is the potential impact? === What is the potential impact?
@ -62,7 +62,7 @@ delete[] p; // Noncompliant: expect array cookie
If you need allocate memory in a custom `T::operator new(std::size_t)`, If you need allocate memory in a custom `T::operator new(std::size_t)`,
you should use `void* ::operator new(std::size_t)` and not `free()`. you should use ``++void* ::operator new(std::size_t)++`` and not `free()`.
Note that `::operator new` is still not compatible with `free()`: Note that `::operator new` is still not compatible with `free()`:

View File

@ -2,9 +2,9 @@
A ``++switch++`` statement is a control flow statement that allows you to execute different blocks of code based on the value of an expression. It provides a more concise way to handle multiple conditions compared to using multiple ``++if-else++`` statements. A ``++switch++`` statement is a control flow statement that allows you to execute different blocks of code based on the value of an expression. It provides a more concise way to handle multiple conditions compared to using multiple ``++if-else++`` statements.
If you only have a single condition to check, using an ``++if++``` statement is simpler and more concise. ``++switch++`` statements are designed for handling multiple cases, so using them for a single condition can be overkill and less readable. If you only have a single condition to check, using an ``++if++`` statement is simpler and more concise. ``++switch++`` statements are designed for handling multiple cases, so using them for a single condition can be overkill and less readable.
This rule raises an issue when a ``++switch++`` statement has only one ``++case++`` clause and possibly a ``++default`` one. This rule raises an issue when a ``++switch++`` statement has only one ``++case++`` clause and possibly a ``++default++`` one.
[source,javascript,diff-id=1,diff-type=noncompliant] [source,javascript,diff-id=1,diff-type=noncompliant]
---- ----

View File

@ -1,4 +1,4 @@
This rule raises an issue when the `Collections.EMPTY_*` fields are used instead of the `Collections.empty*()` methods. This rule raises an issue when the ``++Collections.EMPTY_*++`` fields are used instead of the ``++Collections.empty*()++`` methods.
== Why is this an issue? == Why is this an issue?
@ -23,7 +23,7 @@ Use:
- `Collections.emptySet()` instead of `Collections.EMPTY_SET` - `Collections.emptySet()` instead of `Collections.EMPTY_SET`
- `Collections.emptyMap()` instead of `Collections.EMPTY_MAP` - `Collections.emptyMap()` instead of `Collections.EMPTY_MAP`
In addition, there are variants of `Collections.empty*()` available also for other collection interfaces, In addition, there are variants of ``++Collections.empty*()++`` available also for other collection interfaces,
such as `Collections.emptyIterator()`, `Collections.emptyNavigableMap()`, `Collections.emptySortedSet()`. such as `Collections.emptyIterator()`, `Collections.emptyNavigableMap()`, `Collections.emptySortedSet()`.
=== Code examples === Code examples

View File

@ -3,8 +3,8 @@
Using a function in PHP with the same name as the nesting class was historically used to declare a class constructor. Using a function in PHP with the same name as the nesting class was historically used to declare a class constructor.
However, as of PHP 8.0.0, this declaration is discouraged and will provoke an `E_DEPRECATED` error, albeit it functions as a constructor. However, as of PHP 8.0.0, this declaration is discouraged and will provoke an `E_DEPRECATED` error, albeit it functions as a constructor.
Instead, users should explicitly define the constructor by declaring a `\__construct(...)` function. Instead, users should explicitly define the constructor by declaring a ``++__construct(...)++`` function.
However, if both styles are present in the same class, PHP will treat the `__construct` function as the class constructor, which can cause unintended behavior. However, if both styles are present in the same class, PHP will treat the ``++__construct++`` function as the class constructor, which can cause unintended behavior.
Adhering to this convention improves readability and maintainability by ensuring that the constructor declaration is named uniformly throughout the codebase. Adhering to this convention improves readability and maintainability by ensuring that the constructor declaration is named uniformly throughout the codebase.

View File

@ -33,7 +33,7 @@ When constructing an object of type C, the following occurs:
* The sub-object of type `A` is constructed. * The sub-object of type `A` is constructed.
* The sub-object of type `B` is constructed. * The sub-object of type `B` is constructed.
** The constructor `B::B()` is called, during this call, `*this` is considered as being of type `B`. ** The constructor ``++B::B()++`` is called, during this call, ``++*this++`` is considered as being of type `B`.
** The function `B::f()` is called. ** The function `B::f()` is called.
** The function `A::g()` is called. ** The function `A::g()` is called.
* The object of type `C` is constructed. * The object of type `C` is constructed.

View File

@ -30,7 +30,7 @@ for word in words:
Python will raise a `SyntaxError` when `break` or `continue` are used outside of `for` or `while` loops. Python will raise a `SyntaxError` when `break` or `continue` are used outside of `for` or `while` loops.
If the goal is to interrupt the main program flow, `quit()`, `exit()`, `os._exit()` and `sys.exit()` are the preferred way. If the goal is to interrupt the main program flow, `quit()`, `exit()`, ``++os._exit()++`` and `sys.exit()` are the preferred way.
=== Code examples === Code examples

View File

@ -7,7 +7,7 @@ Using the same value on both sides of a binary operator is a code defect. In the
The following are ignored: The following are ignored:
* The expression `1 << 1` * The expression `1 << 1`
* When an increment or decrement operator is used, ex: ``*p{plus}{plus} == *p{plus}{plus}`` * When an increment or decrement operator is used, ex: ``+++*p++ == *p+++++``
* Bitwise operators `|, &, ^` * Bitwise operators `|, &, ^`
* Arithmetic operators `+, *` * Arithmetic operators `+, *`
* Assignment operators `=, +=, *=` * Assignment operators `=, +=, *=`

View File

@ -4,9 +4,9 @@
* It is not part of the standard, which prevents its use in some contexts. * It is not part of the standard, which prevents its use in some contexts.
* Even if it is supported by virtually all compilers, since its behavior is not defined, it may differ between compilers, especially for some corner cases when determining if two files are identical (for instance, in the presence of symbolic links). * Even if it is supported by virtually all compilers, since its behavior is not defined, it may differ between compilers, especially for some corner cases when determining if two files are identical (for instance, in the presence of symbolic links).
* Its semantic is slightly different from the semantic of an include guard. For instance, if a file is duplicated in two different locations, `#pragma once` will not prevent multiple inclusion of this file. * Its semantic is slightly different from the semantic of an include guard. For instance, if a file is duplicated in two different locations, ``++#pragma once++`` will not prevent multiple inclusion of this file.
Note: There used to be a build performance improvement when using `#pragma once` instead of an include guard because naive implementations of include guards need to parse the full file to get the `#endif` matching the `#if`. But most modern compilers specifically detect the include guard pattern and use a dedicated optimization that makes it about as fast as `#pragma once`. Note: There used to be a build performance improvement when using ``++#pragma once++`` instead of an include guard because naive implementations of include guards need to parse the full file to get the ``++#endif++`` matching the ``++#if++``. But most modern compilers specifically detect the include guard pattern and use a dedicated optimization that makes it about as fast as ``++#pragma once++``.
=== Noncompliant code example === Noncompliant code example

View File

@ -5,7 +5,7 @@ include::../why.adoc[]
The rule ignores The rule ignores
* Initializations to `-1`, `0`, `1`, `undefined`, `[]`, `{}`, `true`, `false` and `""`. * Initializations to `-1`, `0`, `1`, `undefined`, `[]`, `{}`, `true`, `false` and `""`.
* Variables that start with an underscore (e.g. \'`_unused`') are ignored. * Variables that start with an underscore (e.g. ``++_unused++``) are ignored.
* Assignment of `null` is ignored because it is sometimes used to help garbage collection * Assignment of `null` is ignored because it is sometimes used to help garbage collection
* Increment and decrement expressions are ignored because they are often used idiomatically instead of `x+1` * Increment and decrement expressions are ignored because they are often used idiomatically instead of `x+1`
* This rule also ignores variables declared with object destructuring using rest syntax (used to exclude some properties from object) * This rule also ignores variables declared with object destructuring using rest syntax (used to exclude some properties from object)

View File

@ -39,7 +39,7 @@ Within the JDK, types which should not be used for synchronization include:
* `String` literals * `String` literals
* Primitive wrapper classes in `java.lang` (such as `Boolean` with `Boolean.FALSE` and `Boolean.TRUE`) * Primitive wrapper classes in `java.lang` (such as `Boolean` with `Boolean.FALSE` and `Boolean.TRUE`)
* The class `java.lang.Runtime.Version` * The class `java.lang.Runtime.Version`
* The `Optional*` classes in `java.util`: `Optional`, `OptionalInt`, `OptionalLong`, and `OptionalDouble` * The ``++Optional*++`` classes in `java.util`: `Optional`, `OptionalInt`, `OptionalLong`, and `OptionalDouble`
* Various classes in the `java.time` API: `Instant`, `LocalDate`, `LocalTime`, `LocalDateTime`, `ZonedDateTime`, `ZoneId`, `OffsetTime`, `OffsetDateTime`, `ZoneOffset`, `Duration`, `Period`, `Year`, `YearMonth`, and `MonthDay` * Various classes in the `java.time` API: `Instant`, `LocalDate`, `LocalTime`, `LocalDateTime`, `ZonedDateTime`, `ZoneId`, `OffsetTime`, `OffsetDateTime`, `ZoneOffset`, `Duration`, `Period`, `Year`, `YearMonth`, and `MonthDay`
* Various classes in the `java.time.chrono` API: `MinguoDate`, `HijrahDate`, `JapaneseDate`, and `ThaiBuddhistDate` * Various classes in the `java.time.chrono` API: `MinguoDate`, `HijrahDate`, `JapaneseDate`, and `ThaiBuddhistDate`
* The interface `java.lang.ProcessHandle` and its implementation classes * The interface `java.lang.ProcessHandle` and its implementation classes

View File

@ -5,7 +5,7 @@ Both ``++if-else++`` chains and ``++switch++`` statements are used for condition
* In an ``++if-else++`` chain, each condition is checked in order, and only the block associated with the first true condition is executed. If no condition is true, the code inside the ``++else++`` block (if present) will be executed. * In an ``++if-else++`` chain, each condition is checked in order, and only the block associated with the first true condition is executed. If no condition is true, the code inside the ``++else++`` block (if present) will be executed.
* In a ``++switch++`` statement, the expression is evaluated once, and its value is compared against each case. If a matching case is found, the corresponding block of code is executed. The ``++break++`` statement is used to exit the ``++switch++`` block after a match. If no case matches the expression, the code inside the ``++default++`` block (if present) will be executed. * In a ``++switch++`` statement, the expression is evaluated once, and its value is compared against each case. If a matching case is found, the corresponding block of code is executed. The ``++break++`` statement is used to exit the ``++switch++`` block after a match. If no case matches the expression, the code inside the ``++default++`` block (if present) will be executed.
Having the same condition in both ``++if-else++`` chains and `++switch++`` cases can lead to unreachable code and a potential source of bugs. It defeats the purpose of conditional branching and can make the code harder to read and maintain. Having the same condition in both ``++if-else++`` chains and ``++switch++`` cases can lead to unreachable code and a potential source of bugs. It defeats the purpose of conditional branching and can make the code harder to read and maintain.
[source,javascript,diff-id=1,diff-type=noncompliant] [source,javascript,diff-id=1,diff-type=noncompliant]
---- ----

View File

@ -1,4 +1,4 @@
Casting expressions are utilized to convert one data type to another, such as transforming an integer into a string. This is especially crucial in strongly typed languages like `C`, `C++`, `C#`, `Java`, `Python`, and others. Casting expressions are utilized to convert one data type to another, such as transforming an integer into a string. This is especially crucial in strongly typed languages like C, {cpp}, C#, Java, Python, and others.
However, there are instances where casting expressions are not needed. These include situations like: However, there are instances where casting expressions are not needed. These include situations like:

View File

@ -8,11 +8,11 @@ However, the way they perform that task differs, and they should not be used int
* ``++include++`` also includes a file, but generates only a warning if an error occurs. * ``++include++`` also includes a file, but generates only a warning if an error occurs.
Predictably, the difference between ``++require++`` and ``++require_once++`` is the same as the difference between ``++include++`` and ``++include_once++``. Predictably, the difference between ``++require++`` and ``++require_once++`` is the same as the difference between ``++include++`` and ``++include_once++``.
The `_once` versions ensure that the specified file is only included once. The ``++_once++`` versions ensure that the specified file is only included once.
=== What is the potential impact? === What is the potential impact?
Including the same file multiple times could have unpredictable results, the `_once` versions are preferred. Including the same file multiple times could have unpredictable results, the ``++_once++`` versions are preferred.
Additionally, as ``++include_once++`` generates only warnings, it should be used only when the file is being included conditionally, i.e. when all possible error conditions have been checked beforehand. Additionally, as ``++include_once++`` generates only warnings, it should be used only when the file is being included conditionally, i.e. when all possible error conditions have been checked beforehand.
== How to fix it == How to fix it

View File

@ -12,10 +12,11 @@ The library helps you to get rid of common dangerous characters, such as:
* `$` * `$`
* `>` * `>`
* `<` * `<`
* `\`` * `++`++`
* `\\` * `++\++`
* `!` * `!`
If user input is to be included in the arguments of a command, the application If user input is to be included in the arguments of a command, the application
must ensure that dangerous options or argument delimiters are neutralized. + must ensure that dangerous options or argument delimiters are neutralized. +
Argument delimiters count `'`, `-` and spaces. Argument delimiters count `'`, `-` and spaces.

View File

@ -10,8 +10,7 @@ The ++arguments++ object has two deprecated properties called ``++arguments.call
Both ``++arguments.caller++`` and ``++arguments.callee++`` are non-standard, deprecated, and leak stack information, which poses security risks and severely limits the possibility of optimizations. Both ``++arguments.caller++`` and ``++arguments.callee++`` are non-standard, deprecated, and leak stack information, which poses security risks and severely limits the possibility of optimizations.
Accessing ``arguments.callee``, ``Function.prototype.caller`` and ``Function.prototype.arguments Accessing ``arguments.callee``, ``Function.prototype.caller`` and ``Function.prototype.arguments`` in strict mode will throw a ``TypeError``.
`` in strict mode will throw a ``TypeError``.
[source,javascript] [source,javascript]
---- ----

View File

@ -58,9 +58,9 @@ ifdef::env-github,rspecator-view[]
=== Message === Message
Remove backticks (`) from "xxx". Remove backticks ``++`++`` from "xxx".
Remove backticks (`) from "xxx" and rename it. Remove backticks ``++`++`` from "xxx" and rename it.
''' '''

View File

@ -21,7 +21,7 @@ public async void button1_Click(object sender, EventArgs e)
await DoSomethingAsync(); await DoSomethingAsync();
} }
---- ----
* Methods name matching `On[A-Z]\w*` pattern. * Methods name matching ``++On[A-Z]\w*++`` pattern.
+ +
Some frameworks may not use the same `EventHandler` method signature Some frameworks may not use the same `EventHandler` method signature
+ +

View File

@ -1,10 +1,10 @@
== Why is this an issue? == Why is this an issue?
For better organization and clarity in test suites, test classes should end with `*Test.php`. For better organization and clarity in test suites, test classes should end with ``++*Test.php++``.
This naming convention helps to easily identify and distinguish test classes from other classes in the codebase. This naming convention helps to easily identify and distinguish test classes from other classes in the codebase.
It allows for automated test runners or frameworks to locate and execute the tests systematically. It allows for automated test runners or frameworks to locate and execute the tests systematically.
The PHPUnit command-line test runner will look for `*Test.php` files. The PHPUnit command-line test runner will look for ``++*Test.php++`` files.
In that case, test files without this pattern are ignored and not executed without warning. In that case, test files without this pattern are ignored and not executed without warning.
=== What is the potential impact? === What is the potential impact?

View File

@ -1,6 +1,6 @@
== Why is this an issue? == Why is this an issue?
The standard PHPUnit assertion methods such as `__assertEquals__`, expect the first argument to be the expected value and the second argument to be the actual value. The standard PHPUnit assertion methods such as ``++__assertEquals__++``, expect the first argument to be the expected value and the second argument to be the actual value.
include::../impact.adoc[] include::../impact.adoc[]

View File

@ -1,6 +1,6 @@
== Why is this an issue? == Why is this an issue?
If a ``++RuntimeException++`` is thrown while code is unit tested, it's likely to be thrown again in production. Therefore, test methods should be allowed to \``++throw RuntimeException++``s so you're alerted early to problems in the code. If a ``++RuntimeException++`` is thrown while code is unit tested, it's likely to be thrown again in production. Therefore, test methods should be allowed to ``++throw RuntimeException++``s so you're alerted early to problems in the code.
=== Noncompliant code example === Noncompliant code example

View File

@ -53,7 +53,7 @@ and discourage the further use of the application.
=== Exceptions === Exceptions
If a function `+return+`s a pointer to the caller or stores it in an external structure, If a function ``return``s a pointer to the caller or stores it in an external structure,
this pointer is said to _escape_ (it is now accessible outside of function, and no longer local to it). this pointer is said to _escape_ (it is now accessible outside of function, and no longer local to it).
This includes storing the pointer in a static or global variable, This includes storing the pointer in a static or global variable,
passing it to a function that can potentially do that, passing it to a function that can potentially do that,

View File

@ -7,7 +7,7 @@ Attempting to release any other type of memory is _undefined behavior_.
The following non-heap memory types may not be released: The following non-heap memory types may not be released:
* Stack allocated memory - local variables or memory allocated with the `alloca`, `_alloca`, `_malloca` and `__builtin_alloca` functions. * Stack allocated memory - local variables or memory allocated with the `alloca`, ``++_alloca++``, ``++_malloca++`` and ``++__builtin_alloca++`` functions.
* Executable program code - function pointers. * Executable program code - function pointers.
* Program data - global and static variables. * Program data - global and static variables.
* Read-only program data - constants and strings. * Read-only program data - constants and strings.

View File

@ -9,7 +9,7 @@ Proprietary compiler extensions can be handy, but they commit you to always usin
* Index range in array initializers * Index range in array initializers
* A array initializer without ``++=++`` * A array initializer without ``++=++``
* A structure member initializer with a colon * A structure member initializer with a colon
* Decimal floating points numbers `_Decimal32`, `_Decimal64`, and `_Decimal128` * Decimal floating points numbers ``++_Decimal32++``, ``++_Decimal64++``, and ``++_Decimal128++``
* Structures and union without named data members * Structures and union without named data members
=== Noncompliant code example === Noncompliant code example

View File

@ -28,7 +28,7 @@ ifdef::env-github,rspecator-view[]
=== Message === Message
Replace the quotes (["|']) with back-ticks (`). Replace the quotes ``++["|']++`` with back-ticks ``++`++``.
''' '''

View File

@ -1,13 +1,13 @@
== Why is this an issue? == Why is this an issue?
The way an `#include` directive finds an actual file is implementation-defined, and in practice, it slightly differs in different systems. The way an ``++#include++`` directive finds an actual file is implementation-defined, and in practice, it slightly differs in different systems.
Therefore, a good practice is to identify the files to include in the most straightforward way possible to reduce the risk of inconsistent behaviors. Therefore, a good practice is to identify the files to include in the most straightforward way possible to reduce the risk of inconsistent behaviors.
This rule raises an issue when: This rule raises an issue when:
* The case of the file in the `#include` directive does not match the case of the file on the disk (the inclusion would not work on a case-sensitive OS), * The case of the file in the ``++#include++`` directive does not match the case of the file on the disk (the inclusion would not work on a case-sensitive OS),
* The file name in the `#include` directive contains trailing spaces (they would be ignored on Windows but considered on Unix). * The file name in the ``++#include++`` directive contains trailing spaces (they would be ignored on Windows but considered on Unix).
=== Noncompliant code example === Noncompliant code example

View File

@ -2,7 +2,7 @@
Operator precedence determines the order in which different operators are evaluated when an expression contains multiple ones. It helps determine how the expression is parsed and executed. JavaScript follows a specific set of rules to determine operator precedence. Operator precedence determines the order in which different operators are evaluated when an expression contains multiple ones. It helps determine how the expression is parsed and executed. JavaScript follows a specific set of rules to determine operator precedence.
Not being aware of JavaScript's operator precedence rules can lead to unexpected and potentially incorrect results when evaluating expressions. This is common when misapplying the logical negation operator (``++!++``). For instance, consider the difference between ``++!key in dict++`` and ``++!(key in dict)++``. The first looks for a boolean value (``++!key++``) in ``++dict++``, and the other looks for a string and inverts the result. The same applies for ``++!obj instanceof SomeClass++` Not being aware of JavaScript's operator precedence rules can lead to unexpected and potentially incorrect results when evaluating expressions. This is common when misapplying the logical negation operator (``++!++``). For instance, consider the difference between ``++!key in dict++`` and ``++!(key in dict)++``. The first looks for a boolean value (``++!key++``) in ``++dict++``, and the other looks for a string and inverts the result. The same applies for ``++!obj instanceof SomeClass++``.
This rule raises an issue when the left operand of an ``++in++`` or ``++instanceof++`` operator is negated with ``++!++``. This rule raises an issue when the left operand of an ``++in++`` or ``++instanceof++`` operator is negated with ``++!++``.

View File

@ -1,6 +1,6 @@
== Why is this an issue? == Why is this an issue?
Pointer and unmanaged function pointer types such as `IntPtr`, `UIntPtr`, `int*` etc. are used to access unmanaged memory, usually in order to use C or {cpp} libraries. If such a pointer is not secured by making it `private`, `internal` or `readonly`, it can lead to a vulnerability allowing access to arbitrary locations. Pointer and unmanaged function pointer types such as `IntPtr`, `UIntPtr`, ``++int*++`` etc. are used to access unmanaged memory, usually in order to use C or {cpp} libraries. If such a pointer is not secured by making it `private`, `internal` or `readonly`, it can lead to a vulnerability allowing access to arbitrary locations.
=== Noncompliant code example === Noncompliant code example

View File

@ -8,7 +8,7 @@ This rule raises an issue in the following cases:
** does not return any value ** does not return any value
** does not access the field with the corresponding name (if it exists). ** does not access the field with the corresponding name (if it exists).
Underscore prefixes for fields are supported, so `setX()` can assign a value to `_x`. Underscore prefixes for fields are supported, so `setX()` can assign a value to ``++_x++``.
The following type of getters and setters are supported: The following type of getters and setters are supported:

View File

@ -10,7 +10,7 @@ In all cases, the attack surface of an affected application is increased. In som
== Ask Yourself Whether == Ask Yourself Whether
* The development of the app is completed and the development feature is activated. * The development of the app is completed and the development feature is activated.
* The app is distributed to end users with the `development feature activated * The app is distributed to end users with the development feature activated
There is a risk if you answered yes to any of those questions. There is a risk if you answered yes to any of those questions.

View File

@ -4,8 +4,8 @@
include::../../common/fix/code-rationale.adoc[] include::../../common/fix/code-rationale.adoc[]
Certificate validation is not enabled by default when `_create_unverified_context` Certificate validation is not enabled by default when ``++_create_unverified_context++``
is used. It is recommended to use `_create_default_https_context` instead to is used. It is recommended to use ``++_create_default_https_context++`` instead to
create a secure context that validates certificates. create a secure context that validates certificates.
==== Noncompliant code example ==== Noncompliant code example

View File

@ -194,7 +194,7 @@ When applicable, it is recommended to replace the VLA with heap-allocated memory
In contrast to VLA, heap allocation functions report in a situation when sufficient memory cannot be provided, by returning `NULL` or throwing an exception (in {cpp}). In contrast to VLA, heap allocation functions report in a situation when sufficient memory cannot be provided, by returning `NULL` or throwing an exception (in {cpp}).
Furthermore, the {cpp} standard library provides containers like `std::vector`, that manage the heap-allocated memory. Furthermore, the {cpp} standard library provides containers like `std::vector`, that manage the heap-allocated memory.
Moreover, the C11 language standard and above only optionally supports VLAs (with `__STDC_NO_VLA__`), Moreover, the C11 language standard and above only optionally supports VLAs (with ``++__STDC_NO_VLA__++``),
and the {cpp} standard never supported it, however, they are commonly accepted as extensions. and the {cpp} standard never supported it, however, they are commonly accepted as extensions.

View File

@ -3,7 +3,7 @@
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm[There is a limit] to the number of batch jobs which can be executed at the same time. Executing batch jobs in triggers or in loops is complex as it can easily reach this limit (example: after a bulk update), thus it is recommended to avoid it. Note that checking if the limit is already reached is not enough as nothing prevents the addition of a job between the check and the subsequent call to ``++executeBatch++``. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm[There is a limit] to the number of batch jobs which can be executed at the same time. Executing batch jobs in triggers or in loops is complex as it can easily reach this limit (example: after a bulk update), thus it is recommended to avoid it. Note that checking if the limit is already reached is not enough as nothing prevents the addition of a job between the check and the subsequent call to ``++executeBatch++``.
This rule raises an issue when \``++Databasee.executeBatch()++`` is called in a Trigger or in a loop. This rule raises an issue when ``++Databasee.executeBatch()++`` is called in a Trigger or in a loop.
=== Noncompliant code example === Noncompliant code example

View File

@ -1,6 +1,6 @@
== Why is this an issue? == Why is this an issue?
Salesforce Governor Limits do not allow more than 10 calls to \``++Messaging.sendEmail++`` in a single transaction. There is a good chance that calling this method in a loop will reach that limit and fail. You can instead send a batch of emails with a single call to \``++Messaging.sendEmail++``. Salesforce Governor Limits do not allow more than 10 calls to ``++Messaging.sendEmail++`` in a single transaction. There is a good chance that calling this method in a loop will reach that limit and fail. You can instead send a batch of emails with a single call to ``++Messaging.sendEmail++``.
This rule raises an issue when a call to ``++Messaging.sendEmail++`` is found in a loop. This rule raises an issue when a call to ``++Messaging.sendEmail++`` is found in a loop.

View File

@ -5,7 +5,7 @@
include::../../common/fix/code-rationale.adoc[] include::../../common/fix/code-rationale.adoc[]
Certificate validation is not enabled by default when Certificate validation is not enabled by default when
`_create_unverified_context` or `_create_stdlib_context` is used. It is ``++_create_unverified_context++`` or ``++_create_stdlib_context++`` is used. It is
recommended to use `create_default_context`, without explicitly setting recommended to use `create_default_context`, without explicitly setting
`check_hostname` to `False`. + `check_hostname` to `False`. +
Doing so creates a secure context that validates both hostnames and Doing so creates a secure context that validates both hostnames and

View File

@ -21,11 +21,12 @@ with MyContextManager():
---- ----
will output: will output:
``` [source,text]
----
Entering Entering
Executing body Executing body
Exiting Exiting
``` ----
If either the ``++__enter__++`` or the ``++__exit__++`` method is missing, an ``AttributeError`` will be raised instead. If either the ``++__enter__++`` or the ``++__exit__++`` method is missing, an ``AttributeError`` will be raised instead.

View File

@ -1,5 +1,5 @@
The function `size_t strlen(const char *s)` measures the length of the string `s` (excluding the final null character). + The function ``++size_t strlen(const char *s)++`` measures the length of the string `s` (excluding the final null character). +
The function `size_t wcslen(const wchar_t *s)` does the same for wide characters, and should be used with the same guidelines. The function ``++size_t wcslen(const wchar_t *s)++`` does the same for wide characters, and should be used with the same guidelines.
Similarly to many other functions in the standard C libraries, Similarly to many other functions in the standard C libraries,
`strlen` and `wcslen` assume that their argument is not a null pointer. `strlen` and `wcslen` assume that their argument is not a null pointer.

View File

@ -29,7 +29,7 @@ Note: This scenario can only happen before {cpp}17. Since {cpp}17, the standard
Using `make_unique()` doesn't impact performance, but `make_shared()` improves it slightly. + Using `make_unique()` doesn't impact performance, but `make_shared()` improves it slightly. +
Indeed, constructing explicitly a `shared_ptr()` requires two heap allocations: one for the managed object and the other for the control block that stores data about the ref-counts and the `shared_ptr()` deleter. `make_shared()` on the other hand, performs only one heap allocation. Indeed, constructing explicitly a `shared_ptr()` requires two heap allocations: one for the managed object and the other for the control block that stores data about the ref-counts and the `shared_ptr()` deleter. `make_shared()` on the other hand, performs only one heap allocation.
Note: Because `make_shared` performs only one allocation for both the object and the control block, the memory occupied by the object will be deallocated when no `shared_ptr` or `weak_ptr` points to it. If the object is large, a `weak_ptr` is used, and memory is a concern, explicitly calling the constructor of `shared_ptr` may be preferred. This way, the object's memory will be deallocated when there are no more shared owners, independently of any `weak_ptr`s. Note: Because `make_shared` performs only one allocation for both the object and the control block, the memory occupied by the object will be deallocated when no `shared_ptr` or `weak_ptr` points to it. If the object is large, a `weak_ptr` is used, and memory is a concern, explicitly calling the constructor of `shared_ptr` may be preferred. This way, the object's memory will be deallocated when there are no more shared owners, independently of any ``weak_ptr``s.
=== Noncompliant code example === Noncompliant code example

View File

@ -47,7 +47,7 @@ void g() {
} }
---- ----
In this case ``++str++`` will not be converted to a ``++std::string++`` when calling the function ``++find++``, and each element of the bucket that corresponds to the hash of ``++str++`` will be compared using homogeneous ``++operator==++``, In this case ``++str++`` will not be converted to a ``++std::string++`` when calling the function ``++find++``, and each element of the bucket that corresponds to the hash of ``++str++`` will be compared using homogeneous ``++operator==++``,
and for each such comparison, a conversion will now happen. The number of compared elements varies depending on the hash distribution from ``++O(1)++` (on average) to ``++O(N)++`` (in the worst case). and for each such comparison, a conversion will now happen. The number of compared elements varies depending on the hash distribution from ``++O(1)++`` (on average) to ``++O(N)++`` (in the worst case).
As consequence, the performance of slow runs (when multiple hash collisions happen due to the data distribution) is made even worse. As consequence, the performance of slow runs (when multiple hash collisions happen due to the data distribution) is made even worse.

View File

@ -1,4 +1,4 @@
Compare integers of mixed signedness safely using `std::cmp_*` functions to avoid any unexpected results. Compare integers of mixed signedness safely using ``++std::cmp_*++`` functions to avoid any unexpected results.
== Why is this an issue? == Why is this an issue?
@ -18,7 +18,7 @@ For example, using container size functions in a comparison can lead to such a p
== How to fix it == How to fix it
{cpp}20 introduced a remedy to this common pitfall: a family of `std::cmp_*` functions defined in the `<utility>` header. {cpp}20 introduced a remedy to this common pitfall: a family of ``++std::cmp_*++`` functions defined in the `<utility>` header.
These functions correctly handle negative numbers and lossy integer conversion. These functions correctly handle negative numbers and lossy integer conversion.
For example, `std::cmp_less(2U, -1)` is `false`. For example, `std::cmp_less(2U, -1)` is `false`.

View File

@ -6,7 +6,7 @@ In {cpp}20 ``++std::to_address++`` was introduced to perform this operation on b
* The first option was to take the address of the element pointed by the iterator: ``++&*it++``. However, this operation has undefined behavior if the iterator is not pointing to any element. This may happen for the iterator returned by a call to ``++end()++`` on the container. This may also be the case when we need the address to construct a new object (via placement new) at the location pointed to by the iterator. ``++std::to_address(it)++`` works in such cases. * The first option was to take the address of the element pointed by the iterator: ``++&*it++``. However, this operation has undefined behavior if the iterator is not pointing to any element. This may happen for the iterator returned by a call to ``++end()++`` on the container. This may also be the case when we need the address to construct a new object (via placement new) at the location pointed to by the iterator. ``++std::to_address(it)++`` works in such cases.
* The second option was to exploit the nature of ``++operator->++`` overloading and call it explicitly on the iterator: ``++it.operator->()++``. This option avoids the pitfalls of the previous one, at the cost of not being portable. It would fail on the implementations that use raw-pointers as iterators for contiguous ranges like ``++std::vector++`` or ``++std::span++``. Moreover, it is confusing, as this functional notation syntax for operators is rarely used. * The second option was to exploit the nature of ``++operator->++`` overloading and call it explicitly on the iterator: ``++it.operator->()++``. This option avoids the pitfalls of the previous one, at the cost of not being portable. It would fail on the implementations that use raw-pointers as iterators for contiguous ranges like ``++std::vector++`` or ``++std::span++``. Moreover, it is confusing, as this functional notation syntax for operators is rarely used.
While both ``++std::to_address++` and above workarounds, can be always used to get the address of the element that the iterator is pointing to (if any), incrementing or decrementing may have undefined behavior. While both ``++std::to_address++`` and above workarounds, can be always used to get the address of the element that the iterator is pointing to (if any), incrementing or decrementing may have undefined behavior.
Performing pointer arithmetic on pointer to elements is safe only in the case of contiguous iterators (e.g. iterators of `std::vector`, `std::array`, `std::span`, `std::string` or `std::string_view`). Performing pointer arithmetic on pointer to elements is safe only in the case of contiguous iterators (e.g. iterators of `std::vector`, `std::array`, `std::span`, `std::string` or `std::string_view`).
This rule raises an issue when dereferencing a pointer-like object is immediately followed by taking the address of the result (``++&*x++`` or ``++std::addressof(*x)++``) or when ``++operator->++`` is called through an explicit functional notation (``++x.operator->()++``). This rule raises an issue when dereferencing a pointer-like object is immediately followed by taking the address of the result (``++&*x++`` or ``++std::addressof(*x)++``) or when ``++operator->++`` is called through an explicit functional notation (``++x.operator->()++``).

View File

@ -51,7 +51,7 @@ https://cloud.google.com/iam/docs/keys-create-delete#creating[GCP docs].
==== Compliant solution ==== Compliant solution
Always avoid committing service account key files to public systems. Use any Always avoid committing service account key files to public systems. Use any
`*ignore` file possible, such as `.gitignore`, `.dockerignore` and equivalents ``++*ignore++`` file possible, such as `.gitignore`, `.dockerignore` and equivalents
for any other system accessing your local codebase. for any other system accessing your local codebase.
//=== How does this work? //=== How does this work?

View File

@ -12,6 +12,6 @@ For example, to match a two-digit number, one could write `+[0-9]{2,2}+` or `+\d
This rule recommends replacing some quantifiers and character classes with more concise equivalents: This rule recommends replacing some quantifiers and character classes with more concise equivalents:
* `+\d+` for `+[0-9]+` and `+\D+` for `[^0-9]` * `+\d+` for `+[0-9]+` and `+\D+` for `[^0-9]`
* `+\w+` for `+[A-Za-z0-9_]+` and `+\W+` for `[^A-Za-z0-9_]` * `+\w+` for `+[A-Za-z0-9_]+` and `+\W+` for ``++[^A-Za-z0-9_]++``
* `+.+` for character classes matching everything (e.g. `+[\w\W]+`, `+[\d\D]+`, or `+[\s\S]+` with `+s+` flag) * `+.+` for character classes matching everything (e.g. `+[\w\W]+`, `+[\d\D]+`, or `+[\s\S]+` with `+s+` flag)
* `+x?+` for `+x{0,1}+`, `+x*+` for `+x{0,}+`, `+x++` for `+x{1,}+`, `+x{N}+` for `+x{N,N}+` * `+x?+` for `+x{0,1}+`, `+x*+` for `+x{0,}+`, `+x++` for `+x{1,}+`, `+x{N}+` for `+x{N,N}+`

View File

@ -162,6 +162,6 @@ include::../message.adoc[]
=== Highlighting === Highlighting
* For ``Microsoft.Batch/batchAccounts/pools``, highlight ``"elevationLevel": "Admin"``. * For ``Microsoft.Batch/batchAccounts/pools``, highlight ``"elevationLevel": "Admin"``.
* For ``Microsoft.ContainerRegistry/registries``, highlight `"adminUserEnabled": true``. * For ``Microsoft.ContainerRegistry/registries``, highlight ``"adminUserEnabled": true``.
endif::env-github,rspecator-view[] endif::env-github,rspecator-view[]

View File

@ -2,7 +2,7 @@
The class `std::optional<T>` either stores a value of type `T` or is empty. The class `std::optional<T>` either stores a value of type `T` or is empty.
One way to access the value of a non-empty optional is the `operator*`. But using the dereference operator gives the optional appearance of a pointer when it is not: it models an object. Additionally, attempting to call the `operator*` on an empty optional will result in undefined behavior. One way to access the value of a non-empty optional is the ``++operator*++``. But using the dereference operator gives the optional appearance of a pointer when it is not: it models an object. Additionally, attempting to call the ``++operator*++`` on an empty optional will result in undefined behavior.
Another way to access the value of a non-empty optional is the function `value()`. But assigning a value to the optional object through this function will throw an exception (`std::bad_optional_access`) if the optional has no value, and the assignment will not happen. Another way to access the value of a non-empty optional is the function `value()`. But assigning a value to the optional object through this function will throw an exception (`std::bad_optional_access`) if the optional has no value, and the assignment will not happen.
@ -13,7 +13,7 @@ For the assignment of an optional to happen correctly, whatever its state, it is
== How to fix it == How to fix it
The rule raises an issue when the `operator*` or the `value()` function are used to assign a new value to an optional. The rule raises an issue when the ``++operator*++`` or the `value()` function are used to assign a new value to an optional.
=== Code examples === Code examples

View File

@ -88,7 +88,7 @@ Make sure it is safe to use host operating system namespaces here.
=== Highlighting === Highlighting
Highlight `host___: true`. Highlight ``++host___: true++``.
endif::env-github,rspecator-view[] endif::env-github,rspecator-view[]

View File

@ -1,8 +1,8 @@
Catching `ExceptionGroup` with `except*` will raise a `TypeError`. Catching `ExceptionGroup` with ``++except*++`` will raise a `TypeError`.
== Why is this an issue? == Why is this an issue?
Python 3.11 introduced `except*` and `ExceptionGroup`, making it possible to handle and raise multiple unrelated exceptions simultaneously. Python 3.11 introduced ``++except*++`` and `ExceptionGroup`, making it possible to handle and raise multiple unrelated exceptions simultaneously.
In the example below, we gather multiple exceptions in an `ExceptionGroup`. This `ExceptionGroup` is then caught by a single except block: In the example below, we gather multiple exceptions in an `ExceptionGroup`. This `ExceptionGroup` is then caught by a single except block:
@ -18,7 +18,7 @@ except ExceptionGroup as exceptions:
pass pass
---- ----
To handle differently each type of exceptions present in an `ExceptionGroup`, we have to use the `except*` keyword. To handle differently each type of exceptions present in an `ExceptionGroup`, we have to use the ``++except*++`` keyword.
[source,python] [source,python]
---- ----
@ -36,7 +36,7 @@ except* TypeError as t:
pass pass
---- ----
While it is possible to catch the `ExceptionGroup` and `BaseExceptionGroup` types with `except`, a `TypeError` will be raised when this is done with `except*`. While it is possible to catch the `ExceptionGroup` and `BaseExceptionGroup` types with `except`, a `TypeError` will be raised when this is done with ``++except*++``.
== How to fix it == How to fix it

View File

@ -11,7 +11,7 @@ The values are stored using 64 bits in the following form:
// When images can be added, add image based on to this one // When images can be added, add image based on to this one
//image::IEEE_754_Double_Floating_Point_Format.svg.png[] //image::IEEE_754_Double_Floating_Point_Format.svg.png[]
The actual value of the stored number will be `(-1)^sign^ * (1 + significand) * 2 ^exponent^` The actual value of the stored number will be `pass:n[(-1)^sign^ * (1 + significand) * 2 ^exponent^]`
Given this structure, there are limits in both *magnitude* and *precision*. Given this structure, there are limits in both *magnitude* and *precision*.
@ -40,7 +40,7 @@ myBigInt + 1n === myBigInt + 2n; // false
For large numbers, JavaScript provides the helper function `Number.isSafeInteger()` to test if a number is between the safe limits. For large numbers, JavaScript provides the helper function `Number.isSafeInteger()` to test if a number is between the safe limits.
When you need to store a large number, use `BigInt`. `bigint` and `number` primitives can be compared between them as usual (e.g. `>`, `==`), but pay attention that arithmetic operations (`+` `pass:[*]` `-` `%` `pass:[**]`) between both types raise an error unless they are converted to the same type. Use the `BigInt` and `Number` functions to convert between both types: When you need to store a large number, use `BigInt`. `bigint` and `number` primitives can be compared between them as usual (e.g. `>`, `==`), but pay attention that arithmetic operations (`+` `pass:[*]` `-` `%` `++**++`) between both types raise an error unless they are converted to the same type. Use the `BigInt` and `Number` functions to convert between both types:
[source,javascript] [source,javascript]
---- ----
const myNumber = Number(myBigInt); const myNumber = Number(myBigInt);

View File

@ -21,7 +21,7 @@ For example, injecting data into the `HTTP_PROXY` variable could lead to data le
==== Application compromise ==== Application compromise
In the worst case, an attacker manages to inject an important environment In the worst case, an attacker manages to inject an important environment
variable such as ` LD _PRELOAD` and execute code by overriding trusted code. variable such as `LD_PRELOAD` and execute code by overriding trusted code.
Depending on the attacker, code execution can be used with different Depending on the attacker, code execution can be used with different
intentions: intentions:

View File

@ -1,12 +1,12 @@
This rule ensures that Django models have a `__str__` method defined. This rule ensures that Django models have a ``++__str__++`` method defined.
== Why is this an issue? == Why is this an issue?
The `__str__` method in Django models is used to represent the model instance as a string. For example, the return value of this method will be inserted in a template when displaying an object in the Django admin site. Without this method, the model instance will be represented by its object identifier, which is not meaningful to end-users. This can result in confusion and make debugging more difficult. The ``++__str__++`` method in Django models is used to represent the model instance as a string. For example, the return value of this method will be inserted in a template when displaying an object in the Django admin site. Without this method, the model instance will be represented by its object identifier, which is not meaningful to end-users. This can result in confusion and make debugging more difficult.
== How to fix it == How to fix it
To fix this issue, the Django model must define a `__str__` method that returns a string representation of the instance. This string should be meaningful to end-users and provide information about the model instance. To fix this issue, the Django model must define a ``++__str__++`` method that returns a string representation of the instance. This string should be meaningful to end-users and provide information about the model instance.
=== Code examples === Code examples

View File

@ -1,8 +1,8 @@
This rule discourages the use of `exclude` or `__all__` with ModelForm in Django and suggests using fields instead. This rule discourages the use of `exclude` or ``++__all__++`` with ModelForm in Django and suggests using fields instead.
== Why is this an issue? == Why is this an issue?
In Django, when creating a `ModelForm`, it is common to use `exclude` to remove fields from the form. It is also possible to set the `fields` value to `__all__` to conveniently indicate that all the model fields should be included in the form. However, this can lead to security issues when new fields are added to the model, as they will automatically be included in the form, which may not be intended. Additionally, `exclude` or `__all__` can make it harder to maintain the codebase by hiding the dependencies between the model and the form. In Django, when creating a `ModelForm`, it is common to use `exclude` to remove fields from the form. It is also possible to set the `fields` value to ``++__all__++`` to conveniently indicate that all the model fields should be included in the form. However, this can lead to security issues when new fields are added to the model, as they will automatically be included in the form, which may not be intended. Additionally, `exclude` or ``++__all__++`` can make it harder to maintain the codebase by hiding the dependencies between the model and the form.
== How to fix it == How to fix it
Developers should use the "fields" attribute instead of "exclude" or "all" when creating ModelForms in Django. This ensures that all fields are explicitly listed and makes it clear what fields are included in the form. Developers should use the "fields" attribute instead of "exclude" or "all" when creating ModelForms in Django. This ensures that all fields are explicitly listed and makes it clear what fields are included in the form.

View File

@ -35,7 +35,7 @@ This code will print `"Hello World"`, omitting the leading and trailing whitespa
==== Compliant solution ==== Compliant solution
This example demonstrates pathname expansion using the `echo` command, which will print `"command t*.sh"` as intended: This example demonstrates pathname expansion using the `echo` command, which will print ``++"command t*.sh"++`` as intended:
[source,docker,diff-id=1,diff-type=compliant] [source,docker,diff-id=1,diff-type=compliant]
---- ----
RUN test="command t*.sh" && echo "$test" RUN test="command t*.sh" && echo "$test"

View File

@ -57,10 +57,11 @@ public void EndsWith_Char()
Hardware configuration: Hardware configuration:
``` [source,text]
----
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update) BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.203 .NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 .NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
``` ----

Some files were not shown because too many files have changed in this diff Show More