blob: 1ae177b7019d2a0061b37587ed4462b12824ad37 [file] [log] [blame]
// 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
#include "lifetime_annotations/function_lifetimes.h"
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "lifetime_annotations/lifetime.h"
#include "lifetime_annotations/type_lifetimes.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
namespace clang {
namespace tidy {
namespace lifetimes {
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForDecl(
const clang::FunctionDecl* func,
const FunctionLifetimeFactory& lifetime_factory) {
clang::QualType this_type;
if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(func);
method && !method->isStatic()) {
this_type = method->getThisType();
}
return Create(func->getType()->getAs<clang::FunctionProtoType>(), this_type,
lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
const clang::FunctionProtoType* func,
const FunctionLifetimeFactory& lifetime_factory) {
return Create(func, clang::QualType(), lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateCopy(
const LifetimeFactory& factory) const {
FunctionLifetimes ret;
if (this_lifetimes_.has_value()) {
ValueLifetimes tmp;
if (llvm::Error err =
ValueLifetimes::Create(this_lifetimes_->Type(), factory)
.moveInto(tmp)) {
return std::move(err);
}
ret.this_lifetimes_ = std::move(tmp);
}
ret.param_lifetimes_.reserve(param_lifetimes_.size());
for (size_t i = 0; i < param_lifetimes_.size(); i++) {
ValueLifetimes tmp;
if (llvm::Error err =
ValueLifetimes::Create(param_lifetimes_[i].Type(), factory)
.moveInto(tmp)) {
return std::move(err);
}
ret.param_lifetimes_.push_back(std::move(tmp));
}
if (llvm::Error err =
ValueLifetimes::Create(return_lifetimes_.Type(), factory)
.moveInto(ret.return_lifetimes_)) {
return std::move(err);
}
return ret;
}
FunctionLifetimes FunctionLifetimes::ForOverriddenMethod(
const clang::CXXMethodDecl* method) {
FunctionLifetimes ret = *this;
assert(this_lifetimes_.has_value());
ret.this_lifetimes_ = ValueLifetimes::ForPointerLikeType(
method->getThisType(),
this_lifetimes_.value().GetPointeeLifetimes().GetFieldOrBaseLifetimes(
method->getThisObjectType(), {}));
assert(ret.IsValidForDecl(method));
return ret;
}
bool FunctionLifetimes::IsValidForDecl(const clang::FunctionDecl* function) {
// TODO(veluca): also validate the types of the arguments, and/or the type of
// the function itself.
if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(function);
method && !method->isStatic()) {
if (!this_lifetimes_.has_value()) return false;
}
return param_lifetimes_.size() == function->param_size();
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::Create(
const clang::FunctionProtoType* type, const clang::QualType this_type,
const FunctionLifetimeFactory& lifetime_factory) {
FunctionLifetimes ret;
if (!this_type.isNull()) {
ValueLifetimes tmp;
if (llvm::Error err =
lifetime_factory.CreateParamLifetimes(this_type).moveInto(tmp)) {
return std::move(err);
}
ret.this_lifetimes_ = std::move(tmp);
}
ret.param_lifetimes_.reserve(type->getNumParams());
for (size_t i = 0; i < type->getNumParams(); i++) {
ValueLifetimes tmp;
if (llvm::Error err =
lifetime_factory.CreateParamLifetimes(type->getParamType(i))
.moveInto(tmp)) {
return std::move(err);
}
ret.param_lifetimes_.push_back(std::move(tmp));
}
if (llvm::Error err =
lifetime_factory
.CreateReturnLifetimes(type->getReturnType(),
ret.param_lifetimes_, ret.this_lifetimes_)
.moveInto(ret.return_lifetimes_)) {
return std::move(err);
}
return ret;
}
bool FunctionLifetimes::HasAny(
const std::function<bool(Lifetime)>& predicate) const {
return std::any_of(param_lifetimes_.begin(), param_lifetimes_.end(),
[&predicate](const ValueLifetimes& v) {
return v.HasAny(predicate);
}) ||
return_lifetimes_.HasAny(predicate) ||
(this_lifetimes_.has_value() && this_lifetimes_->HasAny(predicate));
}
llvm::DenseSet<Lifetime> FunctionLifetimes::AllFreeLifetimes() const {
// TODO(veluca): this is incorrect in the presence of HRTBs.
llvm::DenseSet<Lifetime> all_lifetimes;
Traverse([&all_lifetimes](Lifetime l, Variance) {
if (l != Lifetime::Static()) {
all_lifetimes.insert(l);
}
});
return all_lifetimes;
}
void FunctionLifetimes::SubstituteLifetimes(
const LifetimeSubstitutions& subst) {
// TODO(veluca): this is incorrect in the presence of HRTBs.
std::for_each(param_lifetimes_.begin(), param_lifetimes_.end(),
[&subst](ValueLifetimes& v) { v.SubstituteLifetimes(subst); });
return_lifetimes_.SubstituteLifetimes(subst);
if (this_lifetimes_.has_value()) {
this_lifetimes_->SubstituteLifetimes(subst);
}
}
void FunctionLifetimes::Traverse(
std::function<void(Lifetime&, Variance)> visitor) {
for (auto& param : param_lifetimes_) {
param.Traverse(visitor);
}
return_lifetimes_.Traverse(visitor);
if (this_lifetimes_.has_value()) {
this_lifetimes_->Traverse(visitor);
}
}
void FunctionLifetimes::Traverse(
std::function<void(const Lifetime&, Variance)> visitor) const {
const_cast<FunctionLifetimes*>(this)->Traverse(
[visitor](Lifetime& l, Variance v) { visitor(l, v); });
}
std::string FunctionLifetimes::DebugString(LifetimeFormatter formatter) const {
std::vector<std::string> formatted_param_lifetimes;
// Add parenteses to non-trivial nested lifetimes, i.e. fn parameters with >1
// lifetimes, as their DebugString does not contain parentheses.
auto maybe_add_parentheses = [&](std::string s) {
if (s.find_first_of(",()") != std::string::npos || s.empty()) {
return absl::StrCat("(", s, ")");
}
return s;
};
for (const auto& param : param_lifetimes_) {
formatted_param_lifetimes.push_back(
maybe_add_parentheses(param.DebugString(formatter)));
}
std::string result;
if (this_lifetimes_.has_value()) {
result = absl::StrCat(
maybe_add_parentheses(this_lifetimes_->DebugString(formatter)), ":");
}
if (!result.empty() && !formatted_param_lifetimes.empty()) {
absl::StrAppend(&result, " ");
}
absl::StrAppend(&result, absl::StrJoin(formatted_param_lifetimes, ", "));
if (return_lifetimes_.HasLifetimes()) {
if (!result.empty()) {
absl::StrAppend(&result, " ");
}
absl::StrAppend(
&result, "-> ",
maybe_add_parentheses(return_lifetimes_.DebugString(formatter)));
}
return result;
}
std::ostream& operator<<(std::ostream& os,
const FunctionLifetimes& func_lifetimes) {
return os << func_lifetimes.DebugString();
}
} // namespace lifetimes
} // namespace tidy
} // namespace clang