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