Skip to content

Conversation

mordante
Copy link
Member

@mordante mordante commented May 4, 2025

This ensure the input is properly sorted; when not gives a nice diagnostic.

@mordante mordante requested a review from a team as a code owner May 4, 2025 17:10
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label May 4, 2025
@llvmbot
Copy link
Member

llvmbot commented May 4, 2025

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

This ensure the input is properly sorted; when not gives a nice diagnostic.


Full diff: https://github.com/llvm/llvm-project/pull/138462.diff

2 Files Affected:

  • (modified) libcxx/test/libcxx/feature_test_macro/invalid.sh.py (+168-1)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+23)
diff --git a/libcxx/test/libcxx/feature_test_macro/invalid.sh.py b/libcxx/test/libcxx/feature_test_macro/invalid.sh.py
index ae457f6e1a545..588f6bec1521c 100644
--- a/libcxx/test/libcxx/feature_test_macro/invalid.sh.py
+++ b/libcxx/test/libcxx/feature_test_macro/invalid.sh.py
@@ -12,7 +12,7 @@
 import json
 
 sys.path.append(sys.argv[1])
-from generate_feature_test_macro_components import FeatureTestMacros
+from generate_feature_test_macro_components import FeatureTestMacros, DataNotSorted
 
 
 def test(output, expected):
@@ -106,3 +106,170 @@ def test_error(data, type, message):
     KeyError,
     "'implemented'",
 )
