Add structured error information for errors from `lifetime_analysis`.
PiperOrigin-RevId: 521986503
diff --git a/lifetime_annotations/BUILD b/lifetime_annotations/BUILD
index d8d0364..ca96518 100644
--- a/lifetime_annotations/BUILD
+++ b/lifetime_annotations/BUILD
@@ -14,6 +14,14 @@
],
)
+cc_library(
+ name = "lifetime_error",
+ hdrs = ["lifetime_error.h"],
+ deps = [
+ "@llvm-project//llvm:Support",
+ ],
+)
+
cc_test(
name = "lifetime_test",
srcs = ["lifetime_test.cc"],
@@ -35,6 +43,7 @@
],
deps = [
":lifetime",
+ ":lifetime_error",
":lifetime_substitutions",
":lifetime_symbol_table",
":pointee_type",
@@ -51,6 +60,7 @@
srcs = ["lifetime_annotations.cc"],
hdrs = ["lifetime_annotations.h"],
deps = [
+ ":lifetime_error",
":lifetime_symbol_table",
":pointee_type",
":type_lifetimes",
@@ -68,6 +78,7 @@
srcs = ["lifetime_annotations_test.cc"],
deps = [
":lifetime_annotations",
+ ":lifetime_error",
":type_lifetimes",
"@com_google_googletest//:gtest_main",
"@absl//absl/status",
diff --git a/lifetime_annotations/function_lifetimes.cc b/lifetime_annotations/function_lifetimes.cc
index dcf4861..0221e66 100644
--- a/lifetime_annotations/function_lifetimes.cc
+++ b/lifetime_annotations/function_lifetimes.cc
@@ -9,6 +9,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "lifetime_annotations/lifetime.h"
+#include "lifetime_annotations/lifetime_error.h"
#include "lifetime_annotations/type_lifetimes.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
@@ -120,8 +121,8 @@
}
if (this_type.isNull() && !lifetime_names.empty()) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Encountered a `this` lifetime on a function with no "
"`this` parameter"));
}
@@ -131,8 +132,8 @@
const clang::Expr* lifetime_name = nullptr;
if (!lifetime_names.empty()) {
if (lifetime_names.size() != 1) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Expected a single lifetime but ",
lifetime_names.size(), " were given"));
}
diff --git a/lifetime_annotations/lifetime_annotations.cc b/lifetime_annotations/lifetime_annotations.cc
index c4db67f..e228161 100644
--- a/lifetime_annotations/lifetime_annotations.cc
+++ b/lifetime_annotations/lifetime_annotations.cc
@@ -12,6 +12,7 @@
#include "absl/strings/str_cat.h"
#include "lifetime_annotations/function_lifetimes.h"
+#include "lifetime_annotations/lifetime_error.h"
#include "lifetime_annotations/lifetime_symbol_table.h"
#include "lifetime_annotations/pointee_type.h"
#include "lifetime_annotations/type_lifetimes.h"
@@ -74,8 +75,8 @@
&next_lifetime](const clang::Expr*) -> llvm::Expected<Lifetime> {
llvm::StringRef next = next_lifetime();
if (next.empty()) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
"Invalid lifetime annotation: too few lifetimes");
}
return symbol_table.LookupNameAndMaybeDeclare(next);
@@ -84,8 +85,8 @@
auto ret = FunctionLifetimes::CreateForDecl(func, factory);
if (!next_lifetime().empty()) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
"Invalid lifetime annotation: too many lifetimes");
}
return ret;
@@ -103,7 +104,7 @@
if (!detail.empty()) {
absl::StrAppend(&msg, ": ", detail);
}
- return llvm::createStringError(llvm::inconvertibleErrorCode(), msg);
+ return llvm::make_error<LifetimeError>(LifetimeError::Type::Other, msg);
};
if (attr->args_size() != 1) {
@@ -129,8 +130,8 @@
if (auto annotate = clang::dyn_cast<clang::AnnotateAttr>(attr)) {
if (annotate->getAnnotation() == "lifetimes") {
if (lifetime_annotation != nullptr) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Can't extract lifetimes as '",
func->getNameAsString(),
"' has multiple lifetime annotations"));
@@ -178,8 +179,8 @@
// safely even if elision is disabled.
if (!elision_enabled && func->getDeclName().getNameKind() !=
clang::DeclarationName::CXXDestructorName) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::ElisionNotEnabled,
absl::StrCat("Lifetime elision not enabled for '",
func->getNameAsString(), "'"));
}
@@ -279,8 +280,8 @@
}
if (!elision_enabled) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::ElisionNotEnabled,
absl::StrCat("Lifetime elision not enabled for '",
func->getNameAsString(), "'"));
}
@@ -291,8 +292,8 @@
return *input_lifetime;
} else {
// Otherwise, we don't know how to elide the output lifetime.
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::CannotElideOutputLifetimes,
absl::StrCat("Cannot elide output lifetimes for '",
func->getNameAsString(),
"' because it is a non-member function that "
@@ -312,6 +313,8 @@
}
} // namespace
+char LifetimeError::ID;
+
llvm::Expected<FunctionLifetimes> GetLifetimeAnnotations(
const clang::FunctionDecl* func, const LifetimeAnnotationContext& context,
LifetimeSymbolTable* symbol_table) {
diff --git a/lifetime_annotations/lifetime_annotations.h b/lifetime_annotations/lifetime_annotations.h
index 65392d8..f260291 100644
--- a/lifetime_annotations/lifetime_annotations.h
+++ b/lifetime_annotations/lifetime_annotations.h
@@ -33,6 +33,8 @@
// rules were not applicable.
// The names of annotated function lifetimes as well as autogenerated names for
// elided lifetimes are added to `symbol_table`.
+//
+// Returns structured error information as a `LifetimeError`.
llvm::Expected<FunctionLifetimes> GetLifetimeAnnotations(
const clang::FunctionDecl* func, const LifetimeAnnotationContext& context,
LifetimeSymbolTable* symbol_table = nullptr);
@@ -40,6 +42,8 @@
// Parses "a: b, a -> b"-style lifetime annotations from `lifetimes_str` for the
// function declaration `func`. Lifetimes are inserted into the given
// `symbol_table`, or used from there if already known.
+//
+// Returns structured error information as a `LifetimeError`.
llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
const clang::FunctionDecl* func, const std::string& lifetimes_str,
LifetimeSymbolTable* symbol_table = nullptr);
diff --git a/lifetime_annotations/lifetime_annotations_test.cc b/lifetime_annotations/lifetime_annotations_test.cc
index 316eb30..97d04a1 100644
--- a/lifetime_annotations/lifetime_annotations_test.cc
+++ b/lifetime_annotations/lifetime_annotations_test.cc
@@ -11,6 +11,7 @@
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "common/status_test_matchers.h"
+#include "lifetime_annotations/lifetime_error.h"
#include "lifetime_annotations/test/named_func_lifetimes.h"
#include "lifetime_annotations/test/run_on_code.h"
#include "lifetime_annotations/type_lifetimes.h"
@@ -70,6 +71,29 @@
return result;
}
+std::string FormatErrorString(llvm::Error err) {
+ std::string result;
+ err = llvm::handleErrors(
+ std::move(err), [&result](const LifetimeError& lifetime_err) {
+ switch (lifetime_err.type()) {
+ case LifetimeError::Type::ElisionNotEnabled:
+ result = "ERROR(ElisionNotEnabled): ";
+ break;
+ case LifetimeError::Type::CannotElideOutputLifetimes:
+ result = "ERROR(CannotElideOutputLifetimes): ";
+ break;
+ case LifetimeError::Type::Other:
+ result = "ERROR(Other): ";
+ break;
+ }
+ absl::StrAppend(&result, lifetime_err.message());
+ });
+ if (err) {
+ result = absl::StrCat("ERROR: ", llvm::toString(std::move(err)));
+ }
+ return result;
+}
+
class LifetimeAnnotationsTest : public testing::Test {
protected:
absl::StatusOr<NamedFuncLifetimes> GetNamedLifetimeAnnotations(
@@ -115,8 +139,7 @@
if (func_lifetimes) {
new_entry = NameLifetimes(*func_lifetimes, symbol_table);
} else {
- new_entry = absl::StrCat(
- "ERROR: ", llvm::toString(func_lifetimes.takeError()));
+ new_entry = FormatErrorString(func_lifetimes.takeError());
}
std::string func_name = QualifiedName(func);
@@ -172,8 +195,9 @@
EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
int** f(int*);
)"),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Lifetime elision not enabled for 'f'"}})));
+ IsOkAndHolds(LifetimesAre({{"f",
+ "ERROR(ElisionNotEnabled): Lifetime "
+ "elision not enabled for 'f'"}})));
}
TEST_F(LifetimeAnnotationsTest, Failure_NoOutputAnnotationNoLifetimeElision) {
@@ -182,8 +206,9 @@
)"),
// We specifically want to see this error message rather than
// "Cannot elide output lifetimes".
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Lifetime elision not enabled for 'f'"}})));
+ IsOkAndHolds(LifetimesAre({{"f",
+ "ERROR(ElisionNotEnabled): Lifetime "
+ "elision not enabled for 'f'"}})));
}
TEST_F(LifetimeAnnotationsTest, Failure_NoAnnotationsElisionPragmaInWrongFile) {
@@ -194,8 +219,9 @@
{std::make_pair("header.h", R"(
int** f(int*);
)")}),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Lifetime elision not enabled for 'f'"}})));
+ IsOkAndHolds(LifetimesAre({{"f",
+ "ERROR(ElisionNotEnabled): Lifetime "
+ "elision not enabled for 'f'"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeElision_OneInputLifetime) {
@@ -351,9 +377,9 @@
)"),
IsOkAndHolds(LifetimesAre(
{{"f",
- "ERROR: Cannot elide output lifetimes for 'f' because it "
- "is a non-member function that does not have exactly one "
- "input lifetime"}})));
+ "ERROR(CannotElideOutputLifetimes): Cannot elide output "
+ "lifetimes for 'f' because it is a non-member function "
+ "that does not have exactly one input lifetime"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeElision_FailureTooManyInputLifetimes) {
@@ -363,9 +389,9 @@
)"),
IsOkAndHolds(LifetimesAre(
{{"f",
- "ERROR: Cannot elide output lifetimes for 'f' because it "
- "is a non-member function that does not have exactly one "
- "input lifetime"}})));
+ "ERROR(CannotElideOutputLifetimes): Cannot elide output "
+ "lifetimes for 'f' because it is a non-member function "
+ "that does not have exactly one input lifetime"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_NoLifetimes) {
@@ -406,7 +432,8 @@
void f(int* [[clang::annotate_type("lifetime", 1)]]);
)")),
IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: cannot evaluate argument as a string literal"}})));
+ {{"f",
+ "ERROR(Other): cannot evaluate argument as a string literal"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Simple) {
@@ -429,38 +456,42 @@
TEST_F(LifetimeAnnotationsTest,
LifetimeAnnotation_Invalid_LifetimeOnNonReferenceLikeType) {
- EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
void f(int $a);
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Type may not be annotated with lifetimes"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f", "ERROR(Other): Type may not be annotated with lifetimes"}})));
}
TEST_F(LifetimeAnnotationsTest,
LifetimeAnnotation_Invalid_LifetimeOnFunctionPointer) {
- EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
void f(void (* $a)());
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Type may not be annotated with lifetimes"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f", "ERROR(Other): Type may not be annotated with lifetimes"}})));
}
TEST_F(LifetimeAnnotationsTest,
LifetimeAnnotation_Invalid_LifetimeOnFunctionPointerReturnType) {
- EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
int (* $a f())(float, double);
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Type may not be annotated with lifetimes"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f", "ERROR(Other): Type may not be annotated with lifetimes"}})));
}
TEST_F(LifetimeAnnotationsTest,
LifetimeAnnotation_Invalid_LifetimeOnFunctionReference) {
- EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
void f(void (& $a)());
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Type may not be annotated with lifetimes"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f", "ERROR(Other): Type may not be annotated with lifetimes"}})));
}
TEST_F(LifetimeAnnotationsTest,
@@ -470,7 +501,8 @@
void f(int* $2(a, b));
)")),
IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Expected a single lifetime but 2 were given"}})));
+ {{"f",
+ "ERROR(Other): Expected a single lifetime but 2 were given"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Static) {
@@ -548,29 +580,30 @@
TEST_F(
LifetimeAnnotationsTest,
LifetimeAnnotation_LifetimeParameterizedType_Invalid_WrongNumberOfLifetimes) {
- EXPECT_THAT(
- GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
struct [[clang::annotate("lifetime_params", "a", "b")]] S_param {};
void f(S_param $3(a, b, c) s);
)")),
- IsOkAndHolds(LifetimesAre({{"f",
- "ERROR: Type has 2 lifetime parameters but 3 "
- "lifetime arguments were given"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f",
+ "ERROR(Other): Type has 2 lifetime parameters but 3 "
+ "lifetime arguments were given"}})));
}
TEST_F(
LifetimeAnnotationsTest,
LifetimeAnnotation_LifetimeParameterizedType_Invalid_MultipleAnnotateAttributes) {
- EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
struct [[clang::annotate("lifetime_params", "a", "b")]] S_param {};
void f(S_param $a $b s);
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f",
- "ERROR: Only one `[[annotate_type(\"lifetime\", ...)]]` "
- "attribute may be placed on a type"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f",
+ "ERROR(Other): Only one `[[annotate_type(\"lifetime\", ...)]]` "
+ "attribute may be placed on a type"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Template) {
@@ -700,31 +733,32 @@
)")),
IsOkAndHolds(LifetimesAre(
{{"S::f",
- "ERROR: Invalid lifetime annotation: too few lifetimes"}})));
+ "ERROR(Other): Invalid lifetime annotation: too few lifetimes"}})));
EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
struct S {
int* $a f();
};
)")),
- IsOkAndHolds(LifetimesAre(
- {{"S::f", "ERROR: Lifetime elision not enabled for 'f'"}})));
+ IsOkAndHolds(LifetimesAre({{"S::f",
+ "ERROR(ElisionNotEnabled): Lifetime "
+ "elision not enabled for 'f'"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Invalid_ThisOnFreeFunction) {
- EXPECT_THAT(
- GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
[[clang::annotate("lifetimes", "a: a -> a")]]
int* f(int*);
)")),
- IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Invalid lifetime annotation: too many lifetimes"}})));
- EXPECT_THAT(
- GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
+ IsOkAndHolds(LifetimesAre({{"f",
+ "ERROR(Other): Invalid lifetime "
+ "annotation: too many lifetimes"}})));
+ EXPECT_THAT(GetNamedLifetimeAnnotations(WithLifetimeMacros(R"(
int* $a f(int* $a) $a;
)")),
- IsOkAndHolds(LifetimesAre({{"f",
- "ERROR: Encountered a `this` lifetime on a "
- "function with no `this` parameter"}})));
+ IsOkAndHolds(LifetimesAre(
+ {{"f",
+ "ERROR(Other): Encountered a `this` lifetime on a "
+ "function with no `this` parameter"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Invalid_WrongNumber) {
@@ -734,7 +768,8 @@
int* f(int**);
)"),
IsOkAndHolds(LifetimesAre(
- {{"f", "ERROR: Invalid lifetime annotation: too few lifetimes"}})));
+ {{"f",
+ "ERROR(Other): Invalid lifetime annotation: too few lifetimes"}})));
}
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Callback) {
diff --git a/lifetime_annotations/lifetime_error.h b/lifetime_annotations/lifetime_error.h
new file mode 100644
index 0000000..fde8dd0
--- /dev/null
+++ b/lifetime_annotations/lifetime_error.h
@@ -0,0 +1,46 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef THIRD_PARTY_CRUBIT_LIFETIME_ANNOTATIONS_LIFETIME_ERROR_H_
+#define THIRD_PARTY_CRUBIT_LIFETIME_ANNOTATIONS_LIFETIME_ERROR_H_
+
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace tidy {
+namespace lifetimes {
+
+// Error information for errors that originate in the `lifetime_analysis`
+// package.
+class LifetimeError : public llvm::ErrorInfo<LifetimeError> {
+ public:
+ enum class Type {
+ ElisionNotEnabled,
+ CannotElideOutputLifetimes,
+ Other,
+ };
+
+ LifetimeError(Type type, std::string message)
+ : type_(type), message_(std::move(message)) {}
+
+ Type type() const { return type_; }
+
+ void log(llvm::raw_ostream& OS) const override { OS << message_; }
+
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+
+ static char ID;
+
+ private:
+ Type type_;
+ std::string message_;
+};
+
+} // namespace lifetimes
+} // namespace tidy
+} // namespace clang
+
+#endif // THIRD_PARTY_CRUBIT_LIFETIME_ANNOTATIONS_LIFETIME_ERROR_H_
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 7d58505..989a84d 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -15,6 +15,7 @@
#include "absl/strings/str_join.h"
#include "lifetime_annotations/function_lifetimes.h"
#include "lifetime_annotations/lifetime.h"
+#include "lifetime_annotations/lifetime_error.h"
#include "lifetime_annotations/lifetime_symbol_table.h"
#include "lifetime_annotations/pointee_type.h"
#include "clang/AST/Attr.h"
@@ -91,8 +92,8 @@
continue;
if (saw_annotate_type) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
"Only one `[[annotate_type(\"lifetime\", ...)]]` attribute may be "
"placed on a type");
}
@@ -328,8 +329,8 @@
llvm::SmallVector<std::string> lifetime_params = GetLifetimeParameters(type);
if (!lifetime_params.empty() && !lifetime_names.empty() &&
lifetime_names.size() != lifetime_params.size()) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Type has ", lifetime_params.size(),
" lifetime parameters but ", lifetime_names.size(),
" lifetime arguments were given"));
@@ -376,8 +377,8 @@
if (pointee.isNull() || type->isFunctionPointerType() ||
type->isFunctionReferenceType()) {
if (lifetime_params.empty() && !lifetime_names.empty())
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Type may not be annotated with lifetimes"));
}
if (pointee.isNull()) {
@@ -409,8 +410,8 @@
const clang::Expr* lifetime_name = nullptr;
if (!lifetime_names.empty()) {
if (lifetime_names.size() != 1) {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
absl::StrCat("Expected a single lifetime but ",
lifetime_names.size(), " were given"));
}
@@ -929,8 +930,8 @@
llvm::Expected<llvm::StringRef> EvaluateAsStringLiteral(
const clang::Expr* expr, const clang::ASTContext& ast_context) {
auto error = []() {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
+ return llvm::make_error<LifetimeError>(
+ LifetimeError::Type::Other,
"cannot evaluate argument as a string literal");
};