diff --git a/clang/docs/BSC/BiShengCLanguageUserManual.md b/clang/docs/BSC/BiShengCLanguageUserManual.md index d6bf89f8aec7beefa73c056ed4a674953966b860..d081aac2ab10568a40be41e747d95693ebd1eced 100644 --- a/clang/docs/BSC/BiShengCLanguageUserManual.md +++ b/clang/docs/BSC/BiShengCLanguageUserManual.md @@ -5814,6 +5814,7 @@ int main() { | bsc-nullability | 可屏蔽所有 Nullability 数据流分析过程的报错,
包括 deref-nullable、pass-nullable、return-nullable、cast-nullable、
assign-nullable、assign-nonnull 错误类型标识。 | | use-moved-owned | "use of moved value: \`%0\`“
"use of partially moved value: \`%0\`, %1 moved"
"use of all moved value: \`%0\`" | | use-uninit-owned | "use of uninitialized value: \`%0\`"
"use of possibly uninitialized value: \`%0\`" | +| init-nonnull | "`%0` is a _Nonnull pointer and must be properly initialized." | | use-owned | 包括use-moved-owned、use-uninit-owned 错误类型标识可屏蔽的错误日志 | | assign-moved-owned | "assign to partially moved value: \`%0\`, %1 moved"
"assign to possibly partially moved value: \`%0\`, %1 possibly moved"
"assign to all moved value: \`%0\`"
"assign to part of moved value: \`%0\`" | | assign-uninit-owned | "assign to part of uninitialized value: \`%0\`" | diff --git a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h index 7e862348f0b47536b7395b7c28e7fbe1ac77c7c9..ab4f4f7baa14dd3495b676fe78a6b6f001732f78 100644 --- a/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h +++ b/clang/include/clang/Analysis/Analyses/BSC/BSCNullabilityCheck.h @@ -28,6 +28,7 @@ enum NullabilityCheckDiagKind { NullableCastNonnull, NullablePointerDereference, NullablePointerAccessMember, + NonnullInitByDefault, NullabilityMaxDiagKind }; @@ -37,17 +38,21 @@ const unsigned NullabilityDiagIdList[] = { diag::err_return_nullable, diag::err_nullable_cast_nonnull, diag::err_nullable_pointer_dereference, - diag::err_nullable_pointer_access_member}; + diag::err_nullable_pointer_access_member, + diag::err_nonnull_init_by_default}; struct NullabilityCheckDiagInfo { SourceLocation Loc; NullabilityCheckDiagKind Kind; - + std::string Name; NullabilityCheckDiagInfo(SourceLocation Loc, NullabilityCheckDiagKind Kind) : Loc(Loc), Kind(Kind) {} + NullabilityCheckDiagInfo(SourceLocation Loc, NullabilityCheckDiagKind Kind, + std::string Name) + : Loc(Loc), Kind(Kind), Name(Name) {} bool operator==(const NullabilityCheckDiagInfo &other) const { - return Loc == other.Loc && Kind == other.Kind; + return Loc == other.Loc && Kind == other.Kind && Name == other.Name; } }; @@ -97,7 +102,23 @@ public: }); for (const NullabilityCheckDiagInfo &DI : DIV) { - S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)); + switch (DI.Kind) { + case NonnullAssignedByNullable: + case PassNullableArgument: + case ReturnNullable: + case NullableCastNonnull: + case NullablePointerDereference: + case NullablePointerAccessMember: + S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)); + break; + case NonnullInitByDefault: + S.Diag(DI.Loc, getNullabilityDiagID(DI.Kind)) << DI.Name; + break; + default: + llvm_unreachable("Unknown error type"); + break; + } + S.getDiagnostics().increaseNullabilityCheckErrors(); } } diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td index 7a45626a10edc5842f8b1fec007f7095e8cf5cd3..e20ae4c594f5dd9f1b782bcd01358856929f79ed 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCGroups.td @@ -7,13 +7,15 @@ def ReturnNullable : DiagGroup<"return-nullable">; def CastNullable : DiagGroup<"cast-nullable">; def AssignNullable : DiagGroup<"assign-nullable">; def AssignNonnull : DiagGroup<"assign-nonnull">; +def InitNonnull : DiagGroup<"init-nonnull">; def BSCNullability : DiagGroup<"bsc-nullability", [DerefNullable, PassNullable, ReturnNullable, CastNullable, AssignNullable, - AssignNonnull]>; + AssignNonnull, + InitNonnull]>; def UseMovedOwned : DiagGroup<"use-moved-owned">; def UseUninitOwned : DiagGroup<"use-uninit-owned">; diff --git a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td index cc7cc3d1c97a141d351b6a5ca1e65e66d01322cc..ca7d1bfe28330ba7a13097d2f53ba36b7d9edc0d 100644 --- a/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td +++ b/clang/include/clang/Basic/BSC/DiagnosticBSCSemaKinds.td @@ -278,4 +278,7 @@ def err_nullable_pointer_access_member : Error< def err_nonnull_assigned_by_nullable : Error< "nonnull pointer cannot be assigned by nullable pointer">, InGroup; +def err_nonnull_init_by_default : Error< + "`%0` is a _Nonnull pointer and must be properly initialized.">, + InGroup; diff --git a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp index 111d821cafb2976bf522b0f44a52aadcf4f53824..30393cf5e1c0b8b8a926068287304c6d3ef19c57 100644 --- a/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp +++ b/clang/lib/Analysis/BSC/BSCNullabilityCheck.cpp @@ -116,8 +116,8 @@ public: void HandleVarDeclInit(DeclStmt *DS, VarDecl *VD); void HandlePointerInit(DeclStmt *DS, VarDecl *VD); void HandleStructInit(DeclStmt *DS, VarDecl *VD); - void HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD, Expr *FieldInit); - void HandleFieldStructInit(DeclStmt *DS, VarDecl *VD, RecordDecl *RD,InitListExpr *ILE); + void HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD, + Expr *FieldInit, std::string FullFieldPath); void VisitBinaryOperator(BinaryOperator *BO); void VisitUnaryOperator(UnaryOperator *UO); void VisitMemberExpr(MemberExpr *ME); @@ -126,6 +126,8 @@ public: void VisitCStyleCastExpr(CStyleCastExpr *CSCE); NullabilityKind getExprPathNullability(Expr *E); void PassConditionStatusToSuccBlocks(Stmt *Cond); + void HandleNestedStructInit(DeclStmt *DS, VarDecl *TopVD, RecordDecl *RD, + InitListExpr *InitList, std::string FieldPrefix); }; } // namespace @@ -328,99 +330,90 @@ void TransferFunctions::HandlePointerInit(DeclStmt *DS, VarDecl *VD) { // Init a struct variable with pointer fields. void TransferFunctions::HandleStructInit(DeclStmt *DS, VarDecl *VD) { - Expr *Init = VD->getInit(); - if (!Init) - return; - - // Skip all ParenExpr/ImplicitCastExpr - Init = Init->IgnoreParenImpCasts(); - // Strip all CStyleCastExpr (casts) and CompoundLiteralExpr (compound - // literals) - while (true) { - if (auto *CSE = dyn_cast(Init)) { - Init = CSE->getSubExpr()->IgnoreParenImpCasts(); - continue; - } - if (auto *CLE = dyn_cast(Init)) { - if (Expr *Sub = CLE->getInitializer()) { - Init = Sub->IgnoreParenImpCasts(); - continue; + if (auto RecTy = dyn_cast(VD->getType().getCanonicalType())) { + if (RecordDecl *RD = RecTy->getDecl()) { + if (Expr *Init = VD->getInit()) { + + Init = Init->IgnoreParenImpCasts(); + while (true) { + if (auto *CSE = dyn_cast(Init)) { + Init = CSE->getSubExpr()->IgnoreParenImpCasts(); + continue; + } + if (auto *CLE = dyn_cast(Init)) { + if (Expr *Sub = CLE->getInitializer()) { + Init = Sub->IgnoreParenImpCasts(); + continue; + } + } + break; + } + if (auto InitListE = dyn_cast(Init)) { + HandleNestedStructInit(DS, VD, RD, InitListE, ""); + } } } - break; - } - - // Finally attempt InitListExpr - auto *InitListE = dyn_cast(Init); - if (!InitListE) - return; - - // Get the RecordDecl of variable VD - QualType QT = VD->getType().getCanonicalType(); - if (auto *RT = dyn_cast(QT)) { - RecordDecl *RD = RT->getDecl(); - // Use ParentVD = VD as the recursive context; all subfields belong to the - // same top-level variable - HandleFieldStructInit(DS, VD, RD, InitListE); } } -// Modified by Huang Bowen -void TransferFunctions::HandleFieldStructInit(DeclStmt *DS, VarDecl *ParentVD, - RecordDecl *RD, - InitListExpr *ILE) { - Expr **Inits = ILE->getInits(); - // Iterate over all fields in the record +void TransferFunctions::HandleNestedStructInit(DeclStmt *DS, VarDecl *TopVD, + RecordDecl *RD, + InitListExpr *InitList, + std::string FieldPrefix) { + Expr **Inits = InitList->getInits(); for (FieldDecl *FD : RD->fields()) { - Expr *FieldInit = Inits[FD->getFieldIndex()]->IgnoreParenImpCasts(); + unsigned idx = FD->getFieldIndex(); + std::string fullFieldPath = FieldPrefix + "." + FD->getNameAsString(); + + Expr *fieldInit = Inits[idx]; // Skip all ParenExpr/ImplicitCastExpr - FieldInit = FieldInit->IgnoreParenImpCasts(); - // Process C99 compound literals (CompoundLiteralExpr) and strip all - // CStyleCastExpr (casts) and CompoundLiteralExpr (compound literals) while (true) { - if (auto *CSE = dyn_cast(FieldInit)) { - FieldInit = CSE->getSubExpr()->IgnoreParenImpCasts(); + if (auto *CSE = dyn_cast(fieldInit)) { + fieldInit = CSE->getSubExpr()->IgnoreParenImpCasts(); continue; } - if (auto *CLE = dyn_cast(FieldInit)) { + if (auto *CLE = dyn_cast(fieldInit)) { if (Expr *Sub = CLE->getInitializer()) { - FieldInit = Sub->IgnoreParenImpCasts(); + fieldInit = Sub->IgnoreParenImpCasts(); continue; } } break; } - // (A) If the field is a pointer, perform nullability check if (FD->getType()->isPointerType()) { - HandleFieldInit(DS, ParentVD, FD, Inits[FD->getFieldIndex()]); - } - // (B) If the field is also a struct/class, recurse into it - else if (auto *NestedRT = - dyn_cast(FD->getType().getCanonicalType())) { - if (RecordDecl *NestedRD = NestedRT->getDecl()) { - // The inner FieldInit should be an InitListExpr - if (auto *NestedILE = dyn_cast(FieldInit)) { - HandleFieldStructInit(DS, ParentVD, NestedRD, NestedILE); + HandleFieldInit(DS, TopVD, FD, fieldInit, fullFieldPath); + } else if (auto nestedRecTy = + dyn_cast(FD->getType().getCanonicalType())) { + if (RecordDecl *nestedRD = nestedRecTy->getDecl()) { + if (auto nestedInitList = dyn_cast(fieldInit)) { + HandleNestedStructInit(DS, TopVD, nestedRD, nestedInitList, + fullFieldPath); } } } } } -void TransferFunctions::HandleFieldInit(DeclStmt *DS, VarDecl *VD, FieldDecl *FD, Expr *FieldInit) { +void TransferFunctions::HandleFieldInit(DeclStmt *DS, VarDecl *VD, + FieldDecl *FD, Expr *FieldInit, + std::string FullFieldPath) { + FieldPath FP(VD, FullFieldPath); + NullabilityKind LHSKind = getDefNullability(FD->getType(), Ctx); NullabilityKind RHSKind = getExprPathNullability(FieldInit); + if (LHSKind == NullabilityKind::NonNull) { - if (RHSKind == NullabilityKind::Nullable && ShouldReportNullPtrError(DS)) { - NullabilityCheckDiagInfo DI(FieldInit->getBeginLoc(), NonnullAssignedByNullable); + if (RHSKind != NullabilityKind::NonNull && ShouldReportNullPtrError(DS)) { + NullabilityCheckDiagInfo DI(VD->getBeginLoc(), NonnullInitByDefault, + VD->getNameAsString() + FullFieldPath); Reporter.addDiagInfo(DI); } } else { - FieldPath FP(VD, "." + FD->getNameAsString()); - if (CurrStatusFP.count(FP)) + if (CurrStatusFP.count(FP)) { CurrStatusFP[FP] = RHSKind; + } } } diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs new file mode 100644 index 0000000000000000000000000000000000000000..1af9d928484362d8ec68a8e1d841ae4c1d3d1a70 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct/nested_owned_struct.cbs @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -nullability-check=all -verify %s + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +owned struct B{ + public: + int * owned p; + ~B(B this){ + safe_free((void*owned)this.p); + } +}; + +owned struct A{ + public: + B b; +}; + +int main(){ + B b1={};// expected-error {{`b1.p` is a _Nonnull pointer and must be properly initialized.}} + return 0; +} + + + diff --git a/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs new file mode 100644 index 0000000000000000000000000000000000000000..f0776d7fd20951cbdadcbe4c68f98e9a4175e534 --- /dev/null +++ b/clang/test/BSC/Negative/NullabilityCheck/nested_owned_struct1/nested_owned_struct1.cbs @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -nullability-check=all -verify %s + +safe T *owned safe_malloc(T t); +safe void safe_free(void *owned p); + +owned struct B{ + public: + int * owned p; + int pp; + ~B(B this){ + safe_free((void*owned)this.p); + } +}; + +owned struct A{ + public: + B b; + int * owned p; + int * q; + int * _Nullable nq; + ~A(A this){ + safe_free((void* owned)this.p); + } +}; + +void test1(){ + A a ={};// #1 + B b = {};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}} +} +// expected-error@#1 {{`a.b.p` is a _Nonnull pointer and must be properly initialized.}} +// expected-error@#1 {{`a.p` is a _Nonnull pointer and must be properly initialized.}} + +void test2(){ + B b ={};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}} + A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}} +} + +void test3(){ + B b = {.p=nullptr};// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}} + A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}} +} + +void test4(){ + B b = {.p=safe_malloc(42)}; + A a = {.b=b};// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}} +} + +void test5(){ + A a ={.b={safe_malloc(42)},.p=safe_malloc(420)}; +} \ No newline at end of file diff --git a/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs b/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs index 83370dcc9f9d79b7aa83ce663eaacf3aba4f5a2b..033d9659b23b835324a314f2ce6d97628129dee2 100644 --- a/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs +++ b/clang/test/BSC/Negative/NullabilityCheck/nullable_nonnull_conflict/component_struct_asign.cbs @@ -1,43 +1,59 @@ // RUN: %clang_cc1 -nullability-check=all -verify %s + int *_Nullable foo() { return nullptr; } + struct inside{ int* _Nonnull p; }; + struct mid{ struct inside c1; }; + struct out{ struct inside a; struct mid b; int* _Nonnull pt; }; + +struct simOut{ + struct inside a; +}; + void test1(void){ - struct inside a = (struct inside){ nullptr };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + struct inside a = (struct inside){ nullptr };// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}} } void test2(void){ - struct mid m = { { nullptr } };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + struct mid m = { { nullptr } };// expected-error {{`m.c1.p` is a _Nonnull pointer and must be properly initialized.}} } void test3(void){ - struct out o = { {nullptr},// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + struct out o = { {nullptr},// #1 - {{nullptr}},// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + {{nullptr}}, - nullptr };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + nullptr }; } +// expected-error@#1 {{`o.a.p` is a _Nonnull pointer and must be properly initialized.}} +// expected-error@#1 {{`o.b.c1.p` is a _Nonnull pointer and must be properly initialized.}} +// expected-error@#1 {{`o.pt` is a _Nonnull pointer and must be properly initialized.}} +void test4(){ + struct simOut o1={(struct inside)(struct inside){nullptr}};// expected-error {{`o1.a.p` is a _Nonnull pointer and must be properly initialized.}} + struct simOut o2={(struct inside)(struct inside){}};// expected-error {{`o2.a.p` is a _Nonnull pointer and must be properly initialized.}} +} -void test4(void){ +void test5(void){ int * _Nullable p0 = nullptr; - struct inside a = (struct inside){ p0 };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} - struct inside b = (struct inside){ foo() };// expected-error {{nonnull pointer cannot be assigned by nullable pointer}} + struct inside a = (struct inside){ p0 };// expected-error {{`a.p` is a _Nonnull pointer and must be properly initialized.}} + struct inside b = (struct inside){ foo() };// expected-error {{`b.p` is a _Nonnull pointer and must be properly initialized.}} } \ No newline at end of file diff --git a/libcbs/src/bishengc_safety/bishengc_safety.cbs b/libcbs/src/bishengc_safety/bishengc_safety.cbs index d9465fd53bc830550f099fdf485379e810ed5dce..a02b49d54204b209adffa7b4967e681942a777cd 100644 --- a/libcbs/src/bishengc_safety/bishengc_safety.cbs +++ b/libcbs/src/bishengc_safety/bishengc_safety.cbs @@ -63,7 +63,7 @@ safe void bsc_refcell_immut_borrow_failed(void) { } } -safe void safe_free(void * owned p) { +safe void safe_free(void * _Nullable owned p) { unsafe { free((void *)p); }