diff options
author | Yitzhak Mandelbaum <yitzhakm@google.com> | 2023-04-14 14:21:41 +0000 |
---|---|---|
committer | Yitzhak Mandelbaum <yitzhakm@google.com> | 2023-04-17 18:02:51 +0000 |
commit | cd22e0dc9d0b5487eb2d54dd028a2aa439627159 (patch) | |
tree | d3664f0512d53ffc72d879814ed411a569ebe187 /clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp | |
parent | 774703ec08f62d702d40e1f97cd35ae5f732c544 (diff) | |
download | llvm-cd22e0dc9d0b5487eb2d54dd028a2aa439627159.zip llvm-cd22e0dc9d0b5487eb2d54dd028a2aa439627159.tar.gz llvm-cd22e0dc9d0b5487eb2d54dd028a2aa439627159.tar.bz2 |
[clang][dataflow] Refine matching of optional types to anchor at top level.
This patch refines the matching of the relevant optional types to anchor on the
global namespace. Previously, we could match anything with the right name
(e.g. `base::Optional`) even if nested within other namespaces. This over
matching resulted in an assertion violation when _different_ `base::Optional`
was encountered nested inside another namespace.
Fixes issue #57036.
Differential Revision: https://reviews.llvm.org/D148344
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp')
-rw-r--r-- | clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp | 34 |
1 files changed, 27 insertions, 7 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index 15120233..88f3488 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -43,9 +43,9 @@ using LatticeTransferState = TransferState<NoopLattice>; DeclarationMatcher optionalClass() { return classTemplateSpecializationDecl( - anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), - hasName("__optional_destruct_base"), hasName("absl::optional"), - hasName("base::Optional")), + hasAnyName("::std::optional", "::std::__optional_storage_base", + "::std::__optional_destruct_base", "::absl::optional", + "::base::Optional"), hasTemplateArgument(0, refersToType(type().bind("T")))); } @@ -251,14 +251,34 @@ QualType stripReference(QualType Type) { return Type->isReferenceType() ? Type->getPointeeType() : Type; } +bool isTopLevelNamespaceWithName(const NamespaceDecl &NS, + llvm::StringRef Name) { + return NS.getDeclName().isIdentifier() && NS.getName() == Name && + NS.getParent() != nullptr && NS.getParent()->isTranslationUnit(); +} + /// Returns true if and only if `Type` is an optional type. bool isOptionalType(QualType Type) { if (!Type->isRecordType()) return false; - // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. - auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); - return TypeName == "std::optional" || TypeName == "absl::optional" || - TypeName == "base::Optional"; + const CXXRecordDecl *D = Type->getAsCXXRecordDecl(); + if (D == nullptr || !D->getDeclName().isIdentifier()) + return false; + if (D->getName() == "optional") { + if (const auto *N = + dyn_cast_or_null<NamespaceDecl>(D->getDeclContext())) + return N->isStdNamespace() || isTopLevelNamespaceWithName(*N, "absl"); + return false; + } + + if (D->getName() == "Optional") { + // Check whether namespace is "::base". + const auto *N = + dyn_cast_or_null<NamespaceDecl>(D->getDeclContext()); + return N != nullptr && isTopLevelNamespaceWithName(*N, "base"); + } + + return false; } /// Returns the number of optional wrappers in `Type`. |