diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -918,7 +918,7 @@ auto AST = TU.build(); EXPECT_THAT(*AST.getDiagnostics(), IsEmpty()); const auto *X = cast(findDecl(AST, "foo")).getParamDecl(0); - ASSERT_TRUE(X->getOriginalType()->getNullability() == + ASSERT_TRUE(X->getOriginalType().getNullability() == NullabilityKind::NonNull); } @@ -936,10 +936,10 @@ EXPECT_THAT(*AST.getDiagnostics(), ElementsAre(diagName("pp_eof_in_assume_nonnull"))); const auto *X = cast(findDecl(AST, "foo")).getParamDecl(0); - ASSERT_TRUE(X->getOriginalType()->getNullability() == + ASSERT_TRUE(X->getOriginalType().getNullability() == NullabilityKind::NonNull); const auto *Y = cast(findDecl(AST, "bar")).getParamDecl(0); - ASSERT_FALSE(Y->getOriginalType()->getNullability()); + ASSERT_FALSE(Y->getOriginalType().getNullability()); } TEST(DiagnosticsTest, InsideMacros) { diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2558,8 +2558,8 @@ bool hasSameNullabilityTypeQualifier(QualType SubT, QualType SuperT, bool IsParam) const { - auto SubTnullability = SubT->getNullability(); - auto SuperTnullability = SuperT->getNullability(); + auto SubTnullability = SubT.getNullability(); + auto SuperTnullability = SuperT.getNullability(); if (SubTnullability.has_value() == SuperTnullability.has_value()) { // Neither has nullability; return true if (!SubTnullability) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1400,6 +1400,12 @@ /// Remove all qualifiers including _Atomic. QualType getAtomicUnqualifiedType() const; + /// Determine the nullability of the given type. + /// + /// Accounts for nullability that is only captured as sugar within the type + /// system, as well as when it is part of the canonical type. + std::optional getNullability() const; + private: // These methods are implemented in a separate translation unit; // "static"-ize them to avoid creating temporary QualTypes in the diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6939,7 +6939,7 @@ PrettyArrayType->getIndexTypeQualifiers()); // int x[_Nullable] -> int * _Nullable - if (auto Nullability = Ty->getNullability()) { + if (auto Nullability = Ty.getNullability()) { Result = const_cast(this)->getAttributedType( AttributedType::getNullabilityAttrKind(*Nullability), Result, Result); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1534,6 +1534,16 @@ return getUnqualifiedType(); } +std::optional QualType::getNullability() const { + if (const PointerType *PT = getTypePtr()->getAs()) { + auto PointeeType = PT->getPointeeType(); + if (PointeeType.isOptionalQualified()) { + return NullabilityKind::Nullable; + } + } + return getTypePtr()->getNullability(); +} + std::optional> Type::getObjCSubstitutions(const DeclContext *dc) const { // Look through method scopes. diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4123,7 +4123,7 @@ bool CanCheckNullability = false; if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD) { - auto Nullability = PVD->getType()->getNullability(); + auto Nullability = PVD->getType().getNullability(); CanCheckNullability = Nullability && *Nullability == NullabilityKind::NonNull && PVD->getTypeSourceInfo(); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -759,7 +759,7 @@ if (!SanOpts.has(SanitizerKind::NullabilityAssign)) return; - auto Nullability = LHS.getType()->getNullability(); + auto Nullability = LHS.getType().getNullability(); if (!Nullability || *Nullability != NullabilityKind::NonNull) return; @@ -2616,7 +2616,7 @@ // function satisfy their nullability preconditions. This makes it necessary // to emit null checks for args in the function body itself. if (requiresReturnValueNullabilityCheck()) { - auto Nullability = Ty->getNullability(); + auto Nullability = Ty.getNullability(); if (Nullability && *Nullability == NullabilityKind::NonNull) { SanitizerScope SanScope(this); RetValNullabilityPrecondition = diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -960,7 +960,7 @@ // If we're checking nullability, we need to know whether we can check the // return value. Initialize the flag to 'true' and refine it in EmitParmDecl. if (SanOpts.has(SanitizerKind::NullabilityReturn)) { - auto Nullability = FnRetTy->getNullability(); + auto Nullability = FnRetTy.getNullability(); if (Nullability && *Nullability == NullabilityKind::NonNull) { if (!(SanOpts.has(SanitizerKind::ReturnsNonnullAttribute) && CurCodeDecl && CurCodeDecl->getAttr())) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -565,12 +565,12 @@ void Sema::diagnoseNullableToNonnullConversion(QualType DstType, QualType SrcType, SourceLocation Loc) { - std::optional ExprNullability = SrcType->getNullability(); + std::optional ExprNullability = SrcType.getNullability(); if (!ExprNullability || (*ExprNullability != NullabilityKind::Nullable && *ExprNullability != NullabilityKind::NullableResult)) return; - std::optional TypeNullability = DstType->getNullability(); + std::optional TypeNullability = DstType.getNullability(); if (!TypeNullability || *TypeNullability != NullabilityKind::NonNull) return; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -5636,7 +5636,7 @@ /// Returns true if the value evaluates to null. static bool CheckNonNullExpr(Sema &S, const Expr *Expr) { // If the expression has non-null type, it doesn't evaluate to null. - if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability()) { + if (auto nullability = Expr->IgnoreImplicit()->getType().getNullability()) { if (*nullability == NullabilityKind::NonNull) return false; } @@ -5721,7 +5721,7 @@ /// Determine whether the given type has a non-null nullability annotation. static bool isNonNullType(QualType type) { - if (auto nullability = type->getNullability()) + if (auto nullability = type.getNullability()) return *nullability == NullabilityKind::NonNull; return false; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3399,8 +3399,8 @@ static void mergeParamDeclTypes(ParmVarDecl *NewParam, const ParmVarDecl *OldParam, Sema &S) { - if (auto Oldnullability = OldParam->getType()->getNullability()) { - if (auto Newnullability = NewParam->getType()->getNullability()) { + if (auto Oldnullability = OldParam->getType().getNullability()) { + if (auto Newnullability = NewParam->getType().getNullability()) { if (*Oldnullability != *Newnullability) { S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr) << DiagNullabilityKind( diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -2370,8 +2370,8 @@ !S.Context.hasSameNullabilityTypeQualifier(MethodImpl->getReturnType(), MethodDecl->getReturnType(), false)) { - auto nullabilityMethodImpl = *MethodImpl->getReturnType()->getNullability(); - auto nullabilityMethodDecl = *MethodDecl->getReturnType()->getNullability(); + auto nullabilityMethodImpl = *MethodImpl->getReturnType().getNullability(); + auto nullabilityMethodDecl = *MethodDecl->getReturnType().getNullability(); S.Diag(MethodImpl->getLocation(), diag::warn_conflicting_nullability_attr_overriding_ret_types) << DiagNullabilityKind(nullabilityMethodImpl, @@ -2458,10 +2458,10 @@ !S.Context.hasSameNullabilityTypeQualifier(ImplTy, IfaceTy, true)) { S.Diag(ImplVar->getLocation(), diag::warn_conflicting_nullability_attr_overriding_param_types) - << DiagNullabilityKind(*ImplTy->getNullability(), + << DiagNullabilityKind(*ImplTy.getNullability(), ((ImplVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)) - << DiagNullabilityKind(*IfaceTy->getNullability(), + << DiagNullabilityKind(*IfaceTy.getNullability(), ((IfaceVar->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) != 0)); S.Diag(IfaceVar->getLocation(), diag::note_previous_declaration); @@ -4546,8 +4546,8 @@ QualType prevType, bool prevUsesCSKeyword) { // Determine the nullability of both types. - auto nullability = type->getNullability(); - auto prevNullability = prevType->getNullability(); + auto nullability = type.getNullability(); + auto prevNullability = prevType.getNullability(); // Easy case: both have nullability. if (nullability.has_value() == prevNullability.has_value()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9308,7 +9308,7 @@ return ResTy; auto GetNullability = [](QualType Ty) { - std::optional Kind = Ty->getNullability(); + std::optional Kind = Ty.getNullability(); if (Kind) { // For our purposes, treat _Nullable_result as _Nullable. if (*Kind == NullabilityKind::NullableResult) @@ -9345,7 +9345,7 @@ return ResTy; // Strip all nullability from ResTy. - while (ResTy->getNullability()) + while (ResTy.getNullability()) ResTy = ResTy.getSingleStepDesugaredType(Ctx); // Create a new AttributedType with the new nullability kind. diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -593,7 +593,7 @@ BoxedType = NSStringPointer; // Transfer the nullability from method's return type. std::optional Nullability = - BoxingMethod->getReturnType()->getNullability(); + BoxingMethod->getReturnType().getNullability(); if (Nullability) BoxedType = Context.getAttributedType( AttributedType::getNullabilityAttrKind(*Nullability), BoxedType, @@ -1465,7 +1465,7 @@ auto transferNullability = [&](QualType type) -> QualType { // If the method's result type has nullability, extract it. if (auto nullability = - Method->getSendResultType(ReceiverType)->getNullability()) { + Method->getSendResultType(ReceiverType).getNullability()) { // Strip off any outer nullability sugar from the provided type. (void)AttributedType::stripOuterNullability(type); @@ -1544,7 +1544,7 @@ assert(MD->isClassMethod() && "expected a class method"); QualType NewResultType = Context.getObjCObjectPointerType( Context.getObjCInterfaceType(MD->getClassInterface())); - if (auto Nullability = resultType->getNullability()) + if (auto Nullability = resultType.getNullability()) NewResultType = Context.getAttributedType( AttributedType::getNullabilityAttrKind(*Nullability), NewResultType, NewResultType); @@ -1562,7 +1562,7 @@ // Map the nullability of the result into a table index. unsigned receiverNullabilityIdx = 0; if (std::optional nullability = - ReceiverType->getNullability()) { + ReceiverType.getNullability()) { if (*nullability == NullabilityKind::NullableResult) nullability = NullabilityKind::Nullable; receiverNullabilityIdx = 1 + static_cast(*nullability); @@ -1570,7 +1570,7 @@ unsigned resultNullabilityIdx = 0; if (std::optional nullability = - resultType->getNullability()) { + resultType.getNullability()) { if (*nullability == NullabilityKind::NullableResult) nullability = NullabilityKind::Nullable; resultNullabilityIdx = 1 + static_cast(*nullability); @@ -1603,7 +1603,7 @@ } else { resultType = resultType.getDesugaredType(Context); } - } while (resultType->getNullability()); + } while (resultType.getNullability()); // Add nullability back if needed. if (newResultNullabilityIdx > 0) { diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -777,8 +777,8 @@ if (Context.getCanonicalFunctionResultType(ReturnType) == Context.getCanonicalFunctionResultType(CSI.ReturnType)) { // Use the return type with the strictest possible nullability annotation. - auto RetTyNullability = ReturnType->getNullability(); - auto BlockNullability = CSI.ReturnType->getNullability(); + auto RetTyNullability = ReturnType.getNullability(); + auto BlockNullability = CSI.ReturnType.getNullability(); if (BlockNullability && (!RetTyNullability || hasWeakerNullability(*RetTyNullability, *BlockNullability))) diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -2754,7 +2754,7 @@ if (Attributes & ObjCPropertyAttribute::kind_weak) { // 'weak' and 'nonnull' are mutually exclusive. - if (auto nullability = PropertyTy->getNullability()) { + if (auto nullability = PropertyTy.getNullability()) { if (*nullability == NullabilityKind::NonNull) Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "nonnull" << "weak"; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -4713,7 +4713,7 @@ complainAboutMissingNullability = CAMN_InnerPointers; if (T->canHaveNullability(/*ResultIfUnknown*/ false) && - !T->getNullability()) { + !T.getNullability()) { // Note that we allow but don't require nullability on dependent types. ++NumPointersRemaining; } @@ -4935,7 +4935,7 @@ // nullability and perform consistency checking. if (S.CodeSynthesisContexts.empty()) { if (T->canHaveNullability(/*ResultIfUnknown*/ false) && - !T->getNullability()) { + !T.getNullability()) { if (isVaList(T)) { // Record that we've seen a pointer, but do nothing else. if (NumPointersRemaining > 0) @@ -4959,7 +4959,7 @@ } if (complainAboutMissingNullability == CAMN_Yes && T->isArrayType() && - !T->getNullability() && !isVaList(T) && D.isPrototypeContext() && + !T.getNullability() && !isVaList(T) && D.isPrototypeContext() && !hasOuterPointerLikeChunk(D, D.getNumTypeObjects())) { checkNullabilityConsistency(S, SimplePointerKind::Array, D.getDeclSpec().getTypeSpecTypeLoc()); @@ -7415,7 +7415,7 @@ // This (unlike the code above) looks through typedefs that might // have nullability specifiers on them, which means we cannot // provide a useful Fix-It. - if (auto existingNullability = desugared->getNullability()) { + if (auto existingNullability = desugared.getNullability()) { if (nullability != *existingNullability) { S.Diag(nullabilityLoc, diag::err_nullability_conflicting) << DiagNullabilityKind(nullability, isContextSensitive) @@ -7514,7 +7514,7 @@ // If we started with an object pointer type, rebuild it. if (ptrType) { equivType = S.Context.getObjCObjectPointerType(equivType); - if (auto nullability = type->getNullability()) { + if (auto nullability = type.getNullability()) { // We create a nullability attribute from the __kindof attribute. // Make sure that will make sense. assert(attr.getAttributeSpellingListIndex() == 0 && diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -1332,7 +1332,7 @@ if (T.isNull()) return CXTypeNullability_Invalid; - if (auto nullability = T->getNullability()) { + if (auto nullability = T.getNullability()) { switch (*nullability) { case NullabilityKind::NonNull: return CXTypeNullability_NonNull;