diff --git a/clang-tools-extra/clangd/test/check-range.test b/clang-tools-extra/clangd/test/check-range.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/check-range.test @@ -0,0 +1,14 @@ +// RUN: cp %s %t.cpp +// RUN: not clangd -check=%t.cpp -check-range=5-13 2>&1 | FileCheck -strict-whitespace %s + +// CHECK: Testing on source file {{.*}}check-range.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,22 @@ } // Run AST-based features at each token in the file. - void testLocationFeatures() { + void + testLocationFeatures(llvm::Optional> Range) { 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 (Range.hasValue()) { + uint32_t Line = SM.getSpellingLineNumber(Tok.location()); + if (Line < Range.getValue().first || Line > Range.getValue().second) + 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 +237,9 @@ } // namespace -bool check(llvm::StringRef File, const ThreadsafeFS &TFS, - const ClangdLSPServer::Options &Opts) { +bool check(llvm::StringRef File, + llvm::Optional> Range, + const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts) { llvm::SmallString<0> FakeFile; llvm::Optional Contents; if (File.empty()) { @@ -254,7 +263,7 @@ if (!C.buildCommand(TFS) || !C.buildInvocation(TFS, Contents) || !C.buildAST()) return false; - C.testLocationFeatures(); + C.testLocationFeatures(Range); 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::Optional> Range, + const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts); namespace { @@ -347,6 +348,18 @@ ValueOptional, }; +opt CheckFileRange{ + "check-range", + cat(Misc), + desc("If specified, limits the range of tokens in -check file on which " + "various features are tested. Example --check-range=3-7 restricts " + "testing " + "to lines 3 to 7 (inclusive). Default is testing entire " + "file."), + init(""), + ValueOptional, +}; + enum PCHStorageFlag { Disk, Memory }; opt PCHStorage{ "pch-storage", @@ -883,10 +896,26 @@ 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) + llvm::Optional> Range; + if (!CheckFileRange.empty()) { + StringRef RangeStr(CheckFileRange); + uint32_t Begin, End; + if (RangeStr.consumeInteger(0, Begin) || !RangeStr.consume_front("-") || + RangeStr.consumeInteger(0, End) || !RangeStr.empty()) { + elog( + "Invalid --check-range specified. Use Begin-End format, e.g. 3-17"); + return 1; + } + Range = std::make_pair(Begin, End); + } + return check(Path, Range, TFS, Opts) ? 0 : static_cast(ErrorResultCode::CheckFailed); } + if (!CheckFileRange.empty()) { + elog("--check-range requires --check"); + return 1; + } // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows.