Skip to content

Commit

Permalink
[clangd] Support clang-tidy configuration in clangd.
Browse files Browse the repository at this point in the history
Summary:
This patch adds some basic supports for clang-tidy configurations in clangd:
      - clangd will respect .clang-tidy configurations for each file
      - we don't aim to support all clang-tidy options in clangd, only a
        small subset of condfigurations (options related to which checks will be
        enabled) are supported.
      - add a `clang-tidy-checks` CLI option that can override options from
        .clang-tidy file

Reviewers: ilya-biryukov, sammccall

Reviewed By: sammccall

Subscribers: javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits

Differential Revision: https://reviews.llvm.org/D55256

llvm-svn: 351792
  • Loading branch information
hokein committed Jan 22, 2019
1 parent bd374b2 commit 1ca0c58
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 20 deletions.
4 changes: 3 additions & 1 deletion clang-tools-extra/clangd/ClangdLSPServer.cpp
Expand Up @@ -720,11 +720,13 @@ void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params,
}

ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
const FileSystemProvider &FSProvider,
const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<Path> CompileCommandsDir,
bool UseDirBasedCDB,
const ClangdServer::Options &Opts)
: Transp(Transp), MsgHandler(new MessageHandler(*this)), CCOpts(CCOpts),
: Transp(Transp), MsgHandler(new MessageHandler(*this)),
FSProvider(FSProvider), CCOpts(CCOpts),
SupportedSymbolKinds(defaultSymbolKinds()),
SupportedCompletionItemKinds(defaultCompletionItemKinds()),
UseDirBasedCDB(UseDirBasedCDB),
Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clangd/ClangdLSPServer.h
Expand Up @@ -37,7 +37,8 @@ class ClangdLSPServer : private DiagnosticsConsumer {
/// for compile_commands.json in all parent directories of each file.
/// If UseDirBasedCDB is false, compile commands are not read from disk.
// FIXME: Clean up signature around CDBs.
ClangdLSPServer(Transport &Transp, const clangd::CodeCompleteOptions &CCOpts,
ClangdLSPServer(Transport &Transp, const FileSystemProvider &FSProvider,
const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<Path> CompileCommandsDir, bool UseDirBasedCDB,
const ClangdServer::Options &Opts);
~ClangdLSPServer();
Expand Down Expand Up @@ -128,7 +129,7 @@ class ClangdLSPServer : private DiagnosticsConsumer {
void call(StringRef Method, llvm::json::Value Params);
void notify(StringRef Method, llvm::json::Value Params);

RealFileSystemProvider FSProvider;
const FileSystemProvider &FSProvider;
/// Options used for code completion
clangd::CodeCompleteOptions CCOpts;
/// Options used for diagnostics.
Expand Down
10 changes: 7 additions & 3 deletions clang-tools-extra/clangd/ClangdServer.cpp
Expand Up @@ -105,6 +105,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
DynamicIdx(Opts.BuildDynamicSymbolIndex
? new FileIndex(Opts.HeavyweightDynamicSymbolIndex)
: nullptr),
ClangTidyOptProvider(Opts.ClangTidyOptProvider),
WorkspaceRoot(Opts.WorkspaceRoot),
PCHs(std::make_shared<PCHContainerOperations>()),
// Pass a callback into `WorkScheduler` to extract symbols from a newly
Expand Down Expand Up @@ -140,13 +141,16 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,

void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
WantDiagnostics WantDiags) {
tidy::ClangTidyOptions Options = tidy::ClangTidyOptions::getDefaults();
if (ClangTidyOptProvider)
Options = ClangTidyOptProvider->getOptions(File);
// FIXME: some build systems like Bazel will take time to preparing
// environment to build the file, it would be nice if we could emit a
// "PreparingBuild" status to inform users, it is non-trivial given the
// current implementation.
WorkScheduler.update(File,
ParseInputs{getCompileCommand(File),
FSProvider.getFileSystem(), Contents.str()},
WorkScheduler.update(File, ParseInputs{getCompileCommand(File),
FSProvider.getFileSystem(),
Contents.str(), Options},
WantDiags);
}

Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/clangd/ClangdServer.h
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H

#include "../clang-tidy/ClangTidyOptions.h"
#include "Cancellation.h"
#include "ClangdUnit.h"
#include "CodeComplete.h"
Expand Down Expand Up @@ -92,6 +93,13 @@ class ClangdServer {
/// If set, use this index to augment code completion results.
SymbolIndex *StaticIndex = nullptr;

/// If set, enable clang-tidy in clangd, used to get clang-tidy
/// configurations for a particular file.
/// Clangd supports only a small subset of ClangTidyOptions, these options
/// (Checks, CheckOptions) are about which clang-tidy checks will be
/// enabled.
tidy::ClangTidyOptionsProvider *ClangTidyOptProvider = nullptr;

/// Clangd's workspace root. Relevant for "workspace" operations not bound
/// to a particular file.
/// FIXME: If not set, should use the current working directory.
Expand Down Expand Up @@ -257,6 +265,9 @@ class ClangdServer {
// Storage for merged views of the various indexes.
std::vector<std::unique_ptr<SymbolIndex>> MergedIdx;

// The provider used to provide a clang-tidy option for a specific file.
tidy::ClangTidyOptionsProvider *ClangTidyOptProvider = nullptr;

// GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)
llvm::StringMap<llvm::Optional<FuzzyFindRequest>>
CachedCompletionFuzzyFindRequestByFile;
Expand Down
17 changes: 7 additions & 10 deletions clang-tools-extra/clangd/ClangdUnit.cpp
Expand Up @@ -132,6 +132,9 @@ class ReplayPreamble : private PPCallbacks {
CompilerInstance &Clang) {
auto &PP = Clang.getPreprocessor();
auto *ExistingCallbacks = PP.getPPCallbacks();
// No need to replay events if nobody is listening.
if (!ExistingCallbacks)
return;
PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(
new ReplayPreamble(Includes, ExistingCallbacks,
Clang.getSourceManager(), PP, Clang.getLangOpts())));
Expand Down Expand Up @@ -227,7 +230,8 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI,
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::shared_ptr<PCHContainerOperations> PCHs,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const tidy::ClangTidyOptions &ClangTidyOpts) {
assert(CI);
// Command-line parsing sets DisableFree to true by default, but we don't want
// to leak memory in clangd.
Expand Down Expand Up @@ -264,15 +268,8 @@ ParsedAST::build(std::unique_ptr<CompilerInvocation> CI,
tidy::ClangTidyCheckFactories CTFactories;
for (const auto &E : tidy::ClangTidyModuleRegistry::entries())
E.instantiate()->addCheckFactories(CTFactories);
auto CTOpts = tidy::ClangTidyOptions::getDefaults();
// FIXME: this needs to be configurable, and we need to support .clang-tidy
// files and other options providers.
// These checks exercise the matcher- and preprocessor-based hooks.
CTOpts.Checks = "bugprone-sizeof-expression,"
"bugprone-macro-repeated-side-effects,"
"modernize-deprecated-headers";
CTContext.emplace(llvm::make_unique<tidy::DefaultOptionsProvider>(
tidy::ClangTidyGlobalOptions(), CTOpts));
tidy::ClangTidyGlobalOptions(), ClangTidyOpts));
CTContext->setDiagnosticsEngine(&Clang->getDiagnostics());
CTContext->setASTContext(&Clang->getASTContext());
CTContext->setCurrentFile(MainInput.getFile());
Expand Down Expand Up @@ -538,7 +535,7 @@ buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation,
return ParsedAST::build(llvm::make_unique<CompilerInvocation>(*Invocation),
Preamble,
llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents),
PCHs, std::move(VFS));
PCHs, std::move(VFS), Inputs.ClangTidyOpts);
}

SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
Expand Down
5 changes: 4 additions & 1 deletion clang-tools-extra/clangd/ClangdUnit.h
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H

#include "../clang-tidy/ClangTidyOptions.h"
#include "Diagnostics.h"
#include "FS.h"
#include "Function.h"
Expand Down Expand Up @@ -64,6 +65,7 @@ struct ParseInputs {
tooling::CompileCommand CompileCommand;
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
std::string Contents;
tidy::ClangTidyOptions ClangTidyOpts;
};

/// Stores and provides access to parsed AST.
Expand All @@ -76,7 +78,8 @@ class ParsedAST {
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::shared_ptr<PCHContainerOperations> PCHs,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS);
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const tidy::ClangTidyOptions &ClangTidyOpts);

