blob: 0f682dd3f8c7402d07de2b080b81bd55e6b0d9d8 [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 <algorithm>
#include <cassert>
#include <cstddef>
#include <functional>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#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/lifetime_substitutions.h"
#include "lifetime_annotations/type_lifetimes.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseSet.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();
}
clang::TypeLoc type_loc;
if (func->getTypeSourceInfo()) {
type_loc = func->getTypeSourceInfo()->getTypeLoc();
}
return Create(func->getType()->getAs<clang::FunctionProtoType>(), type_loc,
this_type, lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
const clang::FunctionProtoType* func, clang::TypeLoc func_type_loc,
const FunctionLifetimeFactory& lifetime_factory) {
return Create(func, func_type_loc, clang::QualType(), lifetime_factory);
}
llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForFunctionType(
const clang::FunctionProtoType* func,
const FunctionLifetimeFactory& lifetime_factory) {
return CreateForFunctionType(func, clang::TypeLoc(), 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->getFunctionObjectParameterType(), {}));
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, clang::TypeLoc type_loc,
const clang::QualType this_type,
const FunctionLifetimeFactory& lifetime_factory) {
FunctionLifetimes ret;
llvm::SmallVector<const clang::Attr*> attrs;
if (type_loc) {
StripAttributes(type_loc, attrs);
}
llvm::SmallVector<const clang::Expr*> lifetime_names;
if (llvm::Error err = GetAttributeLifetimes(attrs).moveInto(lifetime_names)) {
return std::move(err);
}
if (this_type.isNull() && !lifetime_names.empty()) {
return llvm::make_error<LifetimeError>(
LifetimeError::Type::Other,
absl::StrCat("Encountered a `this` lifetime on a function with no "
"`this` parameter"));
}
if (!this_type.isNull()) {
ValueLifetimes tmp;
const clang::Expr* lifetime_name = nullptr;
if (!lifetime_names.empty()) {
if (lifetime_names.size() != 1) {
return llvm::make_error<LifetimeError>(
LifetimeError::Type::Other,
absl::StrCat("Expected a single lifetime but ",
lifetime_names.size(), " were given"));
}
lifetime_name = lifetime_names.front();
}
if (llvm::Error err =
lifetime_factory.CreateThisLifetimes(this_type, lifetime_name)
.moveInto(tmp)) {
return std::move(err);
}
ret.this_lifetimes_ = std::move(tmp);
}
clang::FunctionTypeLoc func_type_loc;
if (type_loc) {
func_type_loc = type_loc.getAsAdjusted<clang::FunctionTypeLoc>();
}
ret.param_lifetimes_.reserve(type->getNumParams());
for (size_t i = 0; i < type->getNumParams(); i++) {
clang::TypeLoc param_type_loc;
if (func_type_loc) {
const clang::ParmVarDecl* param = func_type_loc.getParam(i);
if (param && param->getTypeSourceInfo()) {
param_type_loc = param->getTypeSourceInfo()->getTypeLoc();
}
}
ValueLifetimes tmp;
if (llvm::Error err =
lifetime_factory
.CreateParamLifetimes(type->getParamType(i), param_type_loc)
.moveInto(tmp)) {
return std::move(err);
}
ret.param_lifetimes_.push_back(std::move(tmp));
}
clang::TypeLoc return_type_loc;
if (func_type_loc) {
return_type_loc = func_type_loc.getReturnLoc();
}
if (llvm::Error err =
lifetime_factory
.CreateReturnLifetimes(type->getReturnType(), return_type_loc,
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 parentheses 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()) {
// However, don't add redundant parentheses.
if (!(s.size() >= 2 && s.front() == '(' && s.back() == ')')) {
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