blob: 14eecb667a746cfd8416c6c80c313af8e35562eb [file] [log] [blame] [edit]
// 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 "nullability/forwarding_functions.h"
#include <cassert>
#include "absl/base/nullability.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/TemplateBase.h"
#include "clang/Basic/LLVM.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
namespace clang::tidy::nullability {
namespace {
bool isStdMakeUniqueWithNontrivalConstructor(const FunctionDecl &FD) {
if (!FD.getDeclName().isIdentifier()) return false;
if (FD.getName() != "make_unique") return false;
const auto *Namespace = dyn_cast_or_null<NamespaceDecl>(FD.getDeclContext());
if (Namespace == nullptr) return false;
if (!Namespace->isStdNamespace()) return false;
// Check if it is (a) make_unique<T>(args...), or (b) make_unique<T[]>(size).
// The array version would just call the 0-arg constructor, which isn't
// very interesting to diagnose. We also want T in the first version to
// be a record type so that there is a constructor call to analyze.
const TemplateArgumentList *TemplateArgs = FD.getTemplateSpecializationArgs();
if (!TemplateArgs) return false;
ArrayRef<TemplateArgument> TemplateArgsArray = TemplateArgs->asArray();
if (TemplateArgsArray.empty()) return false;
const TemplateArgument &FirstArg = TemplateArgsArray.front();
assert(FirstArg.getKind() == TemplateArgument::Type);
if (FirstArg.getKind() != TemplateArgument::Type) return false;
return FirstArg.getAsType()->isRecordType();
}
const Expr *findMakeUniqueNewExprInitializer(const Stmt *absl_nonnull S) {
// Do a simple walk over the children, which should be sufficient for
// make_unique. RecursiveASTVisitor can also work but is supposed to be more
// expensive to compile. make_unique should have a `new T( ... )` in its
// body. Find that, and extract the Initializer.
if (auto *NE = dyn_cast<CXXNewExpr>(S)) {
return NE->getInitializer();
}
for (const Stmt *Child : S->children()) {
if (auto *E = findMakeUniqueNewExprInitializer(Child)) return E;
}
// We expect to find a `new` in the body of make_unique (vs indirecting to
// another function), since it's a fairly simple implementation.
llvm::errs()
<< "Nullability: expected to find `new` in make_unique but did not\n";
assert(false);
return nullptr;
}
} // namespace
const Expr *absl_nullable getUnderlyingInitExprInStdMakeUnique(
const FunctionDecl &Decl) {
if (!isStdMakeUniqueWithNontrivalConstructor(Decl)) return nullptr;
if (Decl.getBody() == nullptr) return nullptr;
return findMakeUniqueNewExprInitializer(Decl.getBody());
}
const FunctionDecl *absl_nullable getLastForwardingFunctionLayer(
const FunctionDecl &Decl) {
if (!isStdMakeUniqueWithNontrivalConstructor(Decl)) return nullptr;
if (Decl.getBody() == nullptr) return nullptr;
return &Decl;
}
} // namespace clang::tidy::nullability