ソースを参照

Error when using emitter.Build().Emit() (#5152)

I've found myself cleaning up other cases of this, so moving to disallow
it.

```
In file included from toolchain/check/merge.cpp:5:
In file included from ./toolchain/check/merge.h:8:
In file included from ./toolchain/check/context.h:13:
In file included from ./toolchain/check/decl_introducer_state.h:8:
In file included from ./toolchain/check/keyword_modifier_set.h:11:
In file included from ./toolchain/sem_ir/name_scope.h:10:
In file included from ./toolchain/sem_ir/ids.h:12:
./toolchain/diagnostics/diagnostic_emitter.h:95:11: error: static assertion failed: Use `emitter.Emit(...)` or `emitter.Build(...).Note(...).Emit(...)` instead of `emitter.Build(...).Emit(...)`
   95 |           false,
      |           ^~~~~
toolchain/check/merge.cpp:92:61: note: in instantiation of function template specialization 'Carbon::DiagnosticEmitter<Carbon::Check::SemIRLoc>::DiagnosticBuilder::Emit<>' requested here
   92 |   context.emitter().Build(loc, ExternRequiresDeclInApiFile).Emit();
      |                                                             ^
1 error generated.
```
Jon Ross-Perkins 1 年間 前
コミット
84a8c458f9
2 ファイル変更26 行追加5 行削除
  1. 1 1
      toolchain/check/merge.cpp
  2. 25 4
      toolchain/diagnostics/diagnostic_emitter.h

+ 1 - 1
toolchain/check/merge.cpp

@@ -89,7 +89,7 @@ auto DiagnoseExternRequiresDeclInApiFile(Context& context, SemIRLoc loc)
   CARBON_DIAGNOSTIC(
       ExternRequiresDeclInApiFile, Error,
       "`extern` entities must have a declaration in the API file");
-  context.emitter().Build(loc, ExternRequiresDeclInApiFile).Emit();
+  context.emitter().Emit(loc, ExternRequiresDeclInApiFile);
 }
 
 auto DiagnoseIfInvalidRedecl(Context& context, Lex::TokenKind decl_kind,

+ 25 - 4
toolchain/diagnostics/diagnostic_emitter.h

@@ -86,7 +86,11 @@ class DiagnosticEmitter {
     // Emits the built diagnostic and its attached notes.
     // For the expected usage see the builder API: `DiagnosticEmitter::Build`.
     template <typename... Args>
-    auto Emit() -> void;
+    auto Emit() & -> void;
+
+    // Prevent trivial uses of the builder; always `static_assert`s.
+    template <typename... Args>
+    auto Emit() && -> void;
 
     // Returns true if this DiagnosticBuilder may emit a diagnostic. Can be used
     // to avoid excess work computing notes, etc, if no diagnostic is going to
@@ -282,7 +286,7 @@ auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Note(
 
 template <typename LocT>
 template <typename... Args>
-auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() -> void {
+auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() & -> void {
   if (!emitter_) {
     return;
   }
@@ -292,6 +296,22 @@ auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() -> void {
   emitter_->consumer_->HandleDiagnostic(std::move(diagnostic_));
 }
 
+namespace Internal {
+template <typename LocT>
+concept AlwaysFalse = false;
+}  // namespace Internal
+
+template <typename LocT>
+template <typename... Args>
+auto DiagnosticEmitter<LocT>::DiagnosticBuilder::Emit() && -> void {
+  // TODO: This is required by clang-16, but `false` may work in newer clang
+  // versions. Replace when possible.
+  static_assert(Internal::AlwaysFalse<LocT>,
+                "Use `emitter.Emit(...)` or "
+                "`emitter.Build(...).Note(...).Emit(...)` "
+                "instead of `emitter.Build(...).Emit(...)`");
+}
+
 template <typename LocT>
 template <typename... Args>
 DiagnosticEmitter<LocT>::DiagnosticBuilder::DiagnosticBuilder(
@@ -364,8 +384,9 @@ template <typename... Args>
 auto DiagnosticEmitter<LocT>::Emit(
     LocT loc, const DiagnosticBase<Args...>& diagnostic_base,
     Internal::NoTypeDeduction<Args>... args) -> void {
-  DiagnosticBuilder(this, loc, diagnostic_base, {MakeAny<Args>(args)...})
-      .Emit();
+  DiagnosticBuilder builder(this, loc, diagnostic_base,
+                            {MakeAny<Args>(args)...});
+  builder.Emit();
 }
 
 template <typename LocT>