| // 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 |