Skip to content

Commit

Permalink
Add unsafe parameters (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
leogdion authored Feb 2, 2025
1 parent f1fb688 commit ee48f66
Show file tree
Hide file tree
Showing 229 changed files with 2,965 additions and 124 deletions.
263 changes: 162 additions & 101 deletions Scripts/soundness/unsafeflags.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,128 +18,189 @@ output_directory="$1"
mkdir -p "$output_directory"
mkdir -p "$output_directory/Frontend"

# Get the regular help output from swiftc
help_output=$(swiftc --help-hidden)

# Get frontend help output separately
frontend_help=$(swiftc -frontend -help-hidden)

# Create an array to store unsafe flags
declare -A unsafe_flags

# Process regular flags first
filtered_flags=$(echo "$help_output" | awk '
/^ -[^<][^=]*$/ && !/-[a-zA-Z]+ <.*>/ {
# Function to filter and format help output
filter_help_output() {
echo "$1" | awk '
/^ -[^<][^ ]*( +(<[^>]+>|\[[^]]+\]))? *($| .*)/ {
if (NF > 1) {
# Single line case
flag = $1
$1 = ""
desc = substr($0, 2)
print flag "|" desc
} else {
# Multi-line case - store the flag and check next line
flag = $1
getline
if ($0 ~ /^ /) {
# Next line is indented description
desc = substr($0, 26) # 26 spaces indent
print flag "|" desc
param = ""
# Check for parameter in angle brackets or square brackets
if ($2 ~ /^[<\[]/) {
# Strip angle brackets and square brackets from parameter
param = $2
gsub(/[<>\[\]]/, "", param)
$1 = ""
$2 = ""
} else {
# No description on next line
print flag "|"
# Move back one line since we might have skipped a flag
prev_line = $0
if (prev_line ~ /^ -/) {
print prev_line
}
$1 = ""
}
}
}
')

# Process regular flags and store them in the array
while IFS= read -r line; do
# Split the line into flag and description
original_flag=$(echo "$line" | cut -d'|' -f1)
description=$(echo "$line" | cut -d'|' -f2)

# Store the flag in the array (without the leading -)
flag_name=${original_flag#-}
unsafe_flags["$flag_name"]=1

# Convert the flag to CamelCase
camel_case_flag=$(echo "$original_flag" | awk '{gsub("-", " "); print $0}' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}' | sed 's/ //g')

# Write the flag and its CamelCase version to a separate file in the output directory
{
echo "/// Passes the flag \`$original_flag\`"
if [ ! -z "$description" ]; then
# Capitalize first letter and ensure space after ///
capitalized_desc=$(echo "$description" | sed 's/^[[:lower:]]/\U&/')
echo "/// $capitalized_desc"
fi
echo "public struct $camel_case_flag: UnsafeFlag { }"
} > "$output_directory/$camel_case_flag.swift"

echo "File '$output_directory/$camel_case_flag.swift' created."
done <<< "$filtered_flags"

# Process frontend flags, skipping any that exist as unsafe flags
filtered_frontend_flags=$(echo "$frontend_help" | awk '
/^ -[^<][^=]*$/ && !/-[a-zA-Z]+ <.*>/ {
if (NF > 1) {
# Single line case
flag = $1
$1 = ""
desc = substr($0, 2)
print flag "|" desc
print flag "|" param "|" desc
} else {
# Multi-line case - store the flag and check next line
flag = $1
getline
if ($0 ~ /^ /) {
# Next line is indented description
desc = substr($0, 26) # 26 spaces indent
print flag "|" desc
print flag "|" "|" desc
} else {
# No description on next line
print flag "|"
print flag "|" "|"
# Move back one line since we might have skipped a flag
prev_line = $0
if (prev_line ~ /^ -/) {
print prev_line
}
}
}
}
')
}'
}

# Function to process flags and generate Swift files
process_flags() {
local filtered_flags="$1"
local is_frontend="$2"
local output_subdir="$3"
local protocol_name="$4"
local property_name="$5"

while IFS= read -r line; do
# Split the line into flag, parameter, and description
original_flag=$(echo "$line" | cut -d'|' -f1)
parameter=$(echo "$line" | cut -d'|' -f2)
description=$(echo "$line" | cut -d'|' -f3)

# Skip the target flag
if [ "$original_flag" = "target" ]; then
continue
fi

# Process flag name and clean parameter early
if [[ "$original_flag" == *"="* ]]; then
flag_name=$(echo "${original_flag#-}" | cut -d'=' -f1)
if [ -z "$parameter" ]; then
parameter="value"
fi
else
flag_name=${original_flag#-}
fi

# Clean parameter name but preserve original name for Swift code
param_for_type=""
if [ ! -z "$parameter" ]; then
param_for_type=$(echo "$parameter" | sed -E 's/[<>\[\]]//g' | xargs)
fi

# Skip frontend flags that already exist as unsafe flags
if [ "$is_frontend" = "true" ] && [ "${unsafe_flags[$flag_name]}" = "1" ]; then
echo "Skipping frontend flag '$original_flag' as it already exists as an unsafe flag"
continue
fi

# Convert to CamelCase
if [[ "$original_flag" == *"="* ]]; then
flag_for_camel=$(echo "$original_flag" | cut -d'=' -f1)
else
flag_for_camel="$flag_name"
fi
camel_case_flag=$(echo "$flag_for_camel" | awk '{gsub("-", " "); print $0}' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}' | sed 's/ //g')

# Handle single-letter flags
if [ ${#camel_case_flag} -eq 1 ]; then
camel_case_flag="$(tr '[:lower:]' '[:upper:]' <<< ${camel_case_flag})"
fi

# Add "Flag" suffix if the name is "Target"
if [ "$camel_case_flag" = "Target" ]; then
camel_case_flag="${camel_case_flag}Flag"
fi

# Store unsafe flags for later reference
if [ "$is_frontend" = "false" ]; then
unsafe_flags["$flag_name"]=1
fi

# Process parameter type and property name
param_type=""
value_property_name=""
if [ ! -z "$param_for_type" ]; then
# Check if parameter contains slashes, is a number, or is a boolean keyword
if [[ "$param_for_type" == *"/"* ]] || [[ "$param_for_type" =~ ^[0-9]+$ ]] || \
[[ "$param_for_type" == "true" ]] || [[ "$param_for_type" == "false" ]]; then
value_property_name="value"
else
# Use parameter name for the value property, taking only the first part if it contains punctuation
# Convert to camelCase and remove dashes
value_property_name=$(echo "$param_for_type" | cut -d'=' -f1 | cut -d',' -f1 | cut -d':' -f1 | cut -d'#' -f1 | \
awk '{gsub("-", " "); print $0}' | \
awk '{for(i=1;i<=NF;i++)if(i==1){$i=tolower($i)}else{$i=toupper(substr($i,1,1)) tolower(substr($i,2))};}1' | \
sed 's/ //g' | tr '[:upper:]' '[:lower:]')
fi
case "$param_for_type" in
"value") param_type="String";;
"n") param_type="Int";;
"path") param_type="String";;
"vers") param_type="String";;
"regex") param_type="String";;
"format") param_type="String";;
"mode") param_type="String";;
"level") param_type="String";;
"type") param_type="String";;
"check") param_type="String";;
"enforcement") param_type="String";;
*) param_type="String";;
esac
else
value_property_name="value"
fi

# Generate Swift file
{
echo "/// Passes the flag \`$original_flag\`"
if [ ! -z "$description" ]; then
capitalized_desc=$(echo "$description" | sed 's/^[[:lower:]]/\U&/')
echo "/// $capitalized_desc"
fi

if [ ! -z "$parameter" ]; then
echo "public struct $camel_case_flag: $protocol_name {"
echo " public let $value_property_name: $param_type"
echo ""
echo " public init(_ $value_property_name: $param_type) {"
echo " self.$value_property_name = $value_property_name"
echo " }"
echo ""
echo " public var $property_name: [String] {"
if [[ "$original_flag" == *"="* ]]; then
echo " [\"\(name.camelToSnakeCaseFlag())=\($value_property_name)\"]"
else
echo " [\"\(name.camelToSnakeCaseFlag())\", \"\($value_property_name)\"]"
fi
echo " }"
echo "}"
else
echo "public struct $camel_case_flag: $protocol_name { }"
fi
} > "$output_directory/$output_subdir/$camel_case_flag.swift"

echo "File '$output_directory/$output_subdir/$camel_case_flag.swift' created."
done <<< "$filtered_flags"
}

# Get the help outputs
help_output=$(swiftc --help-hidden)
frontend_help=$(swiftc -frontend -help-hidden)

