-
Notifications
You must be signed in to change notification settings - Fork 169
Add style checks and fix what they exposed #333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
661f2a3
Reflow comments and reformat the shell scripts
jserv 32795b3
Check style with clang-format 20 and commentflow
jserv ba650c6
Give the sanitizer build its own objects
jserv 7ceb593
Expand a macro whose replacement list is empty
jserv 18cbcae
Stop suppressing warnings, and fix what they hid
jserv 8672866
Split the four functions nobody can read at once
jserv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # Verify, or with --write impose, commentflow reflow of the comment blocks. | ||
|
|
||
| set -uo pipefail | ||
|
|
||
| source "$(dirname "${BASH_SOURCE[0]}")/common.sh" || exit 2 | ||
|
|
||
| write=0 | ||
| case "${1:-}" in | ||
| --check) shift ;; | ||
| --write) | ||
| write=1 | ||
| shift | ||
| ;; | ||
| esac | ||
|
|
||
| COMMENTFLOW=${COMMENTFLOW:-commentflow} | ||
|
|
||
| files=() | ||
| if [ "$#" -gt 0 ]; then | ||
| files=("$@") | ||
| else | ||
| require_repo | ||
| collect_files '*.c' '*.h' '*.sh' | ||
| files=(${FILES[@]+"${FILES[@]}"}) | ||
| fi | ||
|
|
||
| [ "${#files[@]}" -gt 0 ] || exit 0 | ||
| if ! command -v "$COMMENTFLOW" > /dev/null 2>&1; then | ||
| echo "Error: $COMMENTFLOW not found" >&2 | ||
| echo "Install it from https://github.com/sysprog21/commentflow" >&2 | ||
| exit 2 | ||
| fi | ||
| if [ "$write" -eq 1 ]; then | ||
| exec "$COMMENTFLOW" -- "${files[@]}" | ||
| fi | ||
|
|
||
| status=0 | ||
| "$COMMENTFLOW" --check -- "${files[@]}" || status=$? | ||
| if [ "$status" -eq 1 ]; then | ||
| echo "Run 'make indent' to reflow comments." >&2 | ||
| fi | ||
| exit "$status" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,61 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| SOURCES=$(find $(git rev-parse --show-toplevel) | egrep "\.(c|cxx|cpp|h|hpp)\$") | ||
| # Verify, or with --write impose, clang-format conformance for the C sources. | ||
|
|
||
| set -x | ||
| set -uo pipefail | ||
|
|
||
| for file in ${SOURCES}; | ||
| do | ||
| clang-format-18 ${file} > expected-format | ||
| diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format | ||
| source "$(dirname "${BASH_SOURCE[0]}")/common.sh" || exit 2 | ||
|
|
||
| write=0 | ||
| case "${1:-}" in | ||
| --check) shift ;; | ||
| --write) | ||
| write=1 | ||
| shift | ||
| ;; | ||
| esac | ||
|
|
||
| CLANG_FORMAT=$(find_clang_format) || { | ||
| echo "Error: clang-format version 20 is required" >&2 | ||
| exit 2 | ||
| } | ||
|
|
||
| files=() | ||
| if [ "$#" -gt 0 ]; then | ||
| files=("$@") | ||
| else | ||
| require_repo | ||
| collect_files '*.c' '*.h' | ||
| files=(${FILES[@]+"${FILES[@]}"}) | ||
| fi | ||
|
|
||
| [ "${#files[@]}" -gt 0 ] || exit 0 | ||
| if [ "$write" -eq 1 ]; then | ||
| exec "$CLANG_FORMAT" -i "${files[@]}" | ||
| fi | ||
|
|
||
| # One batched pass answers "is anything unformatted" in a third of the time the | ||
| # per-file loop below takes. The loop only has to run when the answer is yes, | ||
| # and then only to produce the diff that says what to change. | ||
| list=$(mktemp) || exit 2 | ||
| trap 'rm -f "$list"' EXIT | ||
| printf '%s\n' "${files[@]}" > "$list" | ||
| "$CLANG_FORMAT" --dry-run -Werror --files="$list" > /dev/null 2>&1 && exit 0 | ||
|
|
||
| failed=0 | ||
| expected=$(mktemp) || exit 2 | ||
| trap 'rm -f "$list" "$expected"' EXIT | ||
| for file in "${files[@]}"; do | ||
|
|
||
| # An index entry with no file behind it -- a sparse checkout, or a deletion | ||
| # staged but not yet committed -- is nothing to format. | ||
| [ -f "$file" ] || continue | ||
| if ! "$CLANG_FORMAT" "$file" > "$expected"; then | ||
| echo "Error: $CLANG_FORMAT failed on $file" >&2 | ||
| exit 1 | ||
| fi | ||
| diff -u -p --label="$file" --label="expected coding style" \ | ||
| "$file" "$expected" || failed=1 | ||
| done | ||
| exit $(clang-format-18 --output-replacements-xml ${SOURCES} | egrep -c "</replacement>") | ||
|
|
||
| exit "$failed" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,88 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| ret=0 | ||
| show=0 | ||
| # Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e | ||
| while IFS= read -rd '' f; do | ||
| if file --mime-encoding "$f" | grep -qv binary; then | ||
| tail -c1 < "$f" | read -r _ || show=1 | ||
| if [ $show -eq 1 ]; then | ||
| echo "Warning: No newline at end of file $f" | ||
| ret=1 | ||
| show=0 | ||
| fi | ||
| # Ensure every text file uses LF line endings and ends with a newline, which is | ||
| # what the [*] section of .editorconfig asks editors to do. | ||
|
|
||
| set -uo pipefail | ||
|
|
||
| source "$(dirname "${BASH_SOURCE[0]}")/common.sh" || exit 2 | ||
|
|
||
| write=0 | ||
| case "${1:-}" in | ||
| --check) shift ;; | ||
| --write) | ||
| write=1 | ||
| shift | ||
| ;; | ||
| esac | ||
|
|
||
| files=() | ||
| if [ "$#" -gt 0 ]; then | ||
| files=("$@") | ||
| else | ||
| require_repo | ||
| collect_files | ||
| files=(${FILES[@]+"${FILES[@]}"}) | ||
| fi | ||
|
|
||
| [ "${#files[@]}" -gt 0 ] || exit 0 | ||
| if ! command -v file > /dev/null 2>&1; then | ||
| echo "Error: file not found" >&2 | ||
| exit 2 | ||
| fi | ||
|
|
||
| # One file(1) run for the whole set: it spends most of its time loading | ||
| # libmagic, so paying that once rather than per file is the difference between | ||
| # the newline check dominating "make check-style" and disappearing into it. | ||
| encodings=() | ||
| while IFS= read -r encoding; do | ||
| encodings+=("$encoding") | ||
| done < <(file -b --mime-encoding -- "${files[@]}") | ||
|
|
||
| # Answers are matched to inputs by position, so a reply that ran short would | ||
| # quietly reclassify the rest of the tree as text and report nonsense about it. | ||
| if [ "${#encodings[@]}" -ne "${#files[@]}" ]; then | ||
| echo "Error: file described ${#encodings[@]} of ${#files[@]} files" >&2 | ||
| exit 2 | ||
| fi | ||
|
|
||
| text=() | ||
| for i in "${!files[@]}"; do | ||
| [ "${encodings[i]}" = binary ] && continue | ||
| text+=("${files[i]}") | ||
| done | ||
| [ "${#text[@]}" -gt 0 ] || exit 0 | ||
|
|
||
| failed=0 | ||
| for path in "${text[@]}"; do | ||
| last=$(tail -c1 < "$path") || exit 2 | ||
| [ -n "$last" ] || continue | ||
| if [ "$write" -eq 1 ]; then | ||
| printf '\n' >> "$path" | ||
| continue | ||
| fi | ||
| done < <(git ls-files -z src tools tests) | ||
| echo "No newline at end of file: $path" >&2 | ||
| failed=1 | ||
| done | ||
|
|
||
| # grep exits 0 having found a carriage return, 1 having found none, and 2 on a | ||
| # real error, which says nothing about the tree and must not read as clean. Its | ||
| # answer goes through a file because the names it prints are NUL separated and a | ||
| # command substitution would drop the separators along with them. | ||
| matches=$(mktemp) || exit 2 | ||
| trap 'rm -f "$matches"' EXIT | ||
| grep -lZ $'\r' -- "${text[@]}" > "$matches" | ||
| status=$? | ||
| if [ "$status" -gt 1 ]; then | ||
| echo "Error: grep failed to scan for carriage returns" >&2 | ||
| exit 2 | ||
| fi | ||
| while IFS= read -r -d '' path; do | ||
|
|
||
| # Reported even under --write: a carriage return sits inside the text, and | ||
| # stripping one is an edit to content rather than to layout. | ||
| echo "CRLF line ending: $path" >&2 | ||
| failed=1 | ||
| done < "$matches" | ||
|
|
||
| exit $ret | ||
| exit "$failed" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # Verify, or with --write impose, shfmt formatting for the shell scripts, and | ||
| # lint them with ShellCheck. | ||
|
|
||
| set -uo pipefail | ||
|
|
||
| source "$(dirname "${BASH_SOURCE[0]}")/common.sh" || exit 2 | ||
|
|
||
| write=0 | ||
| case "${1:-}" in | ||
| --check) shift ;; | ||
| --write) | ||
| write=1 | ||
| shift | ||
| ;; | ||
| esac | ||
|
|
||
| SHFMT=${SHFMT:-shfmt} | ||
| SHELLCHECK=${SHELLCHECK:-shellcheck} | ||
|
|
||
| files=() | ||
| if [ "$#" -gt 0 ]; then | ||
| files=("$@") | ||
| else | ||
| require_repo | ||
| collect_files '*.sh' | ||
| files=(${FILES[@]+"${FILES[@]}"}) | ||
| fi | ||
|
|
||
| [ "${#files[@]}" -gt 0 ] || exit 0 | ||
| if ! command -v "$SHFMT" > /dev/null 2>&1; then | ||
| echo "Error: $SHFMT not found" >&2 | ||
| exit 2 | ||
| fi | ||
| if [ "$write" -eq 1 ]; then | ||
| exec "$SHFMT" -w -- "${files[@]}" | ||
| fi | ||
|
|
||
| failed=0 | ||
| "$SHFMT" -d -- "${files[@]}" || failed=1 | ||
|
|
||
| # The test scripts embed shecc expressions that read as shell and trip | ||
| # ShellCheck. Naming what is exempt rather than what is covered keeps a new | ||
| # directory linted by default instead of silently skipped. | ||
| lint_files=() | ||
| for file in "${files[@]}"; do | ||
| case "$file" in | ||
| tests/*) ;; | ||
| *) lint_files+=("$file") ;; | ||
| esac | ||
| done | ||
|
|
||
| [ "${#lint_files[@]}" -gt 0 ] || exit "$failed" | ||
| if ! command -v "$SHELLCHECK" > /dev/null 2>&1; then | ||
| echo "Error: $SHELLCHECK not found" >&2 | ||
|
|
||
| # A formatting violation shfmt already found is a verdict on the tree, and | ||
| # outranks the linter that could not run: reporting 2 here would file it | ||
| # under "unavailable" and let the pre-commit hook wave it through. | ||
| [ "$failed" -eq 0 ] || exit "$failed" | ||
| exit 2 | ||
| fi | ||
| "$SHELLCHECK" --severity=warning -- "${lint_files[@]}" || failed=1 | ||
|
|
||
| exit "$failed" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # Shared helpers for the style checks. Sourced, not executed. | ||
| # | ||
| # Every .ci/check-*.sh honors the same exit-code contract, which | ||
| # scripts/git-pre-commit.sh depends on to stay advisory: 0 when clean, 1 when | ||
| # the tree violates the rule, and 2 when the check could not run at all, which | ||
| # in practice means a missing tool. Only 1 blocks a commit. | ||
|
|
||
| # Abort unless the current directory sits inside a git repository. Without this | ||
| # the checks below find no files and report success, which reads exactly like a | ||
| # clean tree. | ||
| require_repo() | ||
| { | ||
| if ! git rev-parse --git-dir > /dev/null 2>&1; then | ||
| echo "Error: not a git repository" >&2 | ||
| exit 2 | ||
| fi | ||
| } | ||
|
|
||
| # Fill the FILES array with every file matching the given pathspecs that git | ||
| # would either track or add, so that a new file is checked by the same rule that | ||
| # formats it. | ||
| # | ||
| # The listing goes through a temporary file rather than a process substitution | ||
| # because git's exit status has to be read in this shell: inside "< <(...)" a | ||
| # failure exits only the subshell, the loop sees end of input, and an | ||
| # enumeration that never happened arrives as an empty list, which every checker | ||
| # reads as a clean tree. | ||
| # | ||
| # An index entry can also outlive its file, during a staged deletion or in a | ||
| # sparse checkout, and there is nothing for any checker to read at that path. | ||
| collect_files() | ||
| { | ||
| local listing status file | ||
| listing=$(mktemp) || exit 2 | ||
|
jserv marked this conversation as resolved.
|
||
| trap 'rm -f "$listing"' EXIT | ||
| git ls-files -z --cached --others --exclude-standard -- "$@" > "$listing" | ||
| status=$? | ||
| FILES=() | ||
| if [ "$status" -ne 0 ]; then | ||
| echo "Error: could not enumerate the worktree" >&2 | ||
| exit 2 | ||
| fi | ||
| while IFS= read -r -d '' file; do | ||
|
|
||
| # -f rather than -e: a submodule gitlink and a symlink to a directory | ||
| # are both entries in the index that no file-oriented checker can read. | ||
| [ -f "$file" ] || continue | ||
| FILES+=("$file") | ||
| done < "$listing" | ||
| rm -f "$listing" | ||
| trap - EXIT | ||
| } | ||
|
|
||
| # Print the name of a clang-format at the version the project pins, or nothing. | ||
| # CLANG_FORMAT names a specific binary to use instead of searching. | ||
| find_clang_format() | ||
| { | ||
| local candidate candidates | ||
| if [ -n "${CLANG_FORMAT:-}" ]; then | ||
| candidates=("$CLANG_FORMAT") | ||
| else | ||
| candidates=(clang-format-20 clang-format) | ||
| fi | ||
| for candidate in "${candidates[@]}"; do | ||
| if command -v "$candidate" > /dev/null 2>&1 \ | ||
| && "$candidate" --version 2> /dev/null | grep -qE 'version 20\.'; then | ||
| echo "$candidate" | ||
| return 0 | ||
| fi | ||
| done | ||
| return 1 | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Top-level EditorConfig file | ||
| root = true | ||
|
|
||
| # Enforced repository-wide by .ci/check-newline.sh | ||
| [*] | ||
| end_of_line = lf | ||
| insert_final_newline = true | ||
|
|
||
| # Matching .clang-format's "UseTab: Never" and "IndentWidth: 4" | ||
| [*.{c,h}] | ||
| indent_style = space | ||
| indent_size = 4 | ||
| trim_trailing_whitespace = true | ||
|
|
||
| # Shell adds the shfmt-specific keys, which no editor reads but which keep | ||
| # "shfmt -d" and "shfmt -w" agreeing without duplicating flags in the Makefile. | ||
| [*.sh] | ||
| indent_style = space | ||
| indent_size = 4 | ||
| trim_trailing_whitespace = true | ||
| function_next_line = true | ||
| switch_case_indent = true | ||
| space_redirects = true | ||
| binary_next_line = true |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.