ParsedAST(ParsedAST &&Other);
ParsedAST &operator=(ParsedAST &&Other);
Expand Down
17 changes: 16 additions & 1 deletion clang-tools-extra/clangd/tool/ClangdMain.cpp
Expand Up @@ -201,6 +201,12 @@ static llvm::cl::opt<bool> EnableFunctionArgSnippets(
"placeholders for method parameters."),
llvm::cl::init(CodeCompleteOptions().EnableFunctionArgSnippets));

static llvm::cl::opt<std::string> ClangTidyChecks(
"clang-tidy-checks",
llvm::cl::desc("List of clang-tidy checks to run (this will overrides "
".clang-tidy files)"),
llvm::cl::init(""), llvm::cl::Hidden);

namespace {

/// \brief Supports a test URI scheme with relaxed constraints for lit tests.
Expand Down Expand Up @@ -408,6 +414,7 @@ int main(int argc, char *argv[]) {
CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
CCOpts.AllScopes = AllScopesCompletion;

RealFileSystemProvider FSProvider;
// Initialize and run ClangdLSPServer.
// Change stdin to binary to not lose \r\n on windows.
llvm::sys::ChangeStdinToBinary();
Expand All @@ -427,8 +434,16 @@ int main(int argc, char *argv[]) {
PrettyPrint, InputStyle);
}

// Create an empty clang-tidy option.
auto OverrideClangTidyOptions = tidy::ClangTidyOptions::getDefaults();
OverrideClangTidyOptions.Checks = ClangTidyChecks;
tidy::FileOptionsProvider ClangTidyOptProvider(
tidy::ClangTidyGlobalOptions(),
/* Default */ tidy::ClangTidyOptions::getDefaults(),
/* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem());
Opts.ClangTidyOptProvider = &ClangTidyOptProvider;
ClangdLSPServer LSPServer(
*TransportLayer, CCOpts, CompileCommandsDirPath,
*TransportLayer, FSProvider, CCOpts, CompileCommandsDirPath,
/*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, Opts);
llvm::set_thread_name("clangd.main");
return LSPServer.run() ? 0
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/unittests/clangd/ClangdUnitTests.cpp
Expand Up @@ -141,6 +141,9 @@ TEST(DiagnosticsTest, ClangTidy) {
)cpp");
auto TU = TestTU::withCode(Test.code());
TU.HeaderFilename = "assert.h"; // Suppress "not found" error.
TU.ClangTidyChecks =
"-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, "
"modernize-deprecated-headers";
EXPECT_THAT(
TU.build().getDiagnostics(),
UnorderedElementsAre(
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/unittests/clangd/FileIndexTests.cpp
Expand Up @@ -363,7 +363,8 @@ TEST(FileIndexTest, ReferencesInMainFileWithPreamble) {
auto AST =
ParsedAST::build(createInvocationFromCommandLine(Cmd), PreambleData,
llvm::MemoryBuffer::getMemBufferCopy(Main.code()),
std::make_shared<PCHContainerOperations>(), PI.FS);
std::make_shared<PCHContainerOperations>(), PI.FS,
tidy::ClangTidyOptions::getDefaults());
ASSERT_TRUE(AST);
FileIndex Index;
Index.updateMain(MainFile, *AST);
Expand Down
3 changes: 2 additions & 1 deletion clang-tools-extra/unittests/clangd/TUSchedulerTests.cpp
Expand Up @@ -38,7 +38,8 @@ class TUSchedulerTests : public ::testing::Test {
protected:
ParseInputs getInputs(PathRef File, std::string Contents) {
return ParseInputs{*CDB.getCompileCommand(File),
buildTestFS(Files, Timestamps), std::move(Contents)};
buildTestFS(Files, Timestamps), std::move(Contents),
tidy::ClangTidyOptions::getDefaults()};
}

void updateWithCallback(TUScheduler &S, PathRef File,
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/unittests/clangd/TestTU.cpp
Expand Up @@ -35,6 +35,8 @@ ParsedAST TestTU::build() const {
Inputs.CompileCommand.Directory = testRoot();
Inputs.Contents = Code;
Inputs.FS = buildTestFS({{FullFilename, Code}, {FullHeaderName, HeaderCode}});
Inputs.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults();
Inputs.ClangTidyOpts.Checks = ClangTidyChecks;
auto PCHs = std::make_shared<PCHContainerOperations>();
auto CI = buildCompilerInvocation(Inputs);
assert(CI && "Failed to build compilation invocation.");
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/unittests/clangd/TestTU.h
Expand Up @@ -48,6 +48,8 @@ struct TestTU {
// Extra arguments for the compiler invocation.
std::vector<const char *> ExtraArgs;

llvm::Optional<std::string> ClangTidyChecks;

ParsedAST build() const;
SymbolSlab headerSymbols() const;
std::unique_ptr<SymbolIndex> index() const;
Expand Down

0 comments on commit 1ca0c58

Please sign in to comment.