CodeAction::INFO_KIND}}}
: llvm::json::Value(true);
-
std::vector<llvm::StringRef> Commands;
for (llvm::StringRef Command : Handlers.CommandHandlers.keys())
Commands.push_back(Command);
}
void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) {
+ llvm::ArrayRef<Diag> Diagnostics) {
PublishDiagnosticsParams Notification;
Notification.version = decodeVersion(Version);
Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File);
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
#include "ClangdServer.h"
+#include "Diagnostics.h"
#include "GlobalCompilationDatabase.h"
#include "LSPBinder.h"
#include "Protocol.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/JSON.h"
#include <chrono>
#include <cstddef>
private:
// Implement ClangdServer::Callbacks.
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override;
+ llvm::ArrayRef<Diag> Diagnostics) override;
void onFileUpdated(PathRef File, const TUStatus &Status) override;
void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
void onSemanticsMaybeChanged(PathRef File) override;
void bindMethods(LSPBinder &, const ClientCapabilities &Caps);
std::vector<CodeAction> getFixes(StringRef File, const clangd::Diagnostic &D);
-
/// Checks if completion request should be ignored. We need this due to the
/// limitation of the LSP. Per LSP, a client sends requests for all "trigger
/// character" we specify, but for '>' and ':' we need to check they actually
if (FIndex)
FIndex->updateMain(Path, AST);
- assert(AST.getDiagnostics() &&
- "We issue callback only with fresh preambles");
- std::vector<Diag> Diagnostics = *AST.getDiagnostics();
if (ServerCallbacks)
Publish([&]() {
ServerCallbacks->onDiagnosticsReady(Path, AST.version(),
- std::move(Diagnostics));
+ AST.getDiagnostics());
if (CollectInactiveRegions) {
ServerCallbacks->onInactiveRegionsReady(Path,
getInactiveRegions(AST));
std::lock_guard<std::mutex> Lock(PublishMu);
for (auto &Entry : ReportableDiagnostics)
Publish->onDiagnosticsReady(Entry.first(), /*Version=*/"",
- std::move(Entry.second));
+ Entry.second);
}
return Context::current().derive(Config::Key, std::move(C));
}
[CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
- if (auto Diags = InpAST->AST.getDiagnostics())
- return CB(*Diags);
- // FIXME: Use ServerCancelled error once it is settled in LSP-3.17.
- return CB(llvm::make_error<LSPError>("server is busy parsing includes",
- ErrorCode::InternalError));
+ return CB(InpAST->AST.getDiagnostics());
};
WorkScheduler->runWithAST("Diagnostics", File, std::move(Action));
#include "support/Path.h"
#include "support/ThreadsafeFS.h"
#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringRef.h"
#include <functional>
/// file, they do not interfere with "pull-based" ClangdServer::diagnostics.
/// May be called concurrently for separate files, not for a single file.
virtual void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) {}
+ llvm::ArrayRef<Diag> Diagnostics) {}
/// Called whenever the file status is updated.
/// May be called concurrently for separate files, not for a single file.
virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
llvm::StringMap<std::string> CheckOptions;
} ClangTidy;
- /// Enable emitting diagnostics using stale preambles.
- bool AllowStalePreamble = false;
-
IncludesPolicy UnusedIncludes = IncludesPolicy::Strict;
IncludesPolicy MissingIncludes = IncludesPolicy::None;
}
}
- if (F.AllowStalePreamble) {
- if (auto Val = F.AllowStalePreamble)
- Out.Apply.push_back([Val](const Params &, Config &C) {
- C.Diagnostics.AllowStalePreamble = **Val;
- });
- }
-
if (F.MissingIncludes)
if (auto Val = compileEnum<Config::IncludesPolicy>("MissingIncludes",
**F.MissingIncludes)
/// - std::nullopt
std::optional<Located<std::string>> UnusedIncludes;
- /// Enable emitting diagnostics using stale preambles.
- std::optional<Located<bool>> AllowStalePreamble;
-
/// Controls if clangd should analyze missing #include directives.
/// clangd will warn if no header providing a symbol is `#include`d
/// (missing) directly, and suggest adding it.
});
Dict.handle("Includes", [&](Node &N) { parse(F.Includes, N); });
Dict.handle("ClangTidy", [&](Node &N) { parse(F.ClangTidy, N); });
- Dict.handle("AllowStalePreamble", [&](Node &N) {
- F.AllowStalePreamble = boolValue(N, "AllowStalePreamble");
- });
Dict.parse(N);
}
});
std::optional<PreamblePatch> Patch;
- bool PreserveDiags = true;
// We might use an ignoring diagnostic consumer if they are going to be
// dropped later on to not pay for extra latency by processing them.
DiagnosticConsumer *DiagConsumer = &ASTDiags;
if (Preamble) {
Patch = PreamblePatch::createFullPatch(Filename, Inputs, *Preamble);
Patch->apply(*CI);
- PreserveDiags = Patch->preserveDiagnostics();
- if (!PreserveDiags)
- DiagConsumer = &DropDiags;
}
auto Clang = prepareCompilerInstance(
std::move(CI), PreamblePCH,
return std::nullopt;
}
tidy::ClangTidyOptions ClangTidyOpts;
- if (PreserveDiags) {
+ {
trace::Span Tracer("ClangTidyOpts");
ClangTidyOpts = getTidyOptionsForFile(Inputs.ClangTidyProvider, Filename);
dlog("ClangTidy configuration for file {0}: {1}", Filename,
applyWarningOptions(*ClangTidyOpts.ExtraArgsBefore, TidyGroups, Diags);
if (ClangTidyOpts.ExtraArgs)
applyWarningOptions(*ClangTidyOpts.ExtraArgs, TidyGroups, Diags);
- } else {
- // Skips some analysis.
- Clang->getDiagnosticOpts().IgnoreWarnings = true;
}
auto Action = std::make_unique<ClangdFrontendAction>();
llvm::DenseMap<diag::kind, DiagnosticsEngine::Level> OverriddenSeverity;
// No need to run clang-tidy or IncludeFixerif we are not going to surface
// diagnostics.
- if (PreserveDiags) {
+ {
trace::Span Tracer("ClangTidyInit");
static const auto *CTFactories = [] {
auto *CTFactories = new tidy::ClangTidyCheckFactories;
// CompilerInstance won't run this callback, do it directly.
ASTDiags.EndSourceFile();
- std::optional<std::vector<Diag>> Diags;
+ std::vector<Diag> Diags = CompilerInvocationDiags;
// FIXME: Also skip generation of diagnostics alltogether to speed up ast
// builds when we are patching a stale preamble.
- if (PreserveDiags) {
- Diags = CompilerInvocationDiags;
- // Add diagnostics from the preamble, if any.
- if (Preamble)
- llvm::append_range(*Diags, Patch->patchedDiags());
- // Finally, add diagnostics coming from the AST.
- {
- std::vector<Diag> D = ASTDiags.take(&*CTContext);
- Diags->insert(Diags->end(), D.begin(), D.end());
- }
+ // Add diagnostics from the preamble, if any.
+ if (Preamble)
+ llvm::append_range(Diags, Patch->patchedDiags());
+ // Finally, add diagnostics coming from the AST.
+ {
+ std::vector<Diag> D = ASTDiags.take(&*CTContext);
+ Diags.insert(Diags.end(), D.begin(), D.end());
}
ParsedAST Result(Filename, Inputs.Version, std::move(Preamble),
std::move(Clang), std::move(Action), std::move(Tokens),
std::move(Macros), std::move(Marks), std::move(ParsedDecls),
std::move(Diags), std::move(Includes),
std::move(CanonIncludes));
- if (Result.Diags)
- llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
- std::back_inserter(*Result.Diags));
+ llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
+ std::back_inserter(Result.Diags));
return std::move(Result);
}
auto &AST = getASTContext();
// FIXME(ibiryukov): we do not account for the dynamically allocated part of
// Message and Fixes inside each diagnostic.
- std::size_t Total = clangd::getUsedBytes(LocalTopLevelDecls) +
- (Diags ? clangd::getUsedBytes(*Diags) : 0);
+ std::size_t Total =
+ clangd::getUsedBytes(LocalTopLevelDecls) + clangd::getUsedBytes(Diags);
// FIXME: the rest of the function is almost a direct copy-paste from
// libclang's clang_getCXTUResourceUsage. We could share the implementation.
syntax::TokenBuffer Tokens, MainFileMacros Macros,
std::vector<PragmaMark> Marks,
std::vector<Decl *> LocalTopLevelDecls,
- std::optional<std::vector<Diag>> Diags,
- IncludeStructure Includes, CanonicalIncludes CanonIncludes)
+ std::vector<Diag> Diags, IncludeStructure Includes,
+ CanonicalIncludes CanonIncludes)
: TUPath(TUPath), Version(Version), Preamble(std::move(Preamble)),
Clang(std::move(Clang)), Action(std::move(Action)),
Tokens(std::move(Tokens)), Macros(std::move(Macros)),
return llvm::StringRef(Preamble->Version);
}
+llvm::ArrayRef<Diag> ParsedAST::getDiagnostics() const { return Diags; }
} // namespace clangd
} // namespace clang
ArrayRef<Decl *> getLocalTopLevelDecls();
ArrayRef<const Decl *> getLocalTopLevelDecls() const;
- const std::optional<std::vector<Diag>> &getDiagnostics() const {
- return Diags;
- }
+ llvm::ArrayRef<Diag> getDiagnostics() const;
/// Returns the estimated size of the AST and the accessory structures, in
/// bytes. Does not include the size of the preamble.
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens,
MainFileMacros Macros, std::vector<PragmaMark> Marks,
- std::vector<Decl *> LocalTopLevelDecls,
- std::optional<std::vector<Diag>> Diags, IncludeStructure Includes,
- CanonicalIncludes CanonIncludes);
+ std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
+ IncludeStructure Includes, CanonicalIncludes CanonIncludes);
Path TUPath;
std::string Version;
MainFileMacros Macros;
// Pragma marks in the main file.
std::vector<PragmaMark> Marks;
- // Data, stored after parsing. std::nullopt if AST was built with a stale
- // preamble.
- std::optional<std::vector<Diag>> Diags;
+ // Diags emitted while parsing this AST (including preamble and compiler
+ // invocation).
+ std::vector<Diag> Diags;
// Top-level decls inside the current file. Not that this does not include
// top-level decls from the preamble.
std::vector<Decl *> LocalTopLevelDecls;
return PP;
}
-bool PreamblePatch::preserveDiagnostics() const {
- return PatchContents.empty() ||
- Config::current().Diagnostics.AllowStalePreamble;
-}
llvm::ArrayRef<PragmaMark> PreamblePatch::marks() const {
if (PatchContents.empty())
return Baseline->Marks;
/// Returns textual patch contents.
llvm::StringRef text() const { return PatchContents; }
- /// Whether diagnostics generated using this patch are trustable.
- bool preserveDiagnostics() const;
-
/// Returns diag locations for Modified contents.
llvm::ArrayRef<Diag> patchedDiags() const { return PatchedDiags; }
// rebuild. Newly built preamble cannot emit diagnostics before this call
// finishes (ast callbacks are called from astpeer thread), hence we
// gurantee eventual consistency.
- if (LatestPreamble && WantDiags != WantDiagnostics::No &&
- Config::current().Diagnostics.AllowStalePreamble)
+ if (LatestPreamble && WantDiags != WantDiagnostics::No)
generateDiagnostics(std::move(Invocation), std::move(Inputs),
std::move(CompilerInvocationDiags));
llvm::StringLiteral TaskName = "Build AST";
// Store preamble and build diagnostics with new preamble if requested.
auto Task = [this, Preamble = std::move(Preamble), CI = std::move(CI),
- PI = std::move(PI), CIDiags = std::move(CIDiags),
+ CIDiags = std::move(CIDiags),
WantDiags = std::move(WantDiags)]() mutable {
// Update the preamble inside ASTWorker queue to ensure atomicity. As a task
// running inside ASTWorker assumes internals won't change until it
// We only need to build the AST if diagnostics were requested.
if (WantDiags == WantDiagnostics::No)
return;
- // The file may have been edited since we started building this preamble.
- // If diagnostics need a fresh preamble, we must use the old version that
- // matches the preamble. We make forward progress as updatePreamble()
- // receives increasing versions, and this is the only place we emit
- // diagnostics.
- // If diagnostics can use a stale preamble, we use the current contents of
- // the file instead. This provides more up-to-date diagnostics, and avoids
- // diagnostics going backwards (we may have already emitted staler-preamble
- // diagnostics for the new version). We still have eventual consistency: at
- // some point updatePreamble() will catch up to the current file.
- if (Config::current().Diagnostics.AllowStalePreamble)
- PI = FileInputs;
+ // Since the file may have been edited since we started building this
+ // preamble, we use the current contents of the file instead. This provides
+ // more up-to-date diagnostics, and avoids diagnostics going backwards (we
+ // may have already emitted staler-preamble diagnostics for the new
+ // version).
+ // We still have eventual consistency: at some point updatePreamble() will
+ // catch up to the current file.
// Report diagnostics with the new preamble to ensure progress. Otherwise
// diagnostics might get stale indefinitely if user keeps invalidating the
// preamble.
- generateDiagnostics(std::move(CI), std::move(PI), std::move(CIDiags));
+ generateDiagnostics(std::move(CI), FileInputs, std::move(CIDiags));
};
if (RunSync) {
runTask(TaskName, Task);
elog("Failed to build AST");
return false;
}
- ErrCount += showErrors(llvm::ArrayRef(*AST->getDiagnostics())
- .drop_front(Preamble->Diags.size()));
+ ErrCount +=
+ showErrors(AST->getDiagnostics().drop_front(Preamble->Diags.size()));
if (Opts.BuildDynamicSymbolIndex) {
log("Indexing AST...");
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
class ErrorCheckingCallbacks : public ClangdServer::Callbacks {
public:
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
bool HadError = diagsContainErrors(Diagnostics);
std::lock_guard<std::mutex> Lock(Mutex);
HadErrorInLastDiags = HadError;
class MultipleErrorCheckingCallbacks : public ClangdServer::Callbacks {
public:
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
bool HadError = diagsContainErrors(Diagnostics);
std::lock_guard<std::mutex> Lock(Mutex);
} FS;
struct Callbacks : public ClangdServer::Callbacks {
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
Got = Context::current().getExisting(Secret);
}
int Got;
MockFS FS;
struct Callbacks : public ClangdServer::Callbacks {
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
Got = Version.str();
}
std::string Got = "";
TestDiagConsumer() : Stats(FilesCount, FileStat()) {}
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
StringRef FileIndexStr = llvm::sys::path::stem(File);
ASSERT_TRUE(FileIndexStr.consume_front("Foo"));
: StartSecondReparse(std::move(StartSecondReparse)) {}
void onDiagnosticsReady(PathRef, llvm::StringRef,
- std::vector<Diag>) override {
+ llvm::ArrayRef<Diag>) override {
++Count;
std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock_t());
ASSERT_TRUE(Lock.owns_lock())
struct DiagsCheckingCallback : public ClangdServer::Callbacks {
public:
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
- std::vector<Diag> Diagnostics) override {
+ llvm::ArrayRef<Diag> Diagnostics) override {
std::lock_guard<std::mutex> Lock(Mutex);
HadDiagsInLastCallback = !Diagnostics.empty();
}
EXPECT_TRUE(compileAndApply());
EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar"));
}
-
-TEST_F(ConfigCompileTests, AllowDiagsFromStalePreamble) {
- Frag = {};
- EXPECT_TRUE(compileAndApply());
- // Off by default.
- EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, false);
-
- Frag.Diagnostics.AllowStalePreamble.emplace(true);
- EXPECT_TRUE(compileAndApply());
- EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, true);
-
- Frag.Diagnostics.AllowStalePreamble.emplace(false);
- EXPECT_TRUE(compileAndApply());
- EXPECT_EQ(Conf.Diagnostics.AllowStalePreamble, false);
-}
} // namespace
} // namespace config
} // namespace clangd
EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces,
ElementsAre(val("foo"), val("bar")));
}
-
-TEST(ParseYAML, DiagnosticsMode) {
- CapturedDiags Diags;
- {
- Annotations YAML(R"yaml(
-Diagnostics:
- AllowStalePreamble: Yes)yaml");
- auto Results =
- Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
- ASSERT_THAT(Diags.Diagnostics, IsEmpty());
- ASSERT_EQ(Results.size(), 1u);
- EXPECT_THAT(Results[0].Diagnostics.AllowStalePreamble,
- llvm::ValueIs(val(true)));
- }
-
- {
- Annotations YAML(R"yaml(
-Diagnostics:
- AllowStalePreamble: No)yaml");
- auto Results =
- Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
- ASSERT_THAT(Diags.Diagnostics, IsEmpty());
- ASSERT_EQ(Results.size(), 1u);
- EXPECT_THAT(Results[0].Diagnostics.AllowStalePreamble,
- llvm::ValueIs(val(false)));
- }
-}
} // namespace
} // namespace config
} // namespace clangd
)cpp");
auto TU = TestTU::withCode(Test.code());
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(
// Make sure the whole token is highlighted.
AllOf(Diag(Test.range("range"),
)cpp");
auto TU = TestTU::withCode(Test.code());
TU.ExtraArgs = {"-Wswitch"};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(Diag(Test.range(),
"enumeration value 'X' not handled in switch")));
}
TEST(DiagnosticsTest, FlagsMatter) {
Annotations Test("[[void]] main() {} // error-ok");
auto TU = TestTU::withCode(Test.code());
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
withFix(Fix(Test.range(), "int",
"change 'void' to 'int'")))));
// Same code built as C gets different diagnostics.
TU.Filename = "Plain.c";
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(), "return type of 'main' is not 'int'"),
withFix(Fix(Test.range(), "int", "change return type to 'int'")))));
)cpp");
auto TU = TestTU::withCode(Test.code());
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(::testing::AllOf(
Diag(Test.range(), "'not-found.h' file not found"),
diagSource(Diag::Clang), diagName("pp_file_not_found"))));
"hicpp-uppercase-literal-suffix");
// Verify that we filter out the duplicated diagnostic message.
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Test.range(),
"floating point literal has suffix 'f', which is not uppercase"),
// The check doesn't handle template instantiations which ends up emitting
// duplicated messages, verify that we deduplicate them.
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Test.range(),
"floating point literal has suffix 'f', which is not uppercase"),
"misc-no-recursion");
TU.ExtraArgs.push_back("-Wno-unsequenced");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(
AllOf(Diag(Test.range("deprecated"),
"inclusion of deprecated C++ header 'assert.h'; consider "
TU.AdditionalFiles["a.h"] = TU.AdditionalFiles["b.h"] = "";
TU.ClangTidyProvider = addTidyChecks("llvm-include-order");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(Contains(
AllOf(Diag(Test.range(), "#includes are not sorted properly"),
diagSource(Diag::ClangTidy), diagName("llvm-include-order")))));
TestTU TU = TestTU::withCode(Main.code());
TU.HeaderCode = Header.code().str();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Main.range(), "in template: base specifier must name a class"),
withNote(Diag(Header.range(), "error occurred here"),
}
}
)cpp";
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Main.range(),
"in template: "
}
)cpp";
TU.ParseOpts.PreambleParseForwardingFunctions = true;
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Main.range(),
"in template: "
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "use range-based for loop instead"),
diagSource(Diag::ClangTidy), diagName("modernize-loop-convert")))));
)cpp");
auto TU = TestTU::withCode(Main.code());
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(Diag(Main.range(), "use of undeclared identifier 'unknown'"),
Diag(Main.range("ret"),
"void function 'x' should not return a value")));
Config Cfg;
Cfg.Diagnostics.Suppress.insert("return-type");
WithContextValue WithCfg(Config::Key, std::move(Cfg));
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(Diag(Main.range(),
"use of undeclared identifier 'unknown'")));
}
Config Cfg;
Cfg.Diagnostics.Suppress.insert("init_conversion_failed");
WithContextValue WithCfg(Config::Key, std::move(Cfg));
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagnosticTest, ClangTidySuppressionComment) {
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "result of integer division used in a floating "
"point context; possible loss of precision"),
// Expect to see warning from user macros, but not system macros.
// This matches clang-tidy --system-headers=0 (the default).
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ifTidyChecks(
UnorderedElementsAre(Diag(Main.range("inline"), BadDivision),
Diag(Main.range("user"), BadDivision))));
TU.ClangTidyProvider =
addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "result of integer division used in a floating "
"point context; possible loss of precision"),
diagSeverity(DiagnosticsEngine::Warning));
// Check the -Wunused warning isn't initially on.
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
// We enable warnings based on clang-tidy extra args, if the matching
// clang-diagnostic- is there.
TU.ClangTidyProvider =
addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+ EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
// clang-diagnostic-* is acceptable
TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-*");
- EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
+ EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning));
// And plain * (may turn on other checks too).
TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "*");
- EXPECT_THAT(*TU.build().getDiagnostics(), Contains(UnusedFooWarning));
+ EXPECT_THAT(TU.build().getDiagnostics(), Contains(UnusedFooWarning));
// And we can explicitly exclude a category too.
TU.ClangTidyProvider = addClangArgs(
{"-Wunused"}, "clang-diagnostic-*,-clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
// Without the exact check specified, the warnings are not enabled.
TU.ClangTidyProvider = addClangArgs({"-Wunused"}, "clang-diagnostic-unused");
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
// We don't respect other args.
TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Dfoo=bar"},
"clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
+ EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(UnusedFooWarning))
<< "Not unused function 'bar'!";
// -Werror doesn't apply to warnings enabled by clang-tidy extra args.
TU.ExtraArgs = {"-Werror"};
TU.ClangTidyProvider =
addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
// But clang-tidy extra args won't *downgrade* errors to warnings either.
TU.ExtraArgs = {"-Wunused", "-Werror"};
TU.ClangTidyProvider =
addClangArgs({"-Wunused"}, "clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(diagSeverity(DiagnosticsEngine::Error)));
// FIXME: we're erroneously downgrading the whole group, this should be Error.
TU.ExtraArgs = {"-Wunused-function", "-Werror"};
TU.ClangTidyProvider =
addClangArgs({"-Wunused"}, "clang-diagnostic-unused-label");
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(diagSeverity(DiagnosticsEngine::Warning)));
// This looks silly, but it's the typical result if a warning is enabled by a
TU.ExtraArgs = {};
TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused"},
"clang-diagnostic-unused-function");
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
// Overriding only works in the proper order.
TU.ClangTidyProvider =
addClangArgs({"-Wunused"}, {"clang-diagnostic-unused-function"});
- EXPECT_THAT(*TU.build().getDiagnostics(), SizeIs(1));
+ EXPECT_THAT(TU.build().getDiagnostics(), SizeIs(1));
// More specific vs less-specific: match clang behavior
TU.ClangTidyProvider = addClangArgs({"-Wunused", "-Wno-unused-function"},
{"clang-diagnostic-unused-function"});
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
TU.ClangTidyProvider = addClangArgs({"-Wunused-function", "-Wno-unused"},
{"clang-diagnostic-unused-function"});
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
// We do allow clang-tidy config to disable warnings from the compile
// command. It's unclear this is ideal, but it's hard to avoid.
TU.ExtraArgs = {"-Wunused"};
TU.ClangTidyProvider = addClangArgs({"-Wno-unused"}, {});
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagnosticTest, LongFixMessages) {
)cpp");
TestTU TU = TestTU::withCode(Source.code());
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(withFix(Fix(
Source.range(),
"somereallyreallyreallyreallyreallyreallyreallyreallylongidentifier",
}
)cpp");
TU.Code = std::string(Source.code());
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(withFix(
Fix(Source.range(), "ident", "change 'ide\\…' to 'ident'"))));
}
TestTU TU = TestTU::withCode(Source.code());
TU.ExtraArgs = {"-Wnewline-eof"};
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(withFix((Fix(Source.range(), "\n", "insert '\\n'")))));
}
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider =
addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
- EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre());
+ EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
}
TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) {
)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread");
- EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
+ EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash
}
TEST(DiagnosticTest, ElseAfterReturnRange) {
)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ifTidyChecks(ElementsAre(
Diag(Main.range(), "do not use 'else' after 'return'"))));
}
ExpectedDFix.Edits.push_back(
TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"});
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(
AllOf(Diag(Main.range("A"), "'A' should be initialized in a member "
"initializer of the constructor"),
#endif
)cpp");
EXPECT_THAT(
- *TestTU::withCode(Test.code()).build().getDiagnostics(),
+ TestTU::withCode(Test.code()).build().getDiagnostics(),
ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
}
)cpp");
TU.ExtraArgs.push_back("-Xclang");
TU.ExtraArgs.push_back("-verify");
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagnosticTest, IgnoreBEFilelistOptions) {
"-fxray-always-instrument=null", "-fxray-never-instrument=null",
"-fxray-attr-list=null"}) {
TU.ExtraArgs.push_back(DisableOption);
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
TU.ExtraArgs.pop_back();
}
}
int symbol;
)cpp");
TU.Filename = "foo.h";
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(diagName("pp_including_mainfile_in_preamble")));
EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
}
int symbol;
)cpp");
TU.Filename = "foo.h";
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
Not(Contains(diagName("pp_including_mainfile_in_preamble"))));
EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
}
)cpp");
TU.Filename = "foo.h";
// FIXME: should be no errors here.
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(diagName("pp_including_mainfile_in_preamble")));
EXPECT_THAT(TU.build().getLocalTopLevelDecls(), SizeIs(1));
}
#pragma clang assume_nonnull end
)cpp");
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
ASSERT_TRUE(X->getOriginalType()->getNullability() ==
NullabilityKind::NonNull);
)cpp");
TU.AdditionalFiles = {{"foo.h", std::string(Header.code())}};
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diagName("pp_eof_in_assume_nonnull")));
const auto *X = cast<FunctionDecl>(findDecl(AST, "foo")).getParamDecl(0);
ASSERT_TRUE(X->getOriginalType()->getNullability() ==
return $bar[[TEN]];
}
)cpp");
- EXPECT_THAT(*TestTU::withCode(Test.code()).build().getDiagnostics(),
+ EXPECT_THAT(TestTU::withCode(Test.code()).build().getDiagnostics(),
ElementsAre(Diag(Test.range("foo"),
"cannot initialize return object of type "
"'int *' with an rvalue of type 'int'"),
[[Define]](main) // error-ok
)cpp");
auto TU = TestTU::withCode(Test.code());
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(AllOf(Diag(Test.range(), "'main' must return 'int'"),
Not(withFix(_)))));
}
Annotations Test("#pragma clang [[system_header]]\n");
auto TU = TestTU::withCode(Test.code());
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(), "#pragma system_header ignored in main file"))));
TU.Filename = "TestTU.h";
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(ClangdTest, MSAsm) {
llvm::InitializeAllTargetInfos(); // As in ClangdMain
auto TU = TestTU::withCode("void fn() { __asm { cmp cl,64 } }");
TU.ExtraArgs = {"-fms-extensions"};
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagnosticsTest, ToLSP) {
Annotations Main(Case.second);
TU.Code = Main.code().str() + "\n // error-ok";
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(diagName(Case.first), hasRange(Main.range()),
withFix(Fix(Range{}, "#include \"x.h\"\n",
"Include \"x.h\" for symbol ns::X")))))
for (auto Case : Tests) {
Annotations Main(Case.second);
TU.Code = Main.code().str() + "\n // error-ok";
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
Contains(AllOf(diagName(Case.first), hasRange(Main.range()),
withFix(Fix(Range{}, "#include \"x.h\"\n",
"Include \"x.h\" for symbol X")))))
MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
TU.ExternalIndex = Index.get();
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Test.range("base"), "base class has incomplete type"),
Diag(Test.range("access"),
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
UnorderedElementsAre(
AllOf(Diag(Test.range("unqualified1"), "unknown type name 'X'"),
diagName("unknown_typename"),
// FIXME: -fms-compatibility (which is default on windows) breaks the
// ns::X cases when the namespace is undeclared. Find out why!
TU.ExtraArgs = {"-fno-ms-compatibility"};
- EXPECT_THAT(*TU.build().getDiagnostics(), Each(withFix(_)));
+ EXPECT_THAT(TU.build().getDiagnostics(), Each(withFix(_)));
}
TEST(IncludeFixerTest, MultipleMatchedSymbols) {
SymbolWithHeader{"na::nb::X", "unittest:///b.h", "\"b.h\""}});
TU.ExternalIndex = Index.get();
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Test.range("unqualified"), "unknown type name 'X'"),
diagName("unknown_typename"),
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
UnorderedElementsAre(Diag(Test.range(), "no member named 'xy' in 'X'")));
}
TU.ExternalIndex = Index.get();
auto Parsed = TU.build();
- for (const auto &D : *Parsed.getDiagnostics()) {
+ for (const auto &D : Parsed.getDiagnostics()) {
if (D.Fixes.size() != 1) {
ADD_FAILURE() << "D.Fixes.size() != 1";
continue;
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
UnorderedElementsAre(
AllOf(Diag(Test.range(), "no member named 'scope' in namespace 'ns'"),
diagName("no_member"),
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
UnorderedElementsAre(
AllOf(Diag(Test.range("q1"), "use of undeclared identifier 'clangd'; "
"did you mean 'clang'?"),
SymbolWithHeader{"a::X", "unittest:///x.h", "\"x.h\""});
TU.ExternalIndex = Index.get();
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Test.range(), "no type named 'X' in namespace 'a'"),
diagName("typename_nested_not_found"),
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'")));
}
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(), "call to undeclared library function 'printf' "
"with type 'int (const char *, ...)'; ISO C99 "
TU.ExtraArgs = {"-xc", "-std=c89"};
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(), "implicitly declaring library function 'printf' "
"with type 'int (const char *, ...)'"),
TU.ExternalIndex = Index.get();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(),
"call to undeclared function 'foo'; ISO C99 and later do not "
"Include \"foo.h\" for symbol foo")))));
TU.ExtraArgs = {"-std=c89", "-Wall"};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(AllOf(
Diag(Test.range(), "implicit declaration of function 'foo'"),
withFix(Fix(Range{}, "#include \"foo.h\"\n",
Annotations Header("[[no_type_spec]]; // error-ok");
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Main.range(), "in included file: a type specifier is "
"required for all declarations"),
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", "#include \"b.h\""},
{"b.h", "no_type_spec; // error-ok"}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(Diag(Main.range(),
"in included file: a type specifier is "
"required for all declarations")));
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", "no_type_spec; // error-ok"},
{"b.h", "no_type_spec; // error-ok"}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Main.range("a"), "in included file: a type specifier is "
"required for all declarations"),
TU.AdditionalFiles = {
{"a.h", "#include \"b.h\"\n"},
{"b.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
Contains(Diag(Main.range(), "in included file: a type specifier "
"is required for all declarations")));
}
{"a.h", "#include \"c.h\"\n"},
{"b.h", "#include \"c.h\"\n"},
{"c.h", "#ifndef X\n#define X\nno_type_spec; // error-ok\n#endif"}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(Diag(Main.range(),
"in included file: a type specifier is "
"required for all declarations")));
no_type_spec_9;
no_type_spec_10;
#endif)cpp"}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(Diag(Main.range(),
"in included file: a type specifier is "
"required for all declarations")));
int x = 5/0;)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Main.range(), "in included file: a type specifier is "
"required for all declarations"),
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
// promote warnings to errors.
TU.ExtraArgs = {"-Werror", "-Wunused"};
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagsInHeaders, FromNonWrittenSources) {
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
TU.ExtraArgs = {"-DFOO=NOOO"};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Main.range(),
"in included file: use of undeclared identifier 'NOOO'"),
X;)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Main.range(), "in included file: use of undeclared "
"identifier 'foo'; did you mean 'fo'?")));
X(foo);)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.AdditionalFiles = {{"a.h", std::string(Header.code())}};
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
Diag(Main.range(), "in included file: use of undeclared "
"identifier 'foo'; did you mean 'fo'?")));
TU.AdditionalFiles = {{"a.h", "void main();"}};
// The diagnostic "main must return int" is from the header, we don't attempt
// to render it in the main file as there is no written location there.
- EXPECT_THAT(*TU.build().getDiagnostics(), UnorderedElementsAre());
+ EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre());
}
TEST(ToLSPDiag, RangeIsInMain) {
TU.FeatureModules = &FMS;
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
testing::Contains(Diag(Code.range(), KDiagMsg.str())));
}
{
TU.Code = "#define FOO\n void bar();\n";
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
}
{
Annotations Code("#define FOO[[]]");
TU.Code = Code.code().str();
auto AST = TU.build();
EXPECT_THAT(
- *AST.getDiagnostics(),
+ AST.getDiagnostics(),
testing::Contains(Diag(Code.range(), "no newline at end of file")));
}
}
$deprecated[[bar]]();
})cpp");
TU.Code = Test.code().str();
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
UnorderedElementsAre(
AllOf(Diag(Test.range("unused"), "unused variable 'x'"),
withTag(DiagnosticTag::Unnecessary)),
TU.Code = Test.code();
TU.ClangTidyProvider = addTidyChecks("modernize-use-using");
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
ifTidyChecks(UnorderedElementsAre(
AllOf(Diag(Test.range("typedef"), "use 'using' instead of 'typedef'"),
withTag(DiagnosticTag::Deprecated)))));
WithContextValue WithCfg(Config::Key, std::move(Cfg));
auto AST = TU.build();
EXPECT_THAT(
- *AST.getDiagnostics(),
+ AST.getDiagnostics(),
Contains(AllOf(
Diag(Test.range("diag"),
"included header unused.h is not used directly"),
withTag(DiagnosticTag::Unnecessary), diagSource(Diag::Clangd),
withFix(Fix(Test.range("fix"), "", "remove #include directive")))));
- auto &Diag = AST.getDiagnostics()->front();
+ auto &Diag = AST.getDiagnostics().front();
EXPECT_EQ(getDiagnosticDocURI(Diag.Source, Diag.ID, Diag.Name),
std::string("https://clangd.llvm.org/guides/include-cleaner"));
Cfg.Diagnostics.SuppressAll = true;
WithContextValue SuppressAllWithCfg(Config::Key, std::move(Cfg));
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
Cfg.Diagnostics.SuppressAll = false;
Cfg.Diagnostics.Suppress = {"unused-includes"};
WithContextValue SuppressFilterWithCfg(Config::Key, std::move(Cfg));
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
TEST(DiagnosticsTest, FixItFromHeader) {
TU.Code = Source.code().str();
TU.HeaderCode = Header.str();
EXPECT_THAT(
- *TU.build().getDiagnostics(),
+ TU.build().getDiagnostics(),
UnorderedElementsAre(AllOf(
Diag(Source.range("diag"), "no matching function for call to 'foo'"),
withFix(Fix(Source.range("fix"), "&",
auto TU = TestTU::withCode("static inline void foo(void) {}");
TU.ExtraArgs.push_back("-Wunused-function");
TU.Filename = "test.c";
- EXPECT_THAT(*TU.build().getDiagnostics(),
+ EXPECT_THAT(TU.build().getDiagnostics(),
ElementsAre(withID(diag::warn_unused_function)));
// Sema should recognize a *.h file open in clangd as a header.
// https://github.com/clangd/vscode-clangd/issues/360
TU.Filename = "test.h";
- EXPECT_THAT(*TU.build().getDiagnostics(), IsEmpty());
+ EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty());
}
} // namespace
{
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
+ EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
}
TU.FeatureModules = &FMS;
{
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), testing::IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
}
}
{
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
+ EXPECT_THAT(AST.getDiagnostics(), testing::Not(testing::IsEmpty()));
}
TU.FeatureModules = &FMS;
{
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), testing::IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), testing::IsEmpty());
}
}
header "module.h"
}
)modulemap";
- EXPECT_TRUE(TU.build().getDiagnostics()->empty());
+ EXPECT_TRUE(TU.build().getDiagnostics().empty());
}
TEST(Modules, Diagnostic) {
auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
{}, EmptyPreamble);
ASSERT_TRUE(PatchedAST);
- ASSERT_FALSE(PatchedAST->getDiagnostics());
// Ensure source location information is correct, including resolved paths.
EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes,
;
)cpp";
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_FALSE(mainIsGuarded(AST));
#include "self.h" // error-ok
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
+ EXPECT_THAT(AST.getDiagnostics(), ElementsAre(diag("nested too deeply")));
EXPECT_FALSE(mainIsGuarded(AST));
TU.Code = R"cpp(
;
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
TU.Code = R"cpp(
#include "self.h"
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
TU.Code = R"cpp(
#include "self.h"
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
TU.Code = R"cpp(
;
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_TRUE(mainIsGuarded(AST));
#endif
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
// Guarded too late...
#endif
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_FALSE(mainIsGuarded(AST));
#endif
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_FALSE(mainIsGuarded(AST));
#endif
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_FALSE(mainIsGuarded(AST));
TU.Code = R"cpp(
;
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_TRUE(mainIsGuarded(AST));
#pragma once
)cpp";
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("recursively when building a preamble")));
EXPECT_TRUE(mainIsGuarded(AST));
}
TU.Code = guard(Interface);
TU.AdditionalFiles = {{"impl.h", Implementation}};
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
// Slightly harder: the `#pragma once` is part of the preamble, and we
// need to transfer it to the main file's HeaderFileInfo.
TU.Code = once(Interface);
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), IsEmpty());
EXPECT_TRUE(mainIsGuarded(AST));
// Editing the implementation file, which is not include guarded.
AST = TU.build();
// The diagnostic is unfortunate in this case, but correct per our model.
// Ultimately the include is skipped and the code is parsed correctly though.
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("in included file: main file cannot be included "
"recursively when building a preamble")));
EXPECT_FALSE(mainIsGuarded(AST));
// Interface is pragma once guarded, same thing.
TU.AdditionalFiles = {{"iface.h", once(Interface)}};
AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(),
+ EXPECT_THAT(AST.getDiagnostics(),
ElementsAre(diag("in included file: main file cannot be included "
"recursively when building a preamble")));
EXPECT_FALSE(mainIsGuarded(AST));
}
}
-TEST(PreamblePatch, DropsDiagnostics) {
- llvm::StringLiteral Code = "#define FOO\nx;/* error-ok */";
- // First check that this code generates diagnostics.
- EXPECT_THAT(*TestTU::withCode(Code).build().getDiagnostics(),
- testing::Not(testing::IsEmpty()));
- // Ensure they are dropeed when a patched preamble is used.
- EXPECT_FALSE(createPatchedAST("", Code)->getDiagnostics());
-}
-
TEST(PreamblePatch, MacroLoc) {
llvm::StringLiteral Baseline = "\n#define MACRO 12\nint num = MACRO;";
llvm::StringLiteral Modified = " \n#define MACRO 12\nint num = MACRO;";
}
TEST(PreamblePatch, DiagnosticsFromMainASTAreInRightPlace) {
- Config Cfg;
- Cfg.Diagnostics.AllowStalePreamble = true;
- WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
{
Annotations Code("#define FOO");
// Check with removals from preamble.
Annotations NewCode("[[x]];/* error-ok */");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
}
{
#define BAR
[[x]];/* error-ok */)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "missing_type_specifier")));
}
}
TEST(PreamblePatch, DiagnosticsToPreamble) {
Config Cfg;
- Cfg.Diagnostics.AllowStalePreamble = true;
Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
WithContextValue WithCfg(Config::Key, std::move(Cfg));
// Check with removals from preamble.
Annotations NewCode(R"([[# include "foo.h"]])");
auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "unused-includes")));
}
{
$foo[[#include "foo.h"]])");
auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
EXPECT_THAT(
- *AST->getDiagnostics(),
+ AST->getDiagnostics(),
UnorderedElementsAre(Diag(NewCode.range("bar"), "unused-includes"),
Diag(NewCode.range("foo"), "unused-includes")));
}
#define $foo2[[FOO]] 2)");
auto AST = createPatchedAST(Code.code(), NewCode.code(), AdditionalFiles);
EXPECT_THAT(
- *AST->getDiagnostics(),
+ AST->getDiagnostics(),
ElementsAre(AllOf(Diag(NewCode.range("foo2"), "-Wmacro-redefined"),
withNote(Diag(NewCode.range("foo1"))))));
}
}
TEST(PreamblePatch, TranslatesDiagnosticsInPreamble) {
- Config Cfg;
- Cfg.Diagnostics.AllowStalePreamble = true;
- WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
{
// Check with additions to preamble.
Annotations Code("#include [[<foo>]]");
#define BAR
#include [[<foo>]])");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
}
{
#include [[<foo>]])");
Annotations NewCode("#include [[<foo>]]");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
}
{
Annotations Code("#include [[<foo>]]");
Annotations NewCode("#define BAR\n#define BAZ\n");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
}
{
// Picks closest line in case of multiple alternatives.
#define BAR
#include <foo>)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
}
{
Annotations Code("#include [[<foo>]]");
Annotations NewCode(" # include <foo>");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
}
{
// Multiple lines.
Annotations NewCode(R"(#include [[<fo\
o>]])");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(),
+ EXPECT_THAT(AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "pp_file_not_found")));
}
{
Annotations NewCode(R"(#include <fo\
x>)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
}
{
// Preserves notes.
#define $main[[BAR]] 2)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
EXPECT_THAT(
- *AST->getDiagnostics(),
+ AST->getDiagnostics(),
ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
withNote(Diag(NewCode.range("note"))))));
}
#define $main[[BAR]] 2)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
EXPECT_THAT(
- *AST->getDiagnostics(),
+ AST->getDiagnostics(),
ElementsAre(AllOf(Diag(NewCode.range("main"), "-Wmacro-redefined"),
Field(&Diag::Notes, IsEmpty()))));
}
#define BAZ 0
#define BAR 1)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
}
{
Annotations Code(R"(
// We shouldn't emit any diagnotiscs (and shouldn't crash).
Annotations NewCode("");
auto AST = createPatchedAST(Code.code(), NewCode.code());
- EXPECT_THAT(*AST->getDiagnostics(), IsEmpty());
+ EXPECT_THAT(AST->getDiagnostics(), IsEmpty());
}
{
Annotations Code(R"(
)");
auto AST = createPatchedAST(Code.code(), NewCode.code());
EXPECT_THAT(
- *AST->getDiagnostics(),
+ AST->getDiagnostics(),
ElementsAre(Diag(NewCode.range(), "pp_unterminated_conditional")));
}
}
}
TEST(PreamblePatch, MacroAndMarkHandling) {
- Config Cfg;
- Cfg.Diagnostics.AllowStalePreamble = true;
- WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
{
Annotations Code(R"cpp(
#ifndef FOO
auto TU = TestTU::withCode(Test.code());
TU.AdditionalFiles["Expand.inc"] = "MACRO\n";
auto AST = TU.build();
- EXPECT_THAT(*AST.getDiagnostics(), ::testing::IsEmpty());
+ EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty());
auto T = makeSelectionTree(Case, AST);
EXPECT_EQ("BreakStmt", T.commonAncestor()->kind());
class CaptureDiags : public ParsingCallbacks {
public:
void onMainAST(PathRef File, ParsedAST &AST, PublishFn Publish) override {
- reportDiagnostics(File, *AST.getDiagnostics(), Publish);
+ reportDiagnostics(File, AST.getDiagnostics(), Publish);
}
void onFailedAST(PathRef File, llvm::StringRef Version,
if (!D)
return;
Publish([&]() {
- const_cast<
- llvm::unique_function<void(PathRef, std::vector<Diag>)> &> (*D)(
- File, std::move(Diags));
+ const_cast<llvm::unique_function<void(PathRef, std::vector<Diag>)> &>(
+ *D)(File, Diags);
});
}
};
Notification Ready;
TUScheduler S(CDB, optsForTest(), captureDiags());
auto Path = testPath("foo.cpp");
- updateWithDiags(S, Path, "", WantDiagnostics::Yes,
+ // Semicolons here and in the following inputs are significant. They ensure
+ // preamble stays the same across runs. Otherwise we might get multiple
+ // diagnostics callbacks, once with the stale preamble and another with the
+ // fresh preamble.
+ updateWithDiags(S, Path, ";", WantDiagnostics::Yes,
[&](std::vector<Diag>) { Ready.wait(); });
- updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes,
+ updateWithDiags(S, Path, ";request diags", WantDiagnostics::Yes,
[&](std::vector<Diag>) { ++CallbackCount; });
- updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto,
+ updateWithDiags(S, Path, ";auto (clobbered)", WantDiagnostics::Auto,
[&](std::vector<Diag>) {
ADD_FAILURE()
<< "auto should have been cancelled by auto";
});
- updateWithDiags(S, Path, "request no diags", WantDiagnostics::No,
+ updateWithDiags(S, Path, ";request no diags", WantDiagnostics::No,
[&](std::vector<Diag>) {
ADD_FAILURE() << "no diags should not be called back";
});
- updateWithDiags(S, Path, "auto (produces)", WantDiagnostics::Auto,
+ updateWithDiags(S, Path, ";auto (produces)", WantDiagnostics::Auto,
[&](std::vector<Diag>) { ++CallbackCount; });
Ready.notify();
CDB.ExtraClangFlags.push_back("-DSOMETHING");
ASSERT_TRUE(DoUpdate(SourceContents));
ASSERT_FALSE(DoUpdate(SourceContents));
- ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 4u);
+ // This causes 2 AST builds always. We first build an AST with the stale
+ // preamble, and build a second AST once the fresh preamble is ready.
+ ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 5u);
ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 3u);
}
DiagVersions;
};
- Config Cfg;
- Cfg.Diagnostics.AllowStalePreamble = true;
- WithContextValue WithCfg(Config::Key, std::move(Cfg));
-
DiagCollector Collector;
Notification UnblockPreamble;
auto DiagCallbacks = std::make_unique<BlockPreambleThread>(
llvm::errs() << "Failed to build code:\n" << Code;
std::abort();
}
- assert(AST->getDiagnostics() &&
- "TestTU should always build an AST with a fresh Preamble");
// Check for error diagnostics and report gtest failures (unless expected).
// This guards against accidental syntax errors silently subverting tests.
// error-ok is awfully primitive - using clang -verify would be nicer.
}();
if (!ErrorOk) {
// We always build AST with a fresh preamble in TestTU.
- for (const auto &D : *AST->getDiagnostics())
+ for (const auto &D : AST->getDiagnostics())
if (D.Severity >= DiagnosticsEngine::Error) {
llvm::errs()
<< "TestTU failed to build (suppress with /*error-ok*/): \n"
// The compiler should produce a diagnostic for hitting the
// template instantiation depth.
- ASSERT_TRUE(!AST.getDiagnostics()->empty());
+ ASSERT_FALSE(AST.getDiagnostics().empty());
// Make sure getTypeHierarchy() doesn't get into an infinite recursion.
// The parent is reported as "S" because "S<0>" is an invalid instantiation.