================ @@ -0,0 +1,534 @@ +#!/usr/bin/env python3 + +"""generate_unsupported_in_drivermode.py + +This script generates Lit regression test files that validate that options are only exposed to intended driver modes. + +The options and driver modes are parsed from Options.td, whose path should be provided on the command line. +See clang/include/clang/Driver/Options.td + +The LLVM TableGen executable can optionally be provided along with the path to the LLVM build tree bin directory. +Otherwise, the script will search for it. + +The primary maintenance task for this script would be updating the expected return message for a driver mode if +there are changes over time. See the instantiations of DriverData, specifically the check_str. + +Logic: +1) For each option, (records of class "Option"), and for each driver, (records of class "OptionVisibility") + a. if the option's "Visibility" field includes the driver flavour, skip processing this option for this driver + b. if the option is part of an option group, (the record has the "Group" property), + and the group's "Visibility" field includes the driver flavour, skip processing this option for this driver + c. otherwise this option is not supported by this driver flavour, and this pairing is saved for testing +2) For each unsupported pairing, generate a Lit RUN line, and a CHECK line to parse for expected output. Ex: "error: unknown argument" +""" + +import os +import json +import subprocess +from bisect import bisect_left +from dataclasses import dataclass +import argparse +import dataclasses +import itertools + +# Strings defined in Options.td for the various driver flavours. See "OptionVisibility" +VISIBILITY_CC1AS = "CC1AsOption" +VISIBILITY_CC1 = "CC1Option" +VISIBILITY_CL = "CLOption" +VISIBILITY_DXC = "DXCOption" +VISIBILITY_DEFAULT = "DefaultVis" +VISIBILITY_FC1 = "FC1Option" +VISIBILITY_FLANG = "FlangOption" + +# Lit test prefix strings +SLASH_SLASH = "// " +EXCLAMATION = "! " + +exceptions_sequence = [ + # Invalid usage of the driver options below causes unique output, so skip testing + "cc1", + "cc1as", + # There is currently a bug with "_no_warnings", i.e. --no-warnings. Diagnostic related options + # are parsed first, and always with CC1 visibility. They're used to set up the diagnostic + # engine, which parses "_no_warnings" (and its alias "w", i.e. -w) and sets an internal flag + # that suppresses all warnings. + "_no_warnings", + "w", +] + + +@dataclass +class DriverOption: + """Defines an unsupported driver-option combination + driver: The driver string as defined by OptionVisibility in Options.td + option: The option object from Options.td + option_name: Corresponding string for an option. See "Name" for a given option in Options.td + prefix: String that precedes the option. Ex. "-" + """ + + driver: str + option: str + option_name: str + prefix: str + + +@dataclass +class DriverData: + """Dataclass for data specific to each driver + lit_cmd_prefix: The beginning string of the Lit command + lit_cmd_options: Strings containing additional options for this driver + visibility_str: The corresponding visibility string from OptionVisibility in Options.td + lit_cmd_end: String at the end of the Lit command + check_str: The string or regex to be sent to FileCheck + supported_joined_option_sequence: List of DriverOption objects for supported options + that are Kind *JOINED*, as defined in Options.td + supported_non_joined_option_sequence: List of DriverOption objects for supported options + that are not Kind *JOINED*, as defined in Options.td + test_option_sequence: A list of all the prefix-option pairs that will be tested for this driver + """ + + lit_cmd_prefix: str + lit_cmd_options: str + visibility_str: str + lit_cmd_end: str + check_str: str + supported_joined_option_sequence: list[DriverOption] = dataclasses.field( + default_factory=list + ) + supported_non_joined_option_sequence: list[DriverOption] = dataclasses.field( + default_factory=list + ) + test_option_sequence: list[str] = dataclasses.field(default_factory=list) + + +def collect_transitive_groups(member, options_dictionary): + """Find the groups for a given member, where a member can be an option or a group. + Note that groups can themselves be part of groups, hence the recursion + + For example, considering option 'C', it has the following 'Group' field as defined by Options.td: + "C": { + "Group": { + "def": "Preprocessor_Group", + // ... + }, + // ... + }, + 'Preprocessor_Group' is itself part of 'CompileOnly_Group', so option 'C' would be part of both groups + "Preprocessor_Group": { + // ... + "Group": { + "def": "CompileOnly_Group", + // ... + }, + // ... + }, + + member: An option object or group object from Options.td. + options_dictionary: The converted Python dictionary from the Options.td json string + + Return: A set including the group(s) found for the member. If no groups found, returns an empty set + """ + parent_field = options_dictionary[member]["Group"] + if parent_field is None: + return set() + + parent_name = parent_field["def"] + return {parent_name} | collect_transitive_groups(parent_name, options_dictionary) + + +def get_visibility(option): + """Get a list of drivers that a given option is exposed to + option: The option object from Options.td + Return: Set that contains the visibilities of the given option + """ + visibility_set = set() + # Check for the option's explicit visibility + for visibility in options_dictionary[option]["Visibility"]: + if visibility is not None: + visibility_set.add(visibility["def"]) + + # Check for the option's group's visibility + group_set = collect_transitive_groups(option, options_dictionary) + if group_set: + for group_name in group_set: + for visibility in options_dictionary[group_name]["Visibility"]: + visibility_set.add(visibility["def"]) + + return visibility_set + + +def get_lit_test_note(test_visibility): + """Return the note to be included at the start of the Lit test file + test_visibility: Any VISIBILITY_* variable. VISIBILITY_DEFAULT will return the .c formatted test note. + All other will return the .f90 formatted test note + """ + test_prefix = SLASH_SLASH if test_visibility == VISIBILITY_DEFAULT else EXCLAMATION ---------------- Maetveis wrote:
I guess here too, use ! and // instead of the constants. It's not a problem that it's "duplicated", as it makes reading the code much simpler. https://github.com/llvm/llvm-project/pull/120900 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits