utils/shfmt.sh: implement checkers for forbidden styles

This commit is contained in:
XuehaiPan 2021-09-15 23:40:14 +08:00
parent 03c7a142be
commit 09a16bcea1

View File

@ -83,14 +83,23 @@ fi
# #
no_tabs() { no_tabs() {
local file="$1" local file="$1"
local tempfile="$2"
local line
local num=0
local retcode=0
local regex_pos='^[[:space:]]+'
local regex_neg='^ +'
# TODO: use bash built-in regex match syntax instead while IFS='' read -r line
if grep -qE '^\t+' "${file}" do
then num="$((num + 1))"
# TODO: add line number if [[ "${line}" =~ ${regex_pos} && ! "${line}" =~ ${regex_neg} ]]
onoe "Indent by tab detected." then
return 1 onoe "Indent by tab detected at \"${file}\", line ${num}."
fi retcode=1
fi
done <"${file}"
return "${retcode}"
} }
# Check pattern: # Check pattern:
@ -98,56 +107,98 @@ no_tabs() {
# ...; do # ...; do
# #
# Use the followings instead (keep for statements only one line): # Use the followings instead (keep for statements only one line):
# ARRAY=( # ARRAY=(
# ... # ...
# ... # )
# ) # for var in "${ARRAY[@]}"
# for var in "${ARRAY[@]}" # do
# do
# #
no_multiline_for_statements() { no_multiline_for_statements() {
local file="$1" local file="$1"
local tempfile="$2"
local line
local num=0
local retcode=0
local regex='^ *for [_[:alnum:]]+ in .*\\$'
# TODO: use bash built-in regex match syntax instead while IFS='' read -r line
if grep -qE '^\s*for .*\\\(#.*\)\?$' "${file}" do
then num="$((num + 1))"
# TODO: add line number if [[ "${line}" =~ ${regex} ]]
onoe "Multi-line for statement detected." then
return 1 onoe "Multiline for statement detected at \"${file}\", line ${num}."
fi cat >&2 <<EOMSG
Use the followings instead (keep for statements only one line):
ARRAY=(
...
)
for var in "\${ARRAY[@]}"
do
...
done
EOMSG
retcode=1
fi
done <"${file}"
return "${retcode}"
} }
# Check pattern: # Check pattern:
# IFS=$'\n' # IFS=$'\n'
# #
# Use the followings instead: # Use the followings instead:
# while IFS='' read -r line # while IFS='' read -r line
# do # do
# ... # ...
# done < <(command) # done < <(command)
# #
no_IFS_newline() { no_IFS_newline() {
local file="$1" local file="$1"
local tempfile="$2"
local line
local num=0
local retcode=0
local regex="^[^#]*IFS=\\\$'\\\\n'"
# TODO: use bash built-in regex match syntax instead while IFS='' read -r line
if grep -qE "^[^#]*IFS=\\\$'\\\\n'" "${file}" do
then num="$((num + 1))"
# TODO: add line number if [[ "${line}" =~ ${regex} ]]
onoe "Pattern \`IFS=\$'\\\\n'\` detected." then
return 1 onoe "Pattern \`IFS=\$'\\n'\` detected at \"${file}\", line ${num}."
fi cat >&2 <<EOMSG
Use the followings instead:
while IFS='' read -r line
do
...
done < <(command)
EOMSG
retcode=1
fi
done <"${file}"
return "${retcode}"
}
# Combine all forbidden styles
no_forbiddens() {
local file="$1"
local tempfile="$2"
no_tabs "${file}" "${tempfile}" || return 1
no_multiline_for_statements "${file}" "${tempfile}" || return 1
no_IFS_newline "${file}" "${tempfile}" || return 1
} }
# Align multiline if condition (indent with 3 spaces or 6 spaces (start with "-")) # Align multiline if condition (indent with 3 spaces or 6 spaces (start with "-"))
# before: after: # before: after:
# if [[ ... ]] || if [[ ... ]] || # if [[ ... ]] || if [[ ... ]] ||
# [[ ... ]] [[ ... ]] # [[ ... ]] [[ ... ]]
# then then # then then
# #
# before: after: # before: after:
# if [[ -n ... || \ if [[ -n ... || \ # if [[ -n ... || \ if [[ -n ... || \
# -n ... ]] -n ... ]] # -n ... ]] -n ... ]]
# then then # then then
# #
align_multiline_if_condition() { align_multiline_if_condition() {
local multiline_if_begin_regex='^( *)(el)?if ' local multiline_if_begin_regex='^( *)(el)?if '
@ -188,20 +239,21 @@ align_multiline_if_condition() {
# Wrap `then` and `do` to a separated line # Wrap `then` and `do` to a separated line
# before: after: # before: after:
# if [[ ... ]]; then if [[ ... ]] # if [[ ... ]]; then if [[ ... ]]
# then # then
# #
# before: after: # before: after:
# if [[ ... ]] || if [[ ... ]] || # if [[ ... ]] || if [[ ... ]] ||
# [[ ... ]]; then [[ ... ]] # [[ ... ]]; then [[ ... ]]
# then # then
# #
# before: after: # before: after:
# for var in ...; do for var in ... # for var in ...; do for var in ...
# do # do
# #
wrap_then_do() { wrap_then_do() {
local file="$1" local file="$1"
local tempfile="$2"
local -a processed local -a processed
local line local line
@ -240,9 +292,9 @@ wrap_then_do() {
buffer=() buffer=()
fi fi
fi fi
done < <(cat "${file}") done <"${tempfile}"
printf "%s\n" "${processed[@]}" >"${file}" printf "%s\n" "${processed[@]}" >"${tempfile}"
} }
# TODO: it's hard to align multiline switch cases # TODO: it's hard to align multiline switch cases
@ -250,10 +302,6 @@ align_multiline_switch_cases() {
true true
} }
no_forbiddens() {
true
}
format() { format() {
local file="$1" local file="$1"
if [[ ! -f "${file}" || ! -r "${file}" ]] if [[ ! -f "${file}" || ! -r "${file}" ]]
@ -282,17 +330,16 @@ format() {
fi fi
# Fail fast when forbidden patterns detected # Fail fast when forbidden patterns detected
if ! no_tabs "${tempfile}" || if ! no_forbiddens "${file}" "${tempfile}"
! no_multiline_for_statements "${tempfile}" ||
! no_IFS_newline "${tempfile}"
then then
return 1 return 2
fi fi
# Tweak it with custom shell script styles # Tweak it with custom shell script styles
wrap_then_do "${tempfile}" wrap_then_do "${file}" "${tempfile}"
align_multiline_switch_cases "${file}" "${tempfile}"
if ! diff -q "${file}" "${tempfile}" if ! diff -q "${file}" "${tempfile}" &>/dev/null
then then
# Show differences # Show differences
diff -d -C 1 --color=auto "${file}" "${tempfile}" diff -d -C 1 --color=auto "${file}" "${tempfile}"
@ -314,9 +361,9 @@ do
then then
if [[ "$?" == 1 ]] if [[ "$?" == 1 ]]
then then
onoe "${0##*/}: Failed to format file \"${file}\". Function exited with code $?." onoe "${0##*/}: Failed to format file \"${file}\". Function exited with code 1."
else else
onoe "${0##*/}: Bad style for file \"${file}\". Function exited with code $?." onoe "${0##*/}: Bad style for file \"${file}\". Function exited with code 2."
fi fi
onoe onoe
RETCODE=1 RETCODE=1