+
+test_error(
+    [
+        {
+            "name": "abc",
+            "values": {
+                "c++17": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+        {
+            "name": "ghi",
+            "values": {
+                "c++17": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+        { # This entry is in the wrong alphabetic order
+            "name": "def",
+            "values": {
+                "c++17": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+        {
+            "name": "jkl",
+            "values": {
+                "c++17": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+    ],
+    DataNotSorted,
+    """\
+The ftm names are not sorted.
+--- input data
++++ sorted data
+@@ -1,4 +1,4 @@
+ abc
++def
+ ghi
+-def
+ jkl
+""",
+)
+
+test_error(
+    [
+        {
+            "name": "abc",
+            "values": {
+                "c++14": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+                "c++23": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+                # This entry is in the wrong alphabetic order
+                # Note we don't use C++98, but C++03 instead so alphabetic order
+                # works this century.
+                "c++20": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+    ],
+    DataNotSorted,
+    """\
+The C++ standard version numbers of ftm 'abc' are not sorted.
+--- input data
++++ sorted data
+@@ -1,3 +1,3 @@
+ c++14
++c++20
+ c++23
+-c++20
+""",
+)
+
+test_error(
+    [
+        {
+            "name": "abc",
+            "values": {
+                "c++14": {
+                    "197001": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                    "197002": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                    "197004": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                    # This entry is in the wrong alphabetic order
+                    "197003": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                    "197005": [
+                        {
+                            "implemented": False,
+                        },
+                    ],
+                },
+            },
+            "headers": [],
+        },
+    ],
+    DataNotSorted,
+    """\
+The value of the fmt 'abc' in c++14 are not sorted.
+--- input data
++++ sorted data
+@@ -1,5 +1,5 @@
+ 197001
+ 197002
++197003
+ 197004
+-197003
+ 197005
+""",
+)
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index cb92dc17ba707..cf69b39b294a5 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -3,6 +3,7 @@
 import os
 from builtins import range
 from dataclasses import dataclass
+from difflib import unified_diff
 from functools import reduce
 from typing import (
     Any,
@@ -1987,6 +1988,25 @@ class VersionHeader:
     condition: str = None
 
 
+class DataNotSorted(Exception):
+    pass
+
+def validate_sorted(name:str, data: List[str]) -> None:
+    sorted_data = sorted(data)
+    if data != sorted_data:
+        raise DataNotSorted(
+            f"The {name} are not sorted.\n"
+            + "\n".join(
+                unified_diff(
+                    data,
+                    sorted_data,
+                    "input data",
+                    "sorted data",
+                    lineterm="",
+                )
+            ) + "\n"
+        )
+
 def get_ftms(
     data, std_dialects: List[Std], use_implemented_status: bool
 ) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
@@ -1996,6 +2016,7 @@ def get_ftms(
         last = None
         entry = dict()
         implemented = True
+        validate_sorted(f"C++ standard version numbers of ftm '{feature['name']}'", list(feature["values"].keys()))
         for std in std_dialects:
             if std not in feature["values"].keys():
                 if last == None:
@@ -2006,6 +2027,7 @@ def get_ftms(
                 if implemented:
                     values = feature["values"][std]
                     assert len(values) > 0, f"{feature['name']}[{std}] has no entries"
+                    validate_sorted(f"value of the fmt '{feature['name']}' in {std}", list(values.keys()))
                     for value in values:
                         papers = list(values[value])
                         assert (
@@ -2024,6 +2046,7 @@ def get_ftms(
                     entry[std] = last
         result[feature["name"]] = entry
 
+    validate_sorted("ftm names", list(result))
     return result
 
 

Copy link

github-actions bot commented May 4, 2025

⚠️ Python code formatter, darker found issues in your code. ⚠️

You can test this locally with the following command:
darker --check --diff -r HEAD~1...HEAD libcxx/test/libcxx/feature_test_macro/invalid.sh.py libcxx/utils/generate_feature_test_macro_components.py
View the diff from darker here.
--- test/libcxx/feature_test_macro/invalid.sh.py	2025-07-29 18:38:31.000000 +0000
+++ test/libcxx/feature_test_macro/invalid.sh.py	2025-07-29 18:49:32.416581 +0000
@@ -133,11 +133,11 @@
                     ],
                 },
             },
             "headers": [],
         },
-        { # This entry is in the wrong alphabetic order
+        {  # This entry is in the wrong alphabetic order
             "name": "def",
             "values": {
                 "c++17": {
                     "197001": [
                         {
--- utils/generate_feature_test_macro_components.py	2025-07-29 18:38:31.000000 +0000
+++ utils/generate_feature_test_macro_components.py	2025-07-29 18:49:33.108765 +0000
@@ -2020,14 +2020,16 @@
 class FtmHeaderTest:
     value: Value = None
     implemented: bool = None
     condition: str = None
 
+
 class DataNotSorted(Exception):
     pass
 
-def validate_sorted(name:str, data: List[str]) -> None:
+
+def validate_sorted(name: str, data: List[str]) -> None:
     sorted_data = sorted(data)
     if data != sorted_data:
         raise DataNotSorted(
             f"The {name} are not sorted.\n"
             + "\n".join(
@@ -2036,34 +2038,42 @@
                     sorted_data,
                     "input data",
                     "sorted data",
                     lineterm="",
                 )
-            ) + "\n"
+            )
+            + "\n"
         )
+
 
 def get_ftms(
     data, std_dialects: List[Std], use_implemented_status: bool
 ) -> Dict[Ftm, Dict[Std, Optional[Value]]]:
     """Impementation for FeatureTestMacros.(standard|implemented)_ftms()."""
     result = dict()
     for feature in data:
         last = None
         entry = dict()
         implemented = True
-        validate_sorted(f"C++ standard version numbers of ftm '{feature['name']}'", list(feature["values"].keys()))
+        validate_sorted(
+            f"C++ standard version numbers of ftm '{feature['name']}'",
+            list(feature["values"].keys()),
+        )
         for std in std_dialects:
             if std not in feature["values"].keys():
                 if last == None:
                     continue
                 else:
                     entry[std] = last
             else:
                 if implemented:
                     values = feature["values"][std]
                     assert len(values) > 0, f"{feature['name']}[{std}] has no entries"
-                    validate_sorted(f"value of the fmt '{feature['name']}' in {std}", list(values.keys()))
+                    validate_sorted(
+                        f"value of the fmt '{feature['name']}' in {std}",
+                        list(values.keys()),
+                    )
                     for value in values:
                         papers = list(values[value])
                         assert (
                             len(papers) > 0
                         ), f"{feature['name']}[{std}][{value}] has no entries"

@ldionne ldionne changed the title [libc++] Adds additional FTM imput validation. [libc++] Adds additional FTM input validation. May 6, 2025
This ensure the input is properly sorted; when not gives a nice
diagnostic.
@mordante mordante force-pushed the review/ftm_add_more_input_validation branch from 82e4bd2 to 4e1f8c3 Compare July 29, 2025 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants