Created
January 28, 2016 20:22
-
-
Save arxanas/f2795068513572430830 to your computer and use it in GitHub Desktop.
inclint
This file contains 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
#!/bin/bash | |
# Linter for C++ imports (`using`s). | |
# | |
# Features: | |
# * Ensures that you don't use `std::foo` directly in your code, but rather | |
# mandates that you do `using std::foo` elsewhere. | |
# * Ensures that for every `using std::foo`, `foo` is used somewhere in your | |
# code. This is the killer feature because it helps you keep your `using`s | |
# in-sync with your actual code. | |
# * Ensures that includes are in alphabetical order. | |
# * Ensures that your header files don't use `using` statements. | |
# | |
# Bugs: | |
# * Is written in bash. | |
# * Nothing else. There are no possible bugs arising from parsing C++ with | |
# grep. | |
# | |
# Usage: | |
# * inclint foo.cpp bar.h | |
# * inclint # runs recursively on all source files found in the current dir | |
set -euo pipefail | |
IFS=$'\n\t' | |
readonly ID_REGEX="[a-zA-Z_][a-zA-Z_0-9]*" | |
failed='false' | |
check_includes_alphabetical_order() { | |
local file="$1" | |
compare_includes() { | |
local expected="$1" | |
local actual="$2" | |
if ! diff -q <(echo "$actual") <(echo "$expected") >/dev/null; then | |
echo "$file: includes in wrong order; should be" | |
echo '~~~' | |
diff -y <(echo "$actual") <(echo "$expected") || true | |
echo '~~~' | |
failed=true | |
fi | |
} | |
local quote_includes | |
local sorted_quote_includes | |
quote_includes=$(grep -E '#include \"' "$file" || true) | |
sorted_quote_includes=$(sort <<<"$quote_includes") | |
compare_includes "$sorted_quote_includes" "$quote_includes" | |
local angle_includes | |
local sorted_angle_includes | |
angle_includes=$(grep -E '#include <' "$file" || true) | |
sorted_angle_includes=$(sort <<<"$angle_includes") | |
compare_includes "$sorted_angle_includes" "$angle_includes" | |
} | |
check_no_unused_using_statements() { | |
local file="$1" | |
local using_statements | |
using_statements=$(grep -E "^using std::$ID_REGEX" "$file" || true) | |
local symbols | |
symbols=$(cut -d':' -f3 <<<"$using_statements" | sed 's/;//g') | |
_filter_comments() { | |
grep -vE '^(\s*//|#)' | |
} | |
local i | |
for i in $symbols; do | |
if ! _filter_comments <"$file" | grep -qE "[^:]$i"; then | |
echo "$file: unused symbol $i" | |
failed='true' | |
fi | |
done | |
} | |
check_no_std_members() { | |
local file="$1" | |
local std_members | |
std_members=$( ( | |
grep "std::$ID_REGEX" | | |
grep -v using | | |
sed "s/.*\(std::$ID_REGEX\).*/\1/g" | |
) || true) | |
local i | |
for i in $std_members; do | |
i=$(sed 's/^[ \t]*//g' <<<"$i") | |
echo "$file: don't write '$i' inline in your code, use 'using $i' instead"; | |
failed='true' | |
done | |
} | |
check_doesnt_use_using_statements() { | |
local file="$i" | |
if grep -qE '^\s*using'; then | |
echo "$file: don't use using-statements in header files" | |
failed='true' | |
fi | |
} | |
preprocess() { | |
local file="$1" | |
# http://stackoverflow.com/a/2394040/344643 | |
g++ -fpreprocessed -dD -E "$file" | |
} | |
main() { | |
local files | |
if [[ "$#" -ge 1 ]]; then | |
files="$*" | |
else | |
files=$(find -E . -type f -iregex '.+\.(c|cc|cpp|h|hpp)') | |
fi | |
local i | |
for i in $files; do | |
check_includes_alphabetical_order "$i" <"$i" | |
if [[ "$i" = *.cpp ]]; then | |
preprocess "$i" | check_no_unused_using_statements "$i" | |
preprocess "$i" | check_no_std_members "$i" | |
else | |
preprocess "$i" | check_doesnt_use_using_statements "$i" | |
fi | |
done | |
if [[ "$failed" == 'true' ]]; then | |
exit 1 | |
else | |
exit 0 | |
fi | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment