diff --git a/.github/workflows/validate-package-files.yml b/.github/workflows/validate-package-files.yml index 0573b00590..0e38e4a1da 100644 --- a/.github/workflows/validate-package-files.yml +++ b/.github/workflows/validate-package-files.yml @@ -56,4 +56,10 @@ jobs: find rule_packages/$LANGUAGE -name \*.json -exec basename {} .json \; | xargs python scripts/generate_rules/generate_package_files.py $LANGUAGE git diff git diff --compact-summary - git diff --quiet \ No newline at end of file + git diff --quiet + + - name: Validate Amendments + env: + LANGUAGE: ${{ matrix.language }} + run: | + python scripts/validate-amendments-csv.py $LANGUAGE \ No newline at end of file diff --git a/amendments.csv b/amendments.csv new file mode 100644 index 0000000000..cd0085493e --- /dev/null +++ b/amendments.csv @@ -0,0 +1,49 @@ +language,standard,amendment,rule_id,supportable,implementation_category,implemented,difficulty +c,MISRA-C-2012,Amendment3,DIR-4-6,Yes,Expand,No,Easy +c,MISRA-C-2012,Amendment3,DIR-4-9,Yes,Refine,No,Easy +c,MISRA-C-2012,Amendment3,DIR-4-11,Yes,Refine,No,Import +c,MISRA-C-2012,Amendment3,RULE-1-4,Yes,Replace,No,Easy +c,MISRA-C-2012,Amendment3,RULE-10-1,Yes,Replace,No,Easy +c,MISRA-C-2012,Amendment3,RULE-10-3,Yes,Refine,No,Easy +c,MISRA-C-2012,Amendment3,RULE-10-4,Yes,Refine,No,Import +c,MISRA-C-2012,Amendment3,RULE-10-5,Yes,Expand,No,Easy +c,MISRA-C-2012,Amendment3,RULE-10-7,Yes,Refine,No,Import +c,MISRA-C-2012,Amendment3,RULE-10-8,Yes,Refine,No,Import +c,MISRA-C-2012,Amendment3,RULE-21-11,Yes,Clarification,No,Import +c,MISRA-C-2012,Amendment3,RULE-21-12,Yes,Replace,No,Easy +c,MISRA-C-2012,Amendment4,RULE-11-3,Yes,Expand,No,Easy +c,MISRA-C-2012,Amendment4,RULE-11-8,Yes,Expand,No,Easy +c,MISRA-C-2012,Amendment4,RULE-13-2,Yes,Expand,No,Very Hard +c,MISRA-C-2012,Amendment4,RULE-18-6,Yes,Expand,No,Medium +c,MISRA-C-2012,Amendment4,RULE-18-8,Yes,Split,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-2-2,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-2-7,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-3-1,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-8-6,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-8-9,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-9-4,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-10-1,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-18-3,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-1-4,Yes,Replace,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-9-1,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-9-2,Yes,Refine,No,Import +c,MISRA-C-2012,Corrigendum2,DIR-4-10,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-7-4,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-8-2,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-8-3,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-8-7,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-10-2,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-10-3,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-11-3,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-11-6,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-13-2,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-13-6,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-14-3,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-15-7,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-17-4,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-17-5,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-18-1,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-20-14,No,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-21-19,Yes,Clarification,No,Import +c,MISRA-C-2012,Corrigendum2,RULE-21-20,Yes,Refine,No,Easy +c,MISRA-C-2012,Corrigendum2,RULE-22-9,Yes,Clarification,No,Import \ No newline at end of file diff --git a/scripts/validate-amendments-csv.py b/scripts/validate-amendments-csv.py new file mode 100644 index 0000000000..9d83b7d0c9 --- /dev/null +++ b/scripts/validate-amendments-csv.py @@ -0,0 +1,128 @@ +from collections import defaultdict +import csv +import os +from pathlib import Path +import sys +import json + +help_statement = """ +Usage: {script_name} + +A script which detects invalid entries in amendments.csv. +""" + +if (len(sys.argv) == 2 and sys.argv[1] == "--help"): + print(help_statement.format(script_name=sys.argv[0])) + sys.exit(0) + +if not len(sys.argv) == 2: + print("Error: incorrect number of arguments", file=sys.stderr) + print("Usage: " + sys.argv[0] + " [--help]", file=sys.stderr) + sys.exit(1) + +repo_root = Path(__file__).parent.parent +rules_file_path = repo_root.joinpath('rules.csv') +amendments_file_path = repo_root.joinpath('amendments.csv') +language_name = sys.argv[1] + +failed = False + +rules_from_csv = {} +try: + rules_file = open(rules_file_path, "r") +except PermissionError: + print("Error: No permission to read the rules file located at '" + str(rules_file_path) + "'") + sys.exit(1) +else: + with rules_file: + rules_reader = csv.reader(rules_file) + # Skip header row + next(rules_reader, None) + for rule in rules_reader: + language = rule[0] + rule_id = rule[2] + + # only validate rules for the specified language + if not language == language_name: + continue + + rule_dict = { + "standard": rule[1], + "rule_id": rule_id, + "supportable": rule[3] + } + rules_from_csv[rule_id] = rule_dict + +print(f"Found {len(rules_from_csv)} rules.") +print(f"Verifying amendments") + +seen_amendments = set() +try: + amendments_file = open(amendments_file_path, "r") +except PermissionError: + print("Error: No permission to read the amendments file located at '" + str(amendments_file_path) + "'") + sys.exit(1) +else: + with amendments_file: + amendments_reader = csv.reader(amendments_file) + # Skip header row + next(amendments_reader, None) + for amendment in amendments_reader: + language = amendment[0] + + # only validate rules for the specified language + if not language == language_name: + continue + + if len(amendment) != 8: + print(f"🔴 Error: amendment {amendment} has wrong number of fields") + failed = True + continue + + standard = amendment[1] + amendment_name = amendment[2] + rule_id = amendment[3] + supportable = amendment[4] + implemented = amendment[6] + amendment_id = f"{rule_id}-{amendment_name}" + + if not rule_id in rules_from_csv: + print(f"🔴 Error: Amendment {amendment_id} references rule {rule_id}, not found in rules.csv") + failed = True + continue + + rule = rules_from_csv[rule_id] + + if rule["standard"] != standard: + print(f"🟡 Invalid: {amendment_id} has a different standard than the {rule_id} in rules.csv") + print(f" '{standard}' vs '{rule['standard']}'") + failed = True + + if supportable not in {"Yes", "No"}: + print(f"🟡 Invalid: {amendment_id} 'supportable' field should be 'Yes' or 'No'.") + print(f" got '{supportable}'") + failed = True + + if rule["supportable"] != supportable: + print(f"🟡 Invalid: {amendment_id} supportable does not match rules.csv supportable.") + print(f" '{supportable}' vs '{rule['supportable']}'") + failed = True + + if implemented not in {"Yes", "No"}: + print(f"🟡 Invalid: {amendment_id} 'implemented' field should be 'Yes' or 'No'.") + print(f" got '{implemented}'") + failed = True + + if amendment_id in seen_amendments: + print(f"🔴 Error: {amendment_id} has duplicate entries") + failed = True + + seen_amendments.add(amendment_id) + +print(f"Checked {len(seen_amendments)} amendments.") + +if failed: + print("❌ FAILED: Validity issues found in amendments.csv!") + sys.exit(1) +else: + print("✅ PASSED: No validity issues found in amendments.csv! 🎉")