diff --git a/clang-tools-extra/clangd/test/check-lines.test b/clang-tools-extra/clangd/test/check-lines.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/check-lines.test @@ -0,0 +1,15 @@ +// RUN: cp %s %t.cpp +// RUN: not clangd -check=%t.cpp -check-lines=6-14 2>&1 | FileCheck -strict-whitespace %s +// RUN: not clangd -check=%t.cpp -check-lines=14 2>&1 | FileCheck -strict-whitespace %s + +// CHECK: Testing on source file {{.*}}check-lines.test +// CHECK: internal (cc1) args are: -cc1 +// CHECK: Building preamble... +// CHECK: Building AST... +// CHECK: Testing features at each token +// CHECK: tweak: ExpandAutoType ==> FAIL +// CHECK: All checks completed, 1 errors + +void fun(); +auto x = fun; // This line is tested +auto y = fun; // This line is not tested diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -192,14 +192,19 @@ } // Run AST-based features at each token in the file. - void testLocationFeatures() { + void testLocationFeatures( + llvm::function_ref ShouldCheckLine) { log("Testing features at each token (may be slow in large files)"); - auto SpelledTokens = - AST->getTokens().spelledTokens(AST->getSourceManager().getMainFileID()); + auto &SM = AST->getSourceManager(); + auto SpelledTokens = AST->getTokens().spelledTokens(SM.getMainFileID()); for (const auto &Tok : SpelledTokens) { unsigned Start = AST->getSourceManager().getFileOffset(Tok.location()); unsigned End = Start + Tok.length(); Position Pos = offsetToPosition(Inputs.Contents, Start); + + if (!ShouldCheckLine(Pos)) + continue; + // FIXME: dumping the tokens may leak sensitive code into bug reports. // Add an option to turn this off, once we decide how options work. vlog(" {0} {1}", Pos, Tok.text(AST->getSourceManager())); @@ -229,8 +234,9 @@ } // namespace -bool check(llvm::StringRef File, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts) { +bool check(llvm::StringRef File, + llvm::function_ref ShouldCheckLine, + const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts) { llvm::SmallString<0> FakeFile; llvm::Optional Contents; if (File.empty()) { @@ -254,7 +260,7 @@ if (!C.buildCommand(TFS) || !C.buildInvocation(TFS, Contents) || !C.buildAST()) return false; - C.testLocationFeatures(); + C.testLocationFeatures(ShouldCheckLine); log("All checks completed, {0} errors", C.ErrCount); return C.ErrCount == 0; diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -60,8 +60,9 @@ namespace clangd { // Implemented in Check.cpp. -bool check(const llvm::StringRef File, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts); +bool check(const llvm::StringRef File, + llvm::function_ref ShouldCheckLine, + const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts); namespace { @@ -347,6 +348,17 @@ ValueOptional, }; +opt CheckFileLines{ + "check-lines", + cat(Misc), + desc("If specified, limits the range of tokens in -check file on which " + "various features are tested. Example --check-lines=3-7 restricts " + "testing to lines 3 to 7 (inclusive) or --check-lines=5 to restrict " + "to one line. Default is testing entire file."), + init(""), + ValueOptional, +}; + enum PCHStorageFlag { Disk, Memory }; opt PCHStorage{ "pch-storage", @@ -883,10 +895,33 @@ llvm::SmallString<256> Path; llvm::sys::fs::real_path(CheckFile, Path, /*expand_tilde=*/true); log("Entering check mode (no LSP server)"); - return check(Path, TFS, Opts) + uint32_t Begin = 0, End = std::numeric_limits::max(); + if (!CheckFileLines.empty()) { + StringRef RangeStr(CheckFileLines); + bool ParseError = RangeStr.consumeInteger(0, Begin); + if (RangeStr.empty()) { + End = Begin; + } else { + ParseError |= !RangeStr.consume_front("-"); + ParseError |= RangeStr.consumeInteger(0, End); + } + if (ParseError || !RangeStr.empty()) { + elog("Invalid --check-line specified. Use Begin-End format, e.g. 3-17"); + return 1; + } + } + auto ShouldCheckLine = [&](const Position &Pos) { + uint32_t Line = Pos.line + 1; // Position::line is 0-based. + return Line >= Begin && Line <= End; + }; + return check(Path, ShouldCheckLine, TFS, Opts) ? 0 : static_cast(ErrorResultCode::CheckFailed); } + if (!CheckFileLines.empty()) { + elog("--check-lines requires --check"); + return 1; + } // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows.