Summary: This was motivated by t7518166. checkCpp crashes on db_test.cc because the file is too big :( Couple of changes: * Added clang-format linter. Now we can catch all code that is not formatted correctly. * Added Howtoeven in our list of linters * Replaced cpplint with flint * Removed checkCpp lint. Nobody ownes it and it doesn't work on db_test.cc Test Plan: Made a random lint error and `arc lint`. Saw an error. Reviewers: yhchiang, kradhakrishnan, anthony, sdong Reviewed By: sdong Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D41949main
parent
a9dcc0a638
commit
a1581eca87
@ -0,0 +1,74 @@ |
|||||||
|
<?php |
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved. |
||||||
|
// This source code is licensed under the BSD-style license found in the |
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant |
||||||
|
// of patent rights can be found in the PATENTS file in the same directory. |
||||||
|
|
||||||
|
abstract class BaseDirectoryScopedFormatLinter extends ArcanistLinter { |
||||||
|
|
||||||
|
const LINT_FORMATTING = 1; |
||||||
|
|
||||||
|
private $changedLines = array(); |
||||||
|
private $rawLintOutput = array(); |
||||||
|
|
||||||
|
abstract protected function getPathsToLint(); |
||||||
|
|
||||||
|
protected function shouldLintPath($path) { |
||||||
|
foreach ($this->getPathsToLint() as $p) { |
||||||
|
// check if $path starts with $p |
||||||
|
if (strncmp($path, $p, strlen($p)) === 0) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// API to tell this linter which lines were changed |
||||||
|
final public function setPathChangedLines($path, $changed) { |
||||||
|
$this->changedLines[$path] = $changed; |
||||||
|
} |
||||||
|
|
||||||
|
final public function willLintPaths(array $paths) { |
||||||
|
$futures = array(); |
||||||
|
foreach ($paths as $path) { |
||||||
|
if (!$this->shouldLintPath($path)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
$changed = $this->changedLines[$path]; |
||||||
|
if (!isset($changed)) { |
||||||
|
// do not run linter if there are no changes |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
$futures[$path] = $this->getFormatFuture($path, $changed); |
||||||
|
} |
||||||
|
|
||||||
|
foreach (Futures($futures)->limit(8) as $p => $f) { |
||||||
|
$this->rawLintOutput[$p] = $f->resolvex(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract protected function getFormatFuture($path, array $changed); |
||||||
|
abstract protected function getLintMessage($diff); |
||||||
|
|
||||||
|
final public function lintPath($path) { |
||||||
|
if (!isset($this->rawLintOutput[$path])) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
list($new_content) = $this->rawLintOutput[$path]; |
||||||
|
$old_content = $this->getData($path); |
||||||
|
|
||||||
|
if ($new_content != $old_content) { |
||||||
|
$diff = ArcanistDiffUtils::renderDifferences($old_content, $new_content); |
||||||
|
$this->raiseLintAtOffset( |
||||||
|
0, |
||||||
|
self::LINT_FORMATTING, |
||||||
|
$this->getLintMessage($diff), |
||||||
|
$old_content, |
||||||
|
$new_content); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,223 @@ |
|||||||
|
<?php |
||||||
|
// Copyright 2015-present Facebook. All Rights Reserved. |
||||||
|
// This source code is licensed under the BSD-style license found in the |
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant |
||||||
|
// of patent rights can be found in the PATENTS file in the same directory. |
||||||
|
|
||||||
|
final class FacebookHowtoevenLinter extends ArcanistLinter { |
||||||
|
|
||||||
|
const VERSION = 'fd9192f324c36d28136d14380f0b552a1385b59b'; |
||||||
|
|
||||||
|
private $parsedTargets = array(); |
||||||
|
|
||||||
|
public function getLinterName() { |
||||||
|
return 'Howtoeven'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function getSeverity($code) { |
||||||
|
$severities = array( |
||||||
|
ArcanistLintSeverity::SEVERITY_DISABLED, |
||||||
|
ArcanistLintSeverity::SEVERITY_ADVICE, |
||||||
|
ArcanistLintSeverity::SEVERITY_WARNING, |
||||||
|
ArcanistLintSeverity::SEVERITY_ERROR, |
||||||
|
); |
||||||
|
return idx($severities, $code, ArcanistLintSeverity::SEVERITY_WARNING); |
||||||
|
} |
||||||
|
|
||||||
|
public function willLintPaths(array $paths) { |
||||||
|
// Cleanup previous runs. |
||||||
|
$this->localExecx("rm -rf _build/_lint"); |
||||||
|
|
||||||
|
// Build compilation database. |
||||||
|
$lintable_paths = $this->getLintablePaths($paths); |
||||||
|
$interesting_paths = $this->getInterestingPaths($lintable_paths); |
||||||
|
|
||||||
|
if (!$lintable_paths) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Run lint. |
||||||
|
try { |
||||||
|
$this->localExecx( |
||||||
|
"%C %C -p _build/dev/ %Ls", |
||||||
|
$this->getBinaryPath(), |
||||||
|
$this->getFilteredIssues(), |
||||||
|
$lintable_paths); |
||||||
|
} catch (CommandException $exception) { |
||||||
|
PhutilConsole::getConsole()->writeErr($exception->getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
// Load results. |
||||||
|
$result = id( |
||||||
|
new SQLite3( |
||||||
|
$this->getProjectRoot().'/_build/_lint/lint.db', |
||||||
|
SQLITE3_OPEN_READONLY)) |
||||||
|
->query("SELECT * FROM raised_issues"); |
||||||
|
|
||||||
|
while ($issue = $result->fetchArray(SQLITE3_ASSOC)) { |
||||||
|
// Skip issues not part of the linted file. |
||||||
|
if (in_array($issue['file'], $interesting_paths)) { |
||||||
|
$this->addLintMessage(id(new ArcanistLintMessage()) |
||||||
|
->setPath($issue['file']) |
||||||
|
->setLine($issue['line']) |
||||||
|
->setChar($issue['column']) |
||||||
|
->setCode('Howtoeven') |
||||||
|
->setSeverity($this->getSeverity($issue['severity'])) |
||||||
|
->setName('Hte-'.$issue['name']) |
||||||
|
->setDescription( |
||||||
|
sprintf( |
||||||
|
"%s\n\n%s", |
||||||
|
($issue['message']) ? $issue['message'] : $issue['description'], |
||||||
|
$issue['explanation'])) |
||||||
|
->setOriginalText(idx($issue, 'original', '')) |
||||||
|
->setReplacementText(idx($issue, 'replacement', ''))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function lintPath($path) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the paths that we know how to lint. |
||||||
|
* |
||||||
|
* The strategy is to first look whether there's an existing compilation |
||||||
|
* database and use that if it's exhaustive. We generate our own only if |
||||||
|
* necessary. |
||||||
|
*/ |
||||||
|
private function getLintablePaths($paths) { |
||||||
|
// Replace headers with existing sources. |
||||||
|
for ($i = 0; $i < count($paths); $i++) { |
||||||
|
if (preg_match("/\.h$/", $paths[$i])) { |
||||||
|
$header = preg_replace("/\.h$/", ".cpp", $paths[$i]); |
||||||
|
if (file_exists($header)) { |
||||||
|
$paths[$i] = $header; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Check if database exists and is exhaustive. |
||||||
|
$available_paths = $this->getAvailablePaths(); |
||||||
|
$lintable_paths = array_intersect($paths, $available_paths); |
||||||
|
if ($paths === $lintable_paths) { |
||||||
|
return $lintable_paths; |
||||||
|
} |
||||||
|
|
||||||
|
// Generate our own database. |
||||||
|
$targets = $this->getTargetsFor($paths); |
||||||
|
if (!$targets) { |
||||||
|
PhutilConsole::getConsole()->writeErr( |
||||||
|
"No build targets found for %s\n", |
||||||
|
implode(', ', $paths)); |
||||||
|
return array(); |
||||||
|
} |
||||||
|
|
||||||
|
$this->localExecx("./tools/build/bin/fbconfig.par -r %Ls", $targets); |
||||||
|
$this->localExecx("./tools/build/bin/fbmake.par gen_cdb"); |
||||||
|
|
||||||
|
$available_paths = $this->getAvailablePaths(); |
||||||
|
$lintable_paths = array_intersect($paths, $available_paths); |
||||||
|
if ($paths != $lintable_paths) { |
||||||
|
PhutilConsole::getConsole()->writeErr( |
||||||
|
"Can't lint %s\n", |
||||||
|
implode(', ', array_diff($paths, $available_paths))); |
||||||
|
} |
||||||
|
|
||||||
|
// Return what we know how to lint. |
||||||
|
return $lintable_paths; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the available paths in the current compilation database. |
||||||
|
*/ |
||||||
|
private function getAvailablePaths() { |
||||||
|
$database_path = $this->getProjectRoot() |
||||||
|
.'/_build/dev/compile_commands.json'; |
||||||
|
if (!file_exists($database_path)) { |
||||||
|
return array(); |
||||||
|
} |
||||||
|
|
||||||
|
$entries = json_decode(file_get_contents($database_path), true); |
||||||
|
$paths = array(); |
||||||
|
foreach ($entries as $entry) { |
||||||
|
$paths[] = $entry['file']; |
||||||
|
} |
||||||
|
return $paths; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Search for the targets directories for the given files. |
||||||
|
*/ |
||||||
|
private static function getTargetsFor($paths) { |
||||||
|
$targets = array(); |
||||||
|
foreach ($paths as $path) { |
||||||
|
while (($path = dirname($path)) !== '.') { |
||||||
|
if (in_array('TARGETS', scandir($path))) { |
||||||
|
$contents = file_get_contents($path.'/TARGETS'); |
||||||
|
if (strpos($contents, 'cpp_binary') !== false) { |
||||||
|
$targets[] = $path; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return array_unique($targets); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The paths that we actually want to report on. |
||||||
|
*/ |
||||||
|
private function getInterestingPaths($paths) { |
||||||
|
$headers = array(); |
||||||
|
foreach ($paths as $path) { |
||||||
|
$headers[] = preg_replace("/\.cpp$/", ".h", $path); |
||||||
|
} |
||||||
|
return array_merge($paths, $headers); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The path where the binary is located. Will return the current dewey binary |
||||||
|
* unless the `HOWTOEVEN_BUILD` environment variable is set. |
||||||
|
*/ |
||||||
|
private function getBinaryPath() { |
||||||
|
$path = sprintf( |
||||||
|
"/mnt/dewey/fbcode/.commits/%s/builds/howtoeven/client", |
||||||
|
self::VERSION); |
||||||
|
|
||||||
|
$build = getenv('HOWTOEVEN_BUILD'); |
||||||
|
if ($build) { |
||||||
|
$path = sprintf( |
||||||
|
"./_build/%s/tools/howtoeven/client", |
||||||
|
$build); |
||||||
|
if (!file_exists($path)) { |
||||||
|
PhutilConsole::getConsole()->writeErr(">> %s does not exist\n", $path); |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $path; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Execute the command in the root directory. |
||||||
|
*/ |
||||||
|
private function localExecx($command /* , ... */) { |
||||||
|
$arguments = func_get_args(); |
||||||
|
return newv('ExecFuture', $arguments) |
||||||
|
->setCWD($this->getProjectRoot()) |
||||||
|
->resolvex(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The root of the project. |
||||||
|
*/ |
||||||
|
private function getProjectRoot() { |
||||||
|
return $this->getEngine()->getWorkingCopy()->getProjectRoot(); |
||||||
|
} |
||||||
|
|
||||||
|
private function getFilteredIssues() { |
||||||
|
$issues = getenv('HOWTOEVEN_ISSUES'); |
||||||
|
return ($issues) ? csprintf('-issues %s', $issues) : ''; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
<?php |
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved. |
||||||
|
// This source code is licensed under the BSD-style license found in the |
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant |
||||||
|
// of patent rights can be found in the PATENTS file in the same directory. |
||||||
|
|
||||||
|
final class FbcodeClangFormatLinter extends BaseDirectoryScopedFormatLinter { |
||||||
|
|
||||||
|
const LINT_FORMATTING = 1; |
||||||
|
const CLANG_FORMAT_BINARY = '/mnt/vol/engshare/admin/scripts/clang-format'; |
||||||
|
|
||||||
|
protected function getPathsToLint() { |
||||||
|
return array(''); |
||||||
|
} |
||||||
|
|
||||||
|
public function getLinterName() { |
||||||
|
return 'CLANG_FORMAT'; |
||||||
|
} |
||||||
|
|
||||||
|
public function getLintSeverityMap() { |
||||||
|
return array( |
||||||
|
self::LINT_FORMATTING => ArcanistLintSeverity::SEVERITY_ADVICE, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function getLintNameMap() { |
||||||
|
return array( |
||||||
|
self::LINT_FORMATTING => pht('Changes are not clang-formatted'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
protected function getFormatFuture($path, array $changed) { |
||||||
|
$args = ""; |
||||||
|
foreach ($changed as $key => $value) { |
||||||
|
$args .= " --lines=$key:$key"; |
||||||
|
} |
||||||
|
|
||||||
|
return new ExecFuture( |
||||||
|
"%s %s $args", |
||||||
|
self::CLANG_FORMAT_BINARY, |
||||||
|
$this->getEngine()->getFilePathOnDisk($path)); |
||||||
|
} |
||||||
|
|
||||||
|
protected function getLintMessage($diff) { |
||||||
|
$link_to_clang_format = |
||||||
|
"[[ http://fburl.com/clang-format | clang-format ]]"; |
||||||
|
return <<<LINT_MSG |
||||||
|
Changes in this file were not formatted using $link_to_clang_format. |
||||||
|
Please run build_tools/format-diff.sh or `make format` |
||||||
|
LINT_MSG; |
||||||
|
} |
||||||
|
} |
@ -1,68 +0,0 @@ |
|||||||
<?php |
|
||||||
// Copyright 2004-present Facebook. All rights reserved. |
|
||||||
|
|
||||||
class PfffCppLinter extends ArcanistLinter { |
|
||||||
const PROGRAM = "/home/engshare/tools/checkCpp"; |
|
||||||
|
|
||||||
public function getLinterName() { |
|
||||||
return "checkCpp"; |
|
||||||
} |
|
||||||
public function getLintNameMap() { |
|
||||||
return array( |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function getLintSeverityMap() { |
|
||||||
return array( |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function willLintPaths(array $paths) { |
|
||||||
$program = false; |
|
||||||
$ret_value = 0; |
|
||||||
$last_line = system("which checkCpp", $ret_value); |
|
||||||
if ($ret_value == 0) { |
|
||||||
$program = $last_line; |
|
||||||
} else if (file_exists(self::PROGRAM)) { |
|
||||||
$program = self::PROGRAM; |
|
||||||
} |
|
||||||
if ($program) { |
|
||||||
$futures = array(); |
|
||||||
foreach ($paths as $p) { |
|
||||||
$futures[$p] = new ExecFuture("%s --lint %s 2>&1", |
|
||||||
$program, $this->getEngine()->getFilePathOnDisk($p)); |
|
||||||
} |
|
||||||
foreach (Futures($futures)->limit(8) as $p => $f) { |
|
||||||
|
|
||||||
list($stdout, $stderr) = $f->resolvex(); |
|
||||||
$raw = json_decode($stdout, true); |
|
||||||
if (!is_array($raw)) { |
|
||||||
throw new Exception( |
|
||||||
"checkCpp returned invalid JSON!". |
|
||||||
"Stdout: {$stdout} Stderr: {$stderr}" |
|
||||||
); |
|
||||||
} |
|
||||||
foreach($raw as $err) { |
|
||||||
$this->addLintMessage( |
|
||||||
ArcanistLintMessage::newFromDictionary( |
|
||||||
array( |
|
||||||
'path' => $err['file'], |
|
||||||
'line' => $err['line'], |
|
||||||
'char' => 0, |
|
||||||
'name' => $err['name'], |
|
||||||
'description' => $err['info'], |
|
||||||
'code' => $this->getLinterName(), |
|
||||||
'severity' => ArcanistLintSeverity::SEVERITY_WARNING, |
|
||||||
) |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
public function lintPath($path) { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
// Copyright 2015-present Facebook. All Rights Reserved. |
||||||
|
// This source code is licensed under the BSD-style license found in the |
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant |
||||||
|
// of patent rights can be found in the PATENTS file in the same directory. |
||||||
|
|
||||||
|
final class FacebookHowtoevenLintEngine extends ArcanistLintEngine { |
||||||
|
|
||||||
|
public function buildLinters() { |
||||||
|
$paths = array(); |
||||||
|
|
||||||
|
foreach ($this->getPaths() as $path) { |
||||||
|
// Don't try to lint deleted files or changed directories. |
||||||
|
if (!Filesystem::pathExists($path) || is_dir($path)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (preg_match('/\.(cpp|c|cc|cxx|h|hh|hpp|hxx|tcc)$/', $path)) { |
||||||
|
$paths[] = $path; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$howtoeven = new FacebookHowtoevenLinter(); |
||||||
|
$howtoeven->setPaths($paths); |
||||||
|
return array($howtoeven); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue