2013-07-29 16:19:24 +08:00
|
|
|
//===--- tools/extra/clang-tidy/ClangTidyMain.cpp - Clang tidy tool -------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2013-07-29 16:19:24 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///
|
|
|
|
/// \file This file implements a clang-tidy tool.
|
|
|
|
///
|
|
|
|
/// This tool uses the Clang Tooling infrastructure, see
|
|
|
|
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
|
|
|
|
/// for details on setting it up with LLVM source tree.
|
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-01-24 08:42:14 +08:00
|
|
|
#include "ClangTidyMain.h"
|
2013-07-29 16:19:24 +08:00
|
|
|
#include "../ClangTidy.h"
|
2018-12-14 15:29:06 +08:00
|
|
|
#include "../ClangTidyForceLinker.h"
|
2019-08-26 23:44:32 +08:00
|
|
|
#include "../GlobList.h"
|
2013-11-14 23:49:44 +08:00
|
|
|
#include "clang/Tooling/CommonOptionsParser.h"
|
2019-11-28 12:50:35 +08:00
|
|
|
#include "llvm/Support/InitLLVM.h"
|
2014-09-25 02:36:03 +08:00
|
|
|
#include "llvm/Support/Process.h"
|
2018-11-26 19:11:52 +08:00
|
|
|
#include "llvm/Support/Signals.h"
|
2017-10-21 07:00:51 +08:00
|
|
|
#include "llvm/Support/TargetSelect.h"
|
2020-06-01 00:41:29 +08:00
|
|
|
#include "llvm/Support/WithColor.h"
|
2013-07-29 16:19:24 +08:00
|
|
|
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
using namespace clang::driver;
|
|
|
|
using namespace clang::tooling;
|
|
|
|
using namespace llvm;
|
|
|
|
|
2014-02-05 21:43:27 +08:00
|
|
|
static cl::OptionCategory ClangTidyCategory("clang-tidy options");
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2013-11-14 23:49:44 +08:00
|
|
|
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
|
2016-02-08 08:19:29 +08:00
|
|
|
static cl::extrahelp ClangTidyHelp(R"(
|
|
|
|
Configuration files:
|
|
|
|
clang-tidy attempts to read configuration for each source file from a
|
|
|
|
.clang-tidy file located in the closest parent directory of the source
|
2020-04-01 17:08:53 +08:00
|
|
|
file. If InheritParentConfig is true in a config file, the configuration file
|
|
|
|
in the parent directory (if any exists) will be taken and current config file
|
|
|
|
will be applied on top of the parent one. If any configuration options have
|
|
|
|
a corresponding command-line option, command-line option takes precedence.
|
|
|
|
The effective configuration can be inspected using -dump-config:
|
2016-02-08 08:19:29 +08:00
|
|
|
|
2017-04-06 22:27:00 +08:00
|
|
|
$ clang-tidy -dump-config
|
2016-02-08 08:19:29 +08:00
|
|
|
---
|
2020-04-01 17:08:53 +08:00
|
|
|
Checks: '-*,some-check'
|
|
|
|
WarningsAsErrors: ''
|
|
|
|
HeaderFilterRegex: ''
|
|
|
|
FormatStyle: none
|
|
|
|
InheritParentConfig: true
|
|
|
|
User: user
|
2016-02-08 08:19:29 +08:00
|
|
|
CheckOptions:
|
|
|
|
- key: some-check.SomeOption
|
|
|
|
value: 'some value'
|
|
|
|
...
|
|
|
|
|
|
|
|
)");
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2016-12-01 02:06:42 +08:00
|
|
|
const char DefaultChecks[] = // Enable these checks by default:
|
|
|
|
"clang-diagnostic-*," // * compiler diagnostics
|
|
|
|
"clang-analyzer-*"; // * Static Analyzer checks
|
2014-05-23 00:07:11 +08:00
|
|
|
|
2016-02-08 08:19:29 +08:00
|
|
|
static cl::opt<std::string> Checks("checks", cl::desc(R"(
|
|
|
|
Comma-separated list of globs with optional '-'
|
|
|
|
prefix. Globs are processed in order of
|
|
|
|
appearance in the list. Globs without '-'
|
|
|
|
prefix add checks with matching names to the
|
|
|
|
set, globs with the '-' prefix remove checks
|
|
|
|
with matching names from the set of enabled
|
2017-03-03 19:16:34 +08:00
|
|
|
checks. This option's value is appended to the
|
2016-02-08 08:19:29 +08:00
|
|
|
value of the 'Checks' option in .clang-tidy
|
|
|
|
file, if any.
|
|
|
|
)"),
|
|
|
|
cl::init(""), cl::cat(ClangTidyCategory));
|
|
|
|
|
|
|
|
static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", cl::desc(R"(
|
|
|
|
Upgrades warnings to errors. Same format as
|
|
|
|
'-checks'.
|
|
|
|
This option's value is appended to the value of
|
|
|
|
the 'WarningsAsErrors' option in .clang-tidy
|
|
|
|
file, if any.
|
|
|
|
)"),
|
|
|
|
cl::init(""),
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
|
|
|
static cl::opt<std::string> HeaderFilter("header-filter", cl::desc(R"(
|
|
|
|
Regular expression matching the names of the
|
|
|
|
headers to output diagnostics from. Diagnostics
|
|
|
|
from the main file of each translation unit are
|
|
|
|
always displayed.
|
|
|
|
Can be used together with -line-filter.
|
2019-07-02 02:55:10 +08:00
|
|
|
This option overrides the 'HeaderFilterRegex'
|
|
|
|
option in .clang-tidy file, if any.
|
2016-02-08 08:19:29 +08:00
|
|
|
)"),
|
|
|
|
cl::init(""),
|
|
|
|
cl::cat(ClangTidyCategory));
|
2014-05-23 00:07:11 +08:00
|
|
|
|
2014-10-29 06:16:13 +08:00
|
|
|
static cl::opt<bool>
|
|
|
|
SystemHeaders("system-headers",
|
2014-11-03 22:06:31 +08:00
|
|
|
cl::desc("Display the errors from system headers."),
|
2014-10-29 06:16:13 +08:00
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
2016-11-08 15:50:19 +08:00
|
|
|
static cl::opt<std::string> LineFilter("line-filter", cl::desc(R"(
|
2016-02-08 08:19:29 +08:00
|
|
|
List of files with line ranges to filter the
|
|
|
|
warnings. Can be used together with
|
|
|
|
-header-filter. The format of the list is a
|
|
|
|
JSON array of objects:
|
|
|
|
[
|
|
|
|
{"name":"file1.cpp","lines":[[1,3],[5,7]]},
|
|
|
|
{"name":"file2.h"}
|
|
|
|
]
|
|
|
|
)"),
|
2016-11-08 15:50:19 +08:00
|
|
|
cl::init(""),
|
|
|
|
cl::cat(ClangTidyCategory));
|
2016-02-08 08:19:29 +08:00
|
|
|
|
|
|
|
static cl::opt<bool> Fix("fix", cl::desc(R"(
|
|
|
|
Apply suggested fixes. Without -fix-errors
|
|
|
|
clang-tidy will bail out if any compilation
|
|
|
|
errors were found.
|
|
|
|
)"),
|
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool> FixErrors("fix-errors", cl::desc(R"(
|
|
|
|
Apply suggested fixes even if compilation
|
|
|
|
errors were found. If compiler errors have
|
|
|
|
attached fix-its, clang-tidy will apply them as
|
|
|
|
well.
|
|
|
|
)"),
|
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
|
|
|
|
2017-03-03 19:16:34 +08:00
|
|
|
static cl::opt<std::string> FormatStyle("format-style", cl::desc(R"(
|
|
|
|
Style for formatting code around applied fixes:
|
|
|
|
- 'none' (default) turns off formatting
|
|
|
|
- 'file' (literally 'file', not a placeholder)
|
|
|
|
uses .clang-format file in the closest parent
|
|
|
|
directory
|
|
|
|
- '{ <json> }' specifies options inline, e.g.
|
|
|
|
-format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
|
|
|
|
- 'llvm', 'google', 'webkit', 'mozilla'
|
|
|
|
See clang-format documentation for the up-to-date
|
|
|
|
information about formatting styles and options.
|
2017-04-06 22:27:00 +08:00
|
|
|
This option overrides the 'FormatStyle` option in
|
|
|
|
.clang-tidy file, if any.
|
2016-12-01 02:06:42 +08:00
|
|
|
)"),
|
2017-03-03 19:16:34 +08:00
|
|
|
cl::init("none"),
|
|
|
|
cl::cat(ClangTidyCategory));
|
2016-12-01 02:06:42 +08:00
|
|
|
|
2016-02-08 08:19:29 +08:00
|
|
|
static cl::opt<bool> ListChecks("list-checks", cl::desc(R"(
|
|
|
|
List all enabled checks and exit. Use with
|
|
|
|
-checks=* to list all available checks.
|
|
|
|
)"),
|
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
|
|
|
|
2016-04-27 17:15:01 +08:00
|
|
|
static cl::opt<bool> ExplainConfig("explain-config", cl::desc(R"(
|
2016-09-22 22:36:43 +08:00
|
|
|
For each enabled check explains, where it is
|
|
|
|
enabled, i.e. in clang-tidy binary, command
|
|
|
|
line or a specific configuration file.
|
2016-04-27 17:15:01 +08:00
|
|
|
)"),
|
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
|
|
|
|
2016-02-08 08:19:29 +08:00
|
|
|
static cl::opt<std::string> Config("config", cl::desc(R"(
|
|
|
|
Specifies a configuration in YAML/JSON format:
|
|
|
|
-config="{Checks: '*',
|
|
|
|
CheckOptions: [{key: x,
|
|
|
|
value: y}]}"
|
|
|
|
When the value is empty, clang-tidy will
|
|
|
|
attempt to find a file named .clang-tidy for
|
|
|
|
each source file in its parent directories.
|
|
|
|
)"),
|
|
|
|
cl::init(""), cl::cat(ClangTidyCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool> DumpConfig("dump-config", cl::desc(R"(
|
|
|
|
Dumps configuration in the YAML format to
|
|
|
|
stdout. This option can be used along with a
|
|
|
|
file name (and '--' if the file is outside of a
|
|
|
|
project with configured compilation database).
|
|
|
|
The configuration used for this file will be
|
|
|
|
printed.
|
|
|
|
Use along with -checks=* to include
|
|
|
|
configuration of all checks.
|
|
|
|
)"),
|
|
|
|
cl::init(false), cl::cat(ClangTidyCategory));
|
|
|
|
|
|
|
|
static cl::opt<bool> EnableCheckProfile("enable-check-profile", cl::desc(R"(
|
|
|
|
Enable per-check timing profiles, and print a
|
|
|
|
report to stderr.
|
|
|
|
)"),
|
|
|
|
cl::init(false),
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
[clang-tidy] Store checks profiling info as JSON files
Summary:
Continuation of D46504.
Example output:
```
$ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp
$ # Note that there won't be timings table printed to the console.
$ cat *.json
{
"file": "/path/to/source.cpp",
"timestamp": "2018-05-16 16:13:18.717446360",
"profile": {
"time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00,
"time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01,
"time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01
}
}
```
There are two arguments that control profile storage:
* `-store-check-profile=<prefix>`
By default reports are printed in tabulated format to stderr. When this option
is passed, these per-TU profiles are instead stored as JSON.
If the prefix is not an absolute path, it is considered to be relative to the
directory from where you have run :program:`clang-tidy`. All `.` and `..`
patterns in the path are collapsed, and symlinks are resolved.
Example:
Let's suppose you have a source file named `example.cpp`, located in
`/source` directory.
* If you specify `-store-check-profile=/tmp`, then the profile will be saved
to `/tmp/<timestamp>-example.cpp.json`
* If you run :program:`clang-tidy` from within `/foo` directory, and specify
`-store-check-profile=.`, then the profile will still be saved to
`/foo/<timestamp>-example.cpp.json`
Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman
Reviewed By: alexfh, george.karpenkov, aaron.ballman
Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D46602
llvm-svn: 334101
2018-06-06 23:07:51 +08:00
|
|
|
static cl::opt<std::string> StoreCheckProfile("store-check-profile",
|
|
|
|
cl::desc(R"(
|
|
|
|
By default reports are printed in tabulated
|
|
|
|
format to stderr. When this option is passed,
|
|
|
|
these per-TU profiles are instead stored as JSON.
|
|
|
|
)"),
|
|
|
|
cl::value_desc("prefix"),
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
/// This option allows enabling the experimental alpha checkers from the static
|
|
|
|
/// analyzer. This option is set to false and not visible in help, because it is
|
|
|
|
/// highly not recommended for users.
|
|
|
|
static cl::opt<bool>
|
|
|
|
AllowEnablingAnalyzerAlphaCheckers("allow-enabling-analyzer-alpha-checkers",
|
|
|
|
cl::init(false), cl::Hidden,
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
2016-02-08 08:19:29 +08:00
|
|
|
static cl::opt<std::string> ExportFixes("export-fixes", cl::desc(R"(
|
|
|
|
YAML file to store suggested fixes in. The
|
2016-08-19 17:36:14 +08:00
|
|
|
stored fixes can be applied to the input source
|
2016-02-08 08:19:29 +08:00
|
|
|
code with clang-apply-replacements.
|
|
|
|
)"),
|
|
|
|
cl::value_desc("filename"),
|
|
|
|
cl::cat(ClangTidyCategory));
|
2014-09-04 18:31:23 +08:00
|
|
|
|
2017-02-10 02:32:02 +08:00
|
|
|
static cl::opt<bool> Quiet("quiet", cl::desc(R"(
|
2017-03-03 19:16:34 +08:00
|
|
|
Run clang-tidy in quiet mode. This suppresses
|
2017-02-10 02:32:02 +08:00
|
|
|
printing statistics about ignored warnings and
|
|
|
|
warnings treated as errors if the respective
|
|
|
|
options are specified.
|
|
|
|
)"),
|
|
|
|
cl::init(false),
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
2018-01-23 20:31:06 +08:00
|
|
|
static cl::opt<std::string> VfsOverlay("vfsoverlay", cl::desc(R"(
|
|
|
|
Overlay the virtual filesystem described by file
|
|
|
|
over the real file system.
|
|
|
|
)"),
|
|
|
|
cl::value_desc("filename"),
|
|
|
|
cl::cat(ClangTidyCategory));
|
|
|
|
|
2014-09-10 19:43:09 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace tidy {
|
|
|
|
|
|
|
|
static void printStats(const ClangTidyStats &Stats) {
|
2014-05-23 00:07:11 +08:00
|
|
|
if (Stats.errorsIgnored()) {
|
|
|
|
llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings (";
|
2014-05-07 17:06:53 +08:00
|
|
|
StringRef Separator = "";
|
|
|
|
if (Stats.ErrorsIgnoredNonUserCode) {
|
|
|
|
llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";
|
|
|
|
Separator = ", ";
|
|
|
|
}
|
2014-05-23 00:07:11 +08:00
|
|
|
if (Stats.ErrorsIgnoredLineFilter) {
|
|
|
|
llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter
|
|
|
|
<< " due to line filter";
|
|
|
|
Separator = ", ";
|
|
|
|
}
|
2014-05-07 17:06:53 +08:00
|
|
|
if (Stats.ErrorsIgnoredNOLINT) {
|
|
|
|
llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
|
|
|
|
Separator = ", ";
|
|
|
|
}
|
|
|
|
if (Stats.ErrorsIgnoredCheckFilter)
|
|
|
|
llvm::errs() << Separator << Stats.ErrorsIgnoredCheckFilter
|
|
|
|
<< " with check filters";
|
|
|
|
llvm::errs() << ").\n";
|
|
|
|
if (Stats.ErrorsIgnoredNonUserCode)
|
2015-07-27 21:41:30 +08:00
|
|
|
llvm::errs() << "Use -header-filter=.* to display errors from all "
|
2016-02-08 08:19:29 +08:00
|
|
|
"non-system headers. Use -system-headers to display "
|
|
|
|
"errors from system headers as well.\n";
|
2014-05-07 17:06:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-18 16:54:28 +08:00
|
|
|
static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider(
|
|
|
|
llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS) {
|
2014-09-10 19:43:09 +08:00
|
|
|
ClangTidyGlobalOptions GlobalOptions;
|
|
|
|
if (std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) {
|
2014-05-23 00:07:11 +08:00
|
|
|
llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";
|
|
|
|
llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
|
2014-09-26 19:42:29 +08:00
|
|
|
return nullptr;
|
2014-05-23 00:07:11 +08:00
|
|
|
}
|
2014-04-29 23:20:10 +08:00
|
|
|
|
2014-09-25 02:36:03 +08:00
|
|
|
ClangTidyOptions DefaultOptions;
|
|
|
|
DefaultOptions.Checks = DefaultChecks;
|
2016-01-14 01:36:41 +08:00
|
|
|
DefaultOptions.WarningsAsErrors = "";
|
2014-09-25 02:36:03 +08:00
|
|
|
DefaultOptions.HeaderFilterRegex = HeaderFilter;
|
2014-10-29 06:16:13 +08:00
|
|
|
DefaultOptions.SystemHeaders = SystemHeaders;
|
2017-04-06 21:41:29 +08:00
|
|
|
DefaultOptions.FormatStyle = FormatStyle;
|
2014-09-25 02:36:03 +08:00
|
|
|
DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
|
|
|
|
// USERNAME is used on Windows.
|
|
|
|
if (!DefaultOptions.User)
|
|
|
|
DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME");
|
2014-09-04 22:23:36 +08:00
|
|
|
|
2014-09-10 19:43:09 +08:00
|
|
|
ClangTidyOptions OverrideOptions;
|
2014-09-04 22:23:36 +08:00
|
|
|
if (Checks.getNumOccurrences() > 0)
|
|
|
|
OverrideOptions.Checks = Checks;
|
2016-01-14 01:36:41 +08:00
|
|
|
if (WarningsAsErrors.getNumOccurrences() > 0)
|
|
|
|
OverrideOptions.WarningsAsErrors = WarningsAsErrors;
|
2014-09-04 22:23:36 +08:00
|
|
|
if (HeaderFilter.getNumOccurrences() > 0)
|
|
|
|
OverrideOptions.HeaderFilterRegex = HeaderFilter;
|
2014-10-29 06:16:13 +08:00
|
|
|
if (SystemHeaders.getNumOccurrences() > 0)
|
|
|
|
OverrideOptions.SystemHeaders = SystemHeaders;
|
2017-04-06 21:41:29 +08:00
|
|
|
if (FormatStyle.getNumOccurrences() > 0)
|
|
|
|
OverrideOptions.FormatStyle = FormatStyle;
|
2014-09-04 22:23:36 +08:00
|
|
|
|
2014-09-26 19:42:29 +08:00
|
|
|
if (!Config.empty()) {
|
|
|
|
if (llvm::ErrorOr<ClangTidyOptions> ParsedConfig =
|
|
|
|
parseConfiguration(Config)) {
|
2019-08-15 07:52:23 +08:00
|
|
|
return std::make_unique<ConfigOptionsProvider>(
|
2016-04-27 17:15:01 +08:00
|
|
|
GlobalOptions,
|
2020-04-01 17:08:53 +08:00
|
|
|
ClangTidyOptions::getDefaults().mergeWith(DefaultOptions, 0),
|
2016-04-27 17:15:01 +08:00
|
|
|
*ParsedConfig, OverrideOptions);
|
2014-09-26 19:42:29 +08:00
|
|
|
} else {
|
|
|
|
llvm::errs() << "Error: invalid configuration specified.\n"
|
|
|
|
<< ParsedConfig.getError().message() << "\n";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
2019-08-15 07:52:23 +08:00
|
|
|
return std::make_unique<FileOptionsProvider>(GlobalOptions, DefaultOptions,
|
2018-04-18 16:54:28 +08:00
|
|
|
OverrideOptions, std::move(FS));
|
2014-09-26 19:42:29 +08:00
|
|
|
}
|
|
|
|
|
2018-01-23 20:31:06 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<vfs::FileSystem>
|
2019-03-22 21:42:48 +08:00
|
|
|
getVfsFromFile(const std::string &OverlayFile,
|
|
|
|
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS) {
|
2018-01-23 20:31:06 +08:00
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
|
2019-03-22 21:42:48 +08:00
|
|
|
BaseFS->getBufferForFile(OverlayFile);
|
2018-01-23 20:31:06 +08:00
|
|
|
if (!Buffer) {
|
|
|
|
llvm::errs() << "Can't load virtual filesystem overlay file '"
|
|
|
|
<< OverlayFile << "': " << Buffer.getError().message()
|
|
|
|
<< ".\n";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getVFSFromYAML(
|
|
|
|
std::move(Buffer.get()), /*DiagHandler*/ nullptr, OverlayFile);
|
|
|
|
if (!FS) {
|
|
|
|
llvm::errs() << "Error: invalid virtual filesystem overlay file '"
|
|
|
|
<< OverlayFile << "'.\n";
|
|
|
|
return nullptr;
|
|
|
|
}
|
2019-03-22 21:42:48 +08:00
|
|
|
return FS;
|
2018-01-23 20:31:06 +08:00
|
|
|
}
|
|
|
|
|
2020-01-24 08:42:14 +08:00
|
|
|
int clangTidyMain(int argc, const char **argv) {
|
2019-11-28 12:50:35 +08:00
|
|
|
llvm::InitLLVM X(argc, argv);
|
2020-06-01 00:41:29 +08:00
|
|
|
llvm::Expected<CommonOptionsParser> OptionsParser =
|
|
|
|
CommonOptionsParser::create(argc, argv, ClangTidyCategory,
|
|
|
|
cl::ZeroOrMore);
|
|
|
|
if (!OptionsParser) {
|
|
|
|
llvm::WithColor::error() << llvm::toString(OptionsParser.takeError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-03-22 21:42:48 +08:00
|
|
|
llvm::IntrusiveRefCntPtr<vfs::OverlayFileSystem> BaseFS(
|
|
|
|
new vfs::OverlayFileSystem(vfs::getRealFileSystem()));
|
|
|
|
|
|
|
|
if (!VfsOverlay.empty()) {
|
|
|
|
IntrusiveRefCntPtr<vfs::FileSystem> VfsFromFile =
|
|
|
|
getVfsFromFile(VfsOverlay, BaseFS);
|
|
|
|
if (!VfsFromFile)
|
|
|
|
return 1;
|
|
|
|
BaseFS->pushOverlay(VfsFromFile);
|
|
|
|
}
|
2014-09-26 19:42:29 +08:00
|
|
|
|
2018-04-18 16:54:28 +08:00
|
|
|
auto OwningOptionsProvider = createOptionsProvider(BaseFS);
|
2017-04-06 21:41:29 +08:00
|
|
|
auto *OptionsProvider = OwningOptionsProvider.get();
|
2014-09-26 19:42:29 +08:00
|
|
|
if (!OptionsProvider)
|
|
|
|
return 1;
|
2014-06-05 21:31:45 +08:00
|
|
|
|
[clang-tidy] Store checks profiling info as JSON files
Summary:
Continuation of D46504.
Example output:
```
$ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp
$ # Note that there won't be timings table printed to the console.
$ cat *.json
{
"file": "/path/to/source.cpp",
"timestamp": "2018-05-16 16:13:18.717446360",
"profile": {
"time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00,
"time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01,
"time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01
}
}
```
There are two arguments that control profile storage:
* `-store-check-profile=<prefix>`
By default reports are printed in tabulated format to stderr. When this option
is passed, these per-TU profiles are instead stored as JSON.
If the prefix is not an absolute path, it is considered to be relative to the
directory from where you have run :program:`clang-tidy`. All `.` and `..`
patterns in the path are collapsed, and symlinks are resolved.
Example:
Let's suppose you have a source file named `example.cpp`, located in
`/source` directory.
* If you specify `-store-check-profile=/tmp`, then the profile will be saved
to `/tmp/<timestamp>-example.cpp.json`
* If you run :program:`clang-tidy` from within `/foo` directory, and specify
`-store-check-profile=.`, then the profile will still be saved to
`/foo/<timestamp>-example.cpp.json`
Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman
Reviewed By: alexfh, george.karpenkov, aaron.ballman
Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D46602
llvm-svn: 334101
2018-06-06 23:07:51 +08:00
|
|
|
auto MakeAbsolute = [](const std::string &Input) -> SmallString<256> {
|
|
|
|
if (Input.empty())
|
|
|
|
return {};
|
|
|
|
SmallString<256> AbsolutePath(Input);
|
|
|
|
if (std::error_code EC = llvm::sys::fs::make_absolute(AbsolutePath)) {
|
|
|
|
llvm::errs() << "Can't make absolute path from " << Input << ": "
|
|
|
|
<< EC.message() << "\n";
|
|
|
|
}
|
|
|
|
return AbsolutePath;
|
|
|
|
};
|
|
|
|
|
|
|
|
SmallString<256> ProfilePrefix = MakeAbsolute(StoreCheckProfile);
|
|
|
|
|
2015-08-17 18:03:27 +08:00
|
|
|
StringRef FileName("dummy");
|
2020-06-01 00:41:29 +08:00
|
|
|
auto PathList = OptionsParser->getSourcePathList();
|
2015-08-17 18:03:27 +08:00
|
|
|
if (!PathList.empty()) {
|
2015-08-17 19:27:11 +08:00
|
|
|
FileName = PathList.front();
|
2015-08-17 18:03:27 +08:00
|
|
|
}
|
2016-07-11 15:47:04 +08:00
|
|
|
|
2020-01-29 03:23:46 +08:00
|
|
|
SmallString<256> FilePath = MakeAbsolute(std::string(FileName));
|
[clang-tidy] Store checks profiling info as JSON files
Summary:
Continuation of D46504.
Example output:
```
$ clang-tidy -enable-check-profile -store-check-profile=. -checks=-*,readability-function-size source.cpp
$ # Note that there won't be timings table printed to the console.
$ cat *.json
{
"file": "/path/to/source.cpp",
"timestamp": "2018-05-16 16:13:18.717446360",
"profile": {
"time.clang-tidy.readability-function-size.wall": 1.0421266555786133e+00,
"time.clang-tidy.readability-function-size.user": 9.2088400000005421e-01,
"time.clang-tidy.readability-function-size.sys": 1.2418899999999974e-01
}
}
```
There are two arguments that control profile storage:
* `-store-check-profile=<prefix>`
By default reports are printed in tabulated format to stderr. When this option
is passed, these per-TU profiles are instead stored as JSON.
If the prefix is not an absolute path, it is considered to be relative to the
directory from where you have run :program:`clang-tidy`. All `.` and `..`
patterns in the path are collapsed, and symlinks are resolved.
Example:
Let's suppose you have a source file named `example.cpp`, located in
`/source` directory.
* If you specify `-store-check-profile=/tmp`, then the profile will be saved
to `/tmp/<timestamp>-example.cpp.json`
* If you run :program:`clang-tidy` from within `/foo` directory, and specify
`-store-check-profile=.`, then the profile will still be saved to
`/foo/<timestamp>-example.cpp.json`
Reviewers: alexfh, sbenza, george.karpenkov, NoQ, aaron.ballman
Reviewed By: alexfh, george.karpenkov, aaron.ballman
Subscribers: Quuxplusone, JonasToth, aaron.ballman, llvm-commits, rja, Eugene.Zelenko, xazax.hun, mgrang, cfe-commits
Tags: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D46602
llvm-svn: 334101
2018-06-06 23:07:51 +08:00
|
|
|
|
2016-07-11 15:47:04 +08:00
|
|
|
ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
std::vector<std::string> EnabledChecks =
|
|
|
|
getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
|
2014-06-03 04:32:06 +08:00
|
|
|
|
2016-04-27 17:15:01 +08:00
|
|
|
if (ExplainConfig) {
|
2016-11-08 15:50:19 +08:00
|
|
|
// FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
|
2016-04-27 17:15:01 +08:00
|
|
|
std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
|
2016-07-11 15:47:04 +08:00
|
|
|
RawOptions = OptionsProvider->getRawOptions(FilePath);
|
2016-04-27 17:15:01 +08:00
|
|
|
for (const std::string &Check : EnabledChecks) {
|
|
|
|
for (auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) {
|
|
|
|
if (It->first.Checks && GlobList(*It->first.Checks).contains(Check)) {
|
|
|
|
llvm::outs() << "'" << Check << "' is enabled in the " << It->second
|
|
|
|
<< ".\n";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Clang-tidy: added --disable-checks, --list-checks options.
Summary:
Allow disabling checks by regex. By default, disable alpha.* checks,
that are not particularly good tested (e.g. IdempotentOperationChecker, see
http://llvm-reviews.chandlerc.com/D2427).
Fixed a bug, that would disable all analyzer checks, when using a regex more
strict, than 'clang-analyzer-', for example --checks='clang-analyzer-deadcode-'.
Added --list-checks to list all enabled checks. This is useful to test specific
values in --checks/--disable-checks.
Reviewers: djasper, klimek
Reviewed By: klimek
CC: cfe-commits, klimek
Differential Revision: http://llvm-reviews.chandlerc.com/D2444
llvm-svn: 197717
2013-12-20 03:57:05 +08:00
|
|
|
if (ListChecks) {
|
2016-04-27 19:45:14 +08:00
|
|
|
if (EnabledChecks.empty()) {
|
|
|
|
llvm::errs() << "No checks enabled.\n";
|
|
|
|
return 1;
|
|
|
|
}
|
Clang-tidy: added --disable-checks, --list-checks options.
Summary:
Allow disabling checks by regex. By default, disable alpha.* checks,
that are not particularly good tested (e.g. IdempotentOperationChecker, see
http://llvm-reviews.chandlerc.com/D2427).
Fixed a bug, that would disable all analyzer checks, when using a regex more
strict, than 'clang-analyzer-', for example --checks='clang-analyzer-deadcode-'.
Added --list-checks to list all enabled checks. This is useful to test specific
values in --checks/--disable-checks.
Reviewers: djasper, klimek
Reviewed By: klimek
CC: cfe-commits, klimek
Differential Revision: http://llvm-reviews.chandlerc.com/D2444
llvm-svn: 197717
2013-12-20 03:57:05 +08:00
|
|
|
llvm::outs() << "Enabled checks:";
|
2016-06-15 23:46:10 +08:00
|
|
|
for (const auto &CheckName : EnabledChecks)
|
2014-03-05 21:14:32 +08:00
|
|
|
llvm::outs() << "\n " << CheckName;
|
Clang-tidy: added --disable-checks, --list-checks options.
Summary:
Allow disabling checks by regex. By default, disable alpha.* checks,
that are not particularly good tested (e.g. IdempotentOperationChecker, see
http://llvm-reviews.chandlerc.com/D2427).
Fixed a bug, that would disable all analyzer checks, when using a regex more
strict, than 'clang-analyzer-', for example --checks='clang-analyzer-deadcode-'.
Added --list-checks to list all enabled checks. This is useful to test specific
values in --checks/--disable-checks.
Reviewers: djasper, klimek
Reviewed By: klimek
CC: cfe-commits, klimek
Differential Revision: http://llvm-reviews.chandlerc.com/D2444
llvm-svn: 197717
2013-12-20 03:57:05 +08:00
|
|
|
llvm::outs() << "\n\n";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-04 22:23:36 +08:00
|
|
|
if (DumpConfig) {
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
EffectiveOptions.CheckOptions =
|
|
|
|
getCheckOptions(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
|
2015-08-17 18:03:27 +08:00
|
|
|
llvm::outs() << configurationAsText(
|
|
|
|
ClangTidyOptions::getDefaults().mergeWith(
|
2020-04-01 17:08:53 +08:00
|
|
|
EffectiveOptions, 0))
|
2014-09-04 22:23:36 +08:00
|
|
|
<< "\n";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-03 04:32:06 +08:00
|
|
|
if (EnabledChecks.empty()) {
|
|
|
|
llvm::errs() << "Error: no checks enabled.\n";
|
|
|
|
llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
|
2018-03-22 22:18:20 +08:00
|
|
|
return 1;
|
2014-06-03 04:32:06 +08:00
|
|
|
}
|
|
|
|
|
2015-08-17 18:03:27 +08:00
|
|
|
if (PathList.empty()) {
|
|
|
|
llvm::errs() << "Error: no input files specified.\n";
|
|
|
|
llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
|
2018-03-22 22:18:20 +08:00
|
|
|
return 1;
|
2015-08-17 18:03:27 +08:00
|
|
|
}
|
2014-10-24 01:23:20 +08:00
|
|
|
|
2017-10-21 07:00:51 +08:00
|
|
|
llvm::InitializeAllTargetInfos();
|
|
|
|
llvm::InitializeAllTargetMCs();
|
|
|
|
llvm::InitializeAllAsmParsers();
|
|
|
|
|
[clang-tidy] Add a flag to enable alpha checkers
Summary: The alpha checkers can already be enabled using the clang driver, this allows them to be enabled using the clang-tidy as well. This can make it easier to test the alpha checkers with projects which already support the compile_commands.json. It will also allow more people to give feedback and patches about the alpha checkers since they can run it as part of clang tidy checks.
Reviewers: aaron.ballman, hokein, ilya-biryukov, alexfh, lebedev.ri, xbolva00
Reviewed By: aaron.ballman, alexfh, lebedev.ri, xbolva00
Subscribers: xbolva00, NoQ, dcoughlin, lebedev.ri, xazax.hun, cfe-commits
Patch by Paul Fultz II!
Differential Revision: https://reviews.llvm.org/D46159
llvm-svn: 332609
2018-05-17 22:04:27 +08:00
|
|
|
ClangTidyContext Context(std::move(OwningOptionsProvider),
|
|
|
|
AllowEnablingAnalyzerAlphaCheckers);
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
std::vector<ClangTidyError> Errors =
|
2020-06-01 00:41:29 +08:00
|
|
|
runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
EnableCheckProfile, ProfilePrefix);
|
2018-04-09 23:12:10 +08:00
|
|
|
bool FoundErrors = llvm::find_if(Errors, [](const ClangTidyError &E) {
|
|
|
|
return E.DiagLevel == ClangTidyError::Error;
|
|
|
|
}) != Errors.end();
|
2014-11-03 22:06:31 +08:00
|
|
|
|
|
|
|
const bool DisableFixes = Fix && FoundErrors && !FixErrors;
|
|
|
|
|
2016-01-14 01:36:41 +08:00
|
|
|
unsigned WErrorCount = 0;
|
|
|
|
|
2014-11-03 22:06:31 +08:00
|
|
|
// -fix-errors implies -fix.
|
[clang-tidy] Get ClangTidyContext out of the business of storing diagnostics. NFC
Summary:
Currently ClangTidyContext::diag() sends the diagnostics to a
DiagnosticsEngine, which probably delegates to a ClangTidyDiagnosticsConsumer,
which is supposed to go back and populate ClangTidyContext::Errors.
After this patch, the diagnostics are stored in the ClangTidyDiagnosticsConsumer
itself and can be retrieved from there.
Why?
- the round-trip from context -> engine -> consumer -> context is confusing
and makes it harder to establish layering between these things.
- context does too many things, and makes it hard to use clang-tidy as a library
- everyone who actually wants the diagnostics has access to the ClangTidyDiagnosticsConsumer
The most natural implementation (ClangTidyDiagnosticsConsumer::take()
finalizes diagnostics) causes a test failure: clang-tidy-run-with-database.cpp
asserts that clang-tidy exits successfully when trying to process a file
that doesn't exist.
In clang-tidy today, this happens because finish() is never called, so the
diagnostic is never flushed. This looks like a bug to me.
For now, this patch carefully preserves that behavior, but I'll ping the
authors to see whether it's deliberate and worth preserving.
Reviewers: hokein
Subscribers: xazax.hun, cfe-commits, alexfh
Differential Revision: https://reviews.llvm.org/D53953
llvm-svn: 345961
2018-11-02 18:01:59 +08:00
|
|
|
handleErrors(Errors, Context, (FixErrors || Fix) && !DisableFixes, WErrorCount,
|
2018-01-23 20:31:06 +08:00
|
|
|
BaseFS);
|
2013-07-29 16:19:24 +08:00
|
|
|
|
2014-09-04 23:19:49 +08:00
|
|
|
if (!ExportFixes.empty() && !Errors.empty()) {
|
2014-09-04 18:31:23 +08:00
|
|
|
std::error_code EC;
|
2019-08-05 13:43:48 +08:00
|
|
|
llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None);
|
2014-09-04 18:31:23 +08:00
|
|
|
if (EC) {
|
|
|
|
llvm::errs() << "Error opening output file: " << EC.message() << '\n';
|
|
|
|
return 1;
|
|
|
|
}
|
2017-01-03 22:36:13 +08:00
|
|
|
exportReplacements(FilePath.str(), Errors, OS);
|
2014-09-04 18:31:23 +08:00
|
|
|
}
|
|
|
|
|
2017-02-10 02:32:02 +08:00
|
|
|
if (!Quiet) {
|
2017-04-06 21:41:29 +08:00
|
|
|
printStats(Context.getStats());
|
2017-02-10 02:32:02 +08:00
|
|
|
if (DisableFixes)
|
|
|
|
llvm::errs()
|
|
|
|
<< "Found compiler errors, but -fix-errors was not specified.\n"
|
|
|
|
"Fixes have NOT been applied.\n\n";
|
|
|
|
}
|
2014-11-03 22:06:31 +08:00
|
|
|
|
2016-01-14 01:36:41 +08:00
|
|
|
if (WErrorCount) {
|
2017-02-10 02:32:02 +08:00
|
|
|
if (!Quiet) {
|
|
|
|
StringRef Plural = WErrorCount == 1 ? "" : "s";
|
|
|
|
llvm::errs() << WErrorCount << " warning" << Plural << " treated as error"
|
|
|
|
<< Plural << "\n";
|
|
|
|
}
|
2020-06-17 21:35:32 +08:00
|
|
|
return 1;
|
2016-01-14 01:36:41 +08:00
|
|
|
}
|
|
|
|
|
2018-04-09 23:12:10 +08:00
|
|
|
if (FoundErrors) {
|
|
|
|
// TODO: Figure out when zero exit code should be used with -fix-errors:
|
|
|
|
// a. when a fix has been applied for an error
|
|
|
|
// b. when a fix has been applied for all errors
|
|
|
|
// c. some other condition.
|
|
|
|
// For now always returning zero when -fix-errors is used.
|
|
|
|
if (FixErrors)
|
|
|
|
return 0;
|
|
|
|
if (!Quiet)
|
|
|
|
llvm::errs() << "Found compiler error(s).\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-07-29 16:19:24 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-08-04 23:56:30 +08:00
|
|
|
|
|
|
|
} // namespace tidy
|
|
|
|
} // namespace clang
|