From cf8d445c9fa098f1c7ee8540d176c08a46e9473e Mon Sep 17 00:00:00 2001 From: Jack Lindamood Date: Mon, 7 Jan 2013 08:16:45 -0800 Subject: [PATCH] CppCheck linter Summary: Lints cpp code using the cppcheck static linter. This linter needs to be downloaded and built from http://cppcheck.sourceforge.net/ Test Plan: Used it on a few files. Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D4353 --- src/__phutil_library_map__.php | 2 + src/lint/linter/ArcanistCppcheckLinter.php | 137 +++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/lint/linter/ArcanistCppcheckLinter.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 28d1cb48..f80ab6a8 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -36,6 +36,7 @@ phutil_register_library_map(array( 'ArcanistConduitLinter' => 'lint/linter/ArcanistConduitLinter.php', 'ArcanistConfiguration' => 'configuration/ArcanistConfiguration.php', 'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php', + 'ArcanistCppcheckLinter' => 'lint/linter/ArcanistCppcheckLinter.php', 'ArcanistCpplintLinter' => 'lint/linter/ArcanistCpplintLinter.php', 'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php', 'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php', @@ -176,6 +177,7 @@ phutil_register_library_map(array( 'ArcanistCommitWorkflow' => 'ArcanistBaseWorkflow', 'ArcanistConduitLinter' => 'ArcanistLinter', 'ArcanistCoverWorkflow' => 'ArcanistBaseWorkflow', + 'ArcanistCppcheckLinter' => 'ArcanistLinter', 'ArcanistCpplintLinter' => 'ArcanistLinter', 'ArcanistCpplintLinterTestCase' => 'ArcanistArcanistLinterTestCase', 'ArcanistDiffParserTestCase' => 'ArcanistTestCase', diff --git a/src/lint/linter/ArcanistCppcheckLinter.php b/src/lint/linter/ArcanistCppcheckLinter.php new file mode 100644 index 00000000..7fd95ade --- /dev/null +++ b/src/lint/linter/ArcanistCppcheckLinter.php @@ -0,0 +1,137 @@ +getEngine()->getWorkingCopy(); + // You will for sure want some options. The below default tends to be ok + $options = $working_copy->getConfig( + 'lint.cppcheck.options', + '-j2 --inconclusive --enable=performance,style,portability,information' + ); + + return $options; + } + + public function getLintPath() { + $working_copy = $this->getEngine()->getWorkingCopy(); + $prefix = $working_copy->getConfig('lint.cppcheck.prefix'); + $bin = $working_copy->getConfig('lint.cppcheck.bin'); + + if ($bin === null && $prefix === null) { + $bin = 'cppcheck'; + } else { + if ($bin === null) { + $bin = 'cppcheck'; + } + + if ($prefix !== null) { + if (!Filesystem::pathExists($prefix.'/'.$bin)) { + throw new ArcanistUsageException( + "Unable to find cppcheck binary in a specified directory. Make ". + "sure that 'lint.cppcheck.prefix' and 'lint.cppcheck.bin' keys are". + " set correctly. If you'd rather use a copy of cppcheck installed ". + "globally, you can just remove these keys from your .arcconfig"); + } + + $bin = csprintf("%s/%s", $prefix, $bin); + + return $bin; + } + + // Look for globally installed cppcheck + list($err) = exec_manual('which %s', $bin); + if ($err) { + throw new ArcanistUsageException( + "cppcheck does not appear to be installed on this system. Install". + "it (from http://cppcheck.sourceforge.net/) or configure". + "'lint.cppcheck.prefix' in your .arcconfig to point to the". + "directory where it resides." + ); + } + } + + return $bin; + + } + + public function lintPath($path) { + $bin = $this->getLintPath(); + $options = $this->getLintOptions(); + + list($rc, $stdout, $stderr) = exec_manual( + "%C %C --inline-suppr --xml-version=2 -q %s", + $bin, + $options, + $path + ); + + if ($rc === 1) { + throw new Exception("cppcheck failed to run correctly:\n".$stderr); + } + + $dom = new DOMDocument(); + libxml_clear_errors(); + if ($dom->loadXML($stderr) === false || libxml_get_errors()) { + throw new ArcanistUsageException('cppcheck Linter failed to load ' . + 'output. Something happened when running cppcheck. ' . + "Output:\n$stderr" . + "\nTry running lint with --trace flag to get more details."); + } + + + $errors = $dom->getElementsByTagName('error'); + foreach ($errors as $error) { + $loc_node = $error->getElementsByTagName('location'); + if (!$loc_node) { + continue; + } + $location = $loc_node->item(0); + if (!$location) { + continue; + } + $file = $location->getAttribute('file'); + $line = $location->getAttribute('line'); + + $id = $error->getAttribute('id'); + $severity = $error->getAttribute('severity'); + $msg = $error->getAttribute('msg'); + $inconclusive = $error->getAttribute('inconclusive'); + $verbose_msg = $error->getAttribute('verbose'); + + $severity_code = ArcanistLintSeverity::SEVERITY_WARNING; + if ($inconclusive) { + $severity_code = ArcanistLintSeverity::SEVERITY_ADVICE; + } else if (stripos($severity, 'error') !== false) { + $severity_code = ArcanistLintSeverity::SEVERITY_ERROR; + } + + $message = new ArcanistLintMessage(); + $message->setPath($path); + $message->setLine($line); + $message->setCode($severity); + $message->setName($id); + $message->setDescription($msg); + $message->setSeverity($severity_code); + + $this->addLintMessage($message); + } + } + +}