# Create array for unsafe flags
declare -A unsafe_flags

# Process regular flags
filtered_flags=$(filter_help_output "$help_output")
process_flags "$filtered_flags" "false" "" "UnsafeFlag" "unsafeFlagArguments"

# Process frontend flags
while IFS= read -r line; do
# Split the line into flag and description
original_flag=$(echo "$line" | cut -d'|' -f1)
description=$(echo "$line" | cut -d'|' -f2)

# Check if this flag already exists as an unsafe flag (without the leading -)
flag_name=${original_flag#-}
if [ "${unsafe_flags[$flag_name]}" = "1" ]; then
echo "Skipping frontend flag '$original_flag' as it already exists as an unsafe flag"
continue
fi

# Convert the flag to CamelCase
camel_case_flag=$(echo "$original_flag" | awk '{gsub("-", " "); print $0}' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2)); print}' | sed 's/ //g')

# Write the flag and its CamelCase version to a separate file in the Frontend directory
{
echo "/// Passes the flag \`$original_flag\`"
if [ ! -z "$description" ]; then
# Capitalize first letter and ensure space after ///
capitalized_desc=$(echo "$description" | sed 's/^[[:lower:]]/\U&/')
echo "/// $capitalized_desc"
fi
echo "public struct $camel_case_flag: FrontendFlag { }"
} > "$output_directory/Frontend/$camel_case_flag.swift"

echo "File '$output_directory/Frontend/$camel_case_flag.swift' created."
done <<< "$filtered_frontend_flags"
filtered_frontend_flags=$(filter_help_output "$frontend_help")
process_flags "$filtered_frontend_flags" "true" "Frontend" "FrontendFlag" "flagArguments"
12 changes: 12 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/AccessNotesPath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Passes the flag `-access-notes-path`
public struct AccessNotesPath: UnsafeFlag {
public let value: String

public init(_ value: String) {
self.value = value
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(value)"]
}
}
12 changes: 12 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/AllowableClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Passes the flag `-allowable-client`
public struct AllowableClient: UnsafeFlag {
public let vers: String

public init(_ vers: String) {
self.vers = vers
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(vers)"]
}
}
12 changes: 12 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/ApiDiffDataDir.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Passes the flag `-api-diff-data-dir`
public struct ApiDiffDataDir: UnsafeFlag {
public let path: String

public init(_ path: String) {
self.path = path
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(path)"]
}
}
12 changes: 12 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/ApiDiffDataFile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Passes the flag `-api-diff-data-file`
public struct ApiDiffDataFile: UnsafeFlag {
public let path: String

public init(_ path: String) {
self.path = path
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(path)"]
}
}
13 changes: 13 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/AssertConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Passes the flag `-assert-config`
/// Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement.
public struct AssertConfig: UnsafeFlag {
public let value: String

public init(_ value: String) {
self.value = value
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(value)"]
}
}
13 changes: 13 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/CasPath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Passes the flag `-cas-path`
/// Path to CAS
public struct CasPath: UnsafeFlag {
public let path: String

public init(_ path: String) {
self.path = path
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(path)"]
}
}
12 changes: 12 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/CasPluginOption.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// Passes the flag `-cas-plugin-option`
public struct CasPluginOption: UnsafeFlag {
public let name: String

public init(_ name: String) {
self.name = name
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(name)"]
}
}
13 changes: 13 additions & 0 deletions Sources/PackageDSL/SwiftSettings/UnsafeFlags/CasPluginPath.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Passes the flag `-cas-plugin-path`
/// Path to CAS Plugin
public struct CasPluginPath: UnsafeFlag {
public let path: String

public init(_ path: String) {
self.path = path
}

public var unsafeFlagArguments: [String] {
["\(name.camelToSnakeCaseFlag())", "\(path)"]
}
}
Loading

0 comments on commit ee48f66

Please sign in to comment.