diff --git a/.arclint b/.arclint --- a/.arclint +++ b/.arclint @@ -46,6 +46,10 @@ "exclude": [ "(^test/lint/lint-python-format\\.py$)" ] + }, + "lint-locale-dependence": { + "type": "lint-locale-dependence", + "include": "(^src/.*\\.(h|cpp)$)" } } } diff --git a/arcanist/.phutil_module_cache b/arcanist/.phutil_module_cache --- a/arcanist/.phutil_module_cache +++ b/arcanist/.phutil_module_cache @@ -1 +1 @@ -{"__symbol_cache_version__":11,"d60c8224f471e0ecddc2a6f3c6839cd1":{"have":{"class":{"AutoPEP8FormatLinter":75}},"need":{"function":{"pht":297,"id":1317},"class":{"ArcanistExternalLinter":104,"ArcanistLintMessage":1324,"Filesystem":1168,"ArcanistLinter":1431,"ArcanistLintSeverity":1509}},"xmap":{"AutoPEP8FormatLinter":["ArcanistExternalLinter"]}},"213c3145da34ed6dfc0d70d628a2a086":{"have":{"class":{"CheckDocLinter":106}},"need":{"function":{"pht":323,"id":1868},"class":{"ArcanistExternalLinter":129,"ArcanistLintMessage":1875,"Filesystem":737,"ArcanistLinter":1923,"ArcanistLintSeverity":2009}},"xmap":{"CheckDocLinter":["ArcanistExternalLinter"]}},"7bab1f879b8a86dd9977b8c0d075935f":{"have":{"class":{"ClangFormatLinter":79}},"need":{"function":{"pht":302,"execx":787,"id":1664},"class":{"ArcanistExternalLinter":105,"ArcanistLintMessage":1671,"Filesystem":1515,"ArcanistLinter":1778,"ArcanistLintSeverity":1856}},"xmap":{"ClangFormatLinter":["ArcanistExternalLinter"]}},"0e068a1116ed03e86a2388020a821983":{"have":{"class":{"PythonFormatLinter":75}},"need":{"function":{"pht":305,"id":1614},"class":{"ArcanistExternalLinter":102,"ArcanistLintMessage":1621,"Filesystem":733,"ArcanistLinter":1752,"ArcanistLintSeverity":1835}},"xmap":{"PythonFormatLinter":["ArcanistExternalLinter"]}},"ea2beb1668dfbdd87488f18fbb20178f":{"have":{"class":{"TestsLinter":103}},"need":{"function":{"pht":318,"id":2676},"class":{"ArcanistExternalLinter":123,"ArcanistLintMessage":2683,"Filesystem":791,"ArcanistLinter":2731,"ArcanistLintSeverity":2839}},"xmap":{"TestsLinter":["ArcanistExternalLinter"]}}} \ No newline at end of file +{"__symbol_cache_version__":11,"d60c8224f471e0ecddc2a6f3c6839cd1":{"have":{"class":{"AutoPEP8FormatLinter":75}},"need":{"function":{"pht":297,"id":1317},"class":{"ArcanistExternalLinter":104,"ArcanistLintMessage":1324,"Filesystem":1168,"ArcanistLinter":1431,"ArcanistLintSeverity":1509}},"xmap":{"AutoPEP8FormatLinter":["ArcanistExternalLinter"]}},"213c3145da34ed6dfc0d70d628a2a086":{"have":{"class":{"CheckDocLinter":106}},"need":{"function":{"pht":323,"id":1868},"class":{"ArcanistExternalLinter":129,"ArcanistLintMessage":1875,"Filesystem":737,"ArcanistLinter":1923,"ArcanistLintSeverity":2009}},"xmap":{"CheckDocLinter":["ArcanistExternalLinter"]}},"7bab1f879b8a86dd9977b8c0d075935f":{"have":{"class":{"ClangFormatLinter":79}},"need":{"function":{"pht":302,"execx":787,"id":1664},"class":{"ArcanistExternalLinter":105,"ArcanistLintMessage":1671,"Filesystem":1515,"ArcanistLinter":1778,"ArcanistLintSeverity":1856}},"xmap":{"ClangFormatLinter":["ArcanistExternalLinter"]}},"ea2beb1668dfbdd87488f18fbb20178f":{"have":{"class":{"TestsLinter":103}},"need":{"function":{"pht":318,"id":2676},"class":{"ArcanistExternalLinter":123,"ArcanistLintMessage":2683,"Filesystem":791,"ArcanistLinter":2731,"ArcanistLintSeverity":2839}},"xmap":{"TestsLinter":["ArcanistExternalLinter"]}},"d8eaa2e7c86750dc3ee35036b5fd8bfc":{"have":{"class":{"LocaleDependenceLinter":137}},"need":{"function":{"pht":375,"id":2052},"class":{"ArcanistExternalLinter":168,"ArcanistLintMessage":2059,"Filesystem":816,"ArcanistLinter":2107,"ArcanistLintSeverity":2227}},"xmap":{"LocaleDependenceLinter":["ArcanistExternalLinter"]}},"e3132d407656d565ce80d359f4f0fe5f":{"have":{"class":{"PythonFormatLinter":124}},"need":{"function":{"pht":354,"id":1845},"class":{"ArcanistExternalLinter":151,"ArcanistLintMessage":1852,"Filesystem":777,"ArcanistLinter":1977,"ArcanistLintSeverity":2060}},"xmap":{"PythonFormatLinter":["ArcanistExternalLinter"]}}} \ No newline at end of file diff --git a/arcanist/__phutil_library_map__.php b/arcanist/__phutil_library_map__.php --- a/arcanist/__phutil_library_map__.php +++ b/arcanist/__phutil_library_map__.php @@ -12,6 +12,7 @@ 'AutoPEP8FormatLinter' => 'linter/AutoPEP8Linter.php', 'CheckDocLinter' => 'linter/CheckDocLinter.php', 'ClangFormatLinter' => 'linter/ClangFormatLinter.php', + 'LocaleDependenceLinter' => 'linter/LocalDependenceLinter.php', 'PythonFormatLinter' => 'linter/PythonFormatLinter.php', 'TestsLinter' => 'linter/TestsLinter.php', ), @@ -20,6 +21,7 @@ 'AutoPEP8FormatLinter' => 'ArcanistExternalLinter', 'CheckDocLinter' => 'ArcanistExternalLinter', 'ClangFormatLinter' => 'ArcanistExternalLinter', + 'LocaleDependenceLinter' => 'ArcanistExternalLinter', 'PythonFormatLinter' => 'ArcanistExternalLinter', 'TestsLinter' => 'ArcanistExternalLinter', ), diff --git a/arcanist/linter/LocalDependenceLinter.php b/arcanist/linter/LocalDependenceLinter.php new file mode 100644 --- /dev/null +++ b/arcanist/linter/LocalDependenceLinter.php @@ -0,0 +1,131 @@ +getProjectRoot()); + } + + public function shouldUseInterpreter() { + return true; + } + + public function getDefaultInterpreter() { + return "bash"; + } + + public function getInstallInstructions() { + return pht('The test/lint/lint-locale-dependence.sh script is part of the + bitcoin-abc project. Requires git >= 2.6.5.'); + } + + public function shouldExpectCommandErrors() { + return false; + } + + protected function getMandatoryFlags() { + return array( + ); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + /* + * Stdout contains 2 sections: + * 1/ The locale dependent function in a sentence: + * The locale dependent function (...) appears to be used + * Followed by the file and the corresponding code lines + * : + * 2/ Section with a general warning, will not be used by Arcanist. Instead + * we define our own advice message. + * + * First a list of the locale dependent functions will be determined, then + * the code lines containing these functions. + * An error message is generated for each line of code. + */ + + /* + * Extract infos from the path + */ + $pathinfo = pathinfo($path); + $fileName = $pathinfo['basename']; + + $messages = []; + + /* Find the functions */ + $pattern = '/The locale dependent function (\w+)\(...\)/'; + if (!preg_match_all($pattern, $stdout, $matches, $flags = PREG_SET_ORDER)) { + return $messages; + } + + $functions = []; + foreach($matches as $match) { + $functions[] = $match[1]; + } + + /* Find the code lines */ + $pattern = '/'.$fileName.':(\d+): (.+)/'; + if (preg_match_all($pattern, $stdout, $matches, $flags = PREG_SET_ORDER)) { + foreach ($matches as $match) { + list(, $lineNumber, $codeSnippet) = $match; + + /* Determine which function is used */ + $functionUsed = ''; + foreach($functions as $function) { + if (strpos($codeSnippet, $function) !== FALSE) { + $functionUsed = $function; + break; + } + } + + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('LOCALE_DEPENDENCE') + ->setPath($path) + ->setLine($lineNumber) + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Locale dependent function '.$functionUsed) + ->setDescription(self::ADVICE_MESSAGE); + } + } + + return $messages; + } +} diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -197,9 +197,9 @@ # Invoke "git grep" only once in order to minimize run-time REGEXP_LOCALE_DEPENDENT_FUNCTIONS=$(join_array "|" "${LOCALE_DEPENDENT_FUNCTIONS[@]}") -GIT_GREP_OUTPUT=$(git grep -nE "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FUNCTIONS}(_r|_s)?)[^a-zA-Z0-9_\`'\"<>]" -- ":/*.cpp" ":/*.h") +GIT_GREP_OUTPUT=$(git grep -nE "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FUNCTIONS}(_r|_s)?)[^a-zA-Z0-9_\`'\"<>]" -- "$1") -EXIT_CODE=0 +FUNCTION_FOUND=0 for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \ grep -vE "\.(c|cpp|h):[0-9]+:\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}") @@ -213,10 +213,10 @@ echo "The locale dependent function ${LOCALE_DEPENDENT_FUNCTION}(...) appears to be used:" echo "${MATCHES}" echo - EXIT_CODE=1 + FUNCTION_FOUND=1 fi done -if [[ ${EXIT_CODE} != 0 ]]; then +if [[ ${FUNCTION_FOUND} != 0 ]]; then echo "Unnecessary locale dependence can cause bugs that are very" echo "tricky to isolate and fix. Please avoid using locale dependent" echo "functions if possible." @@ -224,4 +224,4 @@ echo "Advice not applicable in this specific case? Add an exception" echo "by updating the ignore list in $0" fi -exit ${EXIT_CODE} +exit 0