Files
carbon-lang/toolchain/diagnostics/diagnostic_emitter_test.cpp
T
14f51d70c2 Emit diagnostics produced by Clang after the ASTUnit is constructed. (#5876)
Previously we would drop these diagnostics; now periodically flush them
to Carbon's diagnostics emitter. We flush them at the end of checking,
and also immediately before changing the set of diagnostic annotation
scopes, so that Clang diagnostics get properly annotated.

This exposes some double diagnostics being produced in situations where
Clang's name lookup logic would produce a diagnostic and we also
produced one. For access control issues, use the Carbon diagnostic, in
order to properly handle protected access. For ambiguity issues, use the
Clang diagnostic that produces helpful notes.

Also fix rendering of note diagnostics produced by Clang, by attaching
them to the prior error / warning diagnostic.

---------

Co-authored-by: Boaz Brickner <brickner@google.com>
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2025-07-30 21:01:12 +00:00

119 lines
3.6 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "llvm/ADT/StringRef.h"
#include "toolchain/diagnostics/mocks.h"
namespace Carbon::Testing {
namespace {
using testing::ElementsAre;
class FakeEmitter : public Diagnostics::Emitter<int> {
public:
using Emitter::Emitter;
protected:
auto ConvertLoc(int n, ContextFnT /*context_fn*/) const
-> Diagnostics::ConvertedLoc override {
return {.loc = {.line_number = 1, .column_number = n},
.last_byte_offset = -1};
}
};
class EmitterTest : public ::testing::Test {
public:
EmitterTest() : emitter_(&consumer_) {}
Testing::MockDiagnosticConsumer consumer_;
FakeEmitter emitter_;
};
TEST_F(EmitterTest, EmitSimpleError) {
CARBON_DIAGNOSTIC(TestDiagnostic, Error, "simple error");
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 1, "simple error")));
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 2, "simple error")));
emitter_.Emit(1, TestDiagnostic);
emitter_.Emit(2, TestDiagnostic);
}
TEST_F(EmitterTest, EmitSimpleWarning) {
CARBON_DIAGNOSTIC(TestDiagnostic, Warning, "simple warning");
EXPECT_CALL(consumer_,
HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Warning, 1, 1, "simple warning")));
emitter_.Emit(1, TestDiagnostic);
}
TEST_F(EmitterTest, EmitOneArgDiagnostic) {
CARBON_DIAGNOSTIC(TestDiagnostic, Error, "arg: `{0}`", std::string);
EXPECT_CALL(consumer_, HandleDiagnostic(IsSingleDiagnostic(
Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Error, 1, 1, "arg: `str`")));
emitter_.Emit(1, TestDiagnostic, "str");
}
TEST_F(EmitterTest, EmitNote) {
CARBON_DIAGNOSTIC(TestDiagnostic, Warning, "simple warning");
CARBON_DIAGNOSTIC(TestDiagnosticNote, Note, "note");
EXPECT_CALL(
consumer_,
HandleDiagnostic(IsDiagnostic(
Diagnostics::Level::Warning,
ElementsAre(
IsDiagnosticMessage(Diagnostics::Kind::TestDiagnostic,
Diagnostics::Level::Warning, 1, 1,
"simple warning"),
IsDiagnosticMessage(Diagnostics::Kind::TestDiagnosticNote,
Diagnostics::Level::Note, 1, 2, "note")))));
emitter_.Build(1, TestDiagnostic).Note(2, TestDiagnosticNote).Emit();
}
TEST_F(EmitterTest, Flush) {
bool flushed = false;
auto flush_fn = [&]() { flushed = true; };
{
FakeEmitter emitter(&consumer_);
emitter.AddFlushFn(flush_fn);
// Registering the function does not flush.
EXPECT_FALSE(flushed);
// Explicit calls to `Flush` should flush.
emitter.Flush();
EXPECT_TRUE(flushed);
flushed = false;
{
Diagnostics::AnnotationScope annot(&emitter, [](auto&) {});
// Registering an annotation scope should flush.
EXPECT_TRUE(flushed);
flushed = false;
}
// Unregistering an annotation scope should flush.
EXPECT_TRUE(flushed);
flushed = false;
}
// Destroying the emitter should flush.
EXPECT_TRUE(flushed);
flushed = false;
}
} // namespace
} // namespace Carbon::Testing