https://github.com/mxms0 updated https://github.com/llvm/llvm-project/pull/176466
>From 19944e88d79b1b8ebcc0bf343f251b52d8cc1868 Mon Sep 17 00:00:00 2001 From: mxms <[email protected]> Date: Fri, 16 Jan 2026 19:33:44 +0000 Subject: [PATCH 1/5] [clang][-Wunsafe-buffer-usage] Add flag support for -Wunsafe-buffer-usage-in-static-sized-arrays --- .../Analysis/Analyses/UnsafeBufferUsage.h | 5 + .../Analyses/UnsafeBufferUsageGadgets.def | 2 +- clang/include/clang/Basic/DiagnosticGroups.td | 3 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Analysis/UnsafeBufferUsage.cpp | 19 +- clang/lib/Sema/AnalysisBasedWarnings.cpp | 621 +++++++++--------- 6 files changed, 334 insertions(+), 318 deletions(-) diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index ea41eb3becfcf..462dabcc41da7 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -178,6 +178,11 @@ class UnsafeBufferUsageHandler { virtual bool ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const = 0; + /// \return true iff array subscript accesses on fixed size arrays should NOT + /// be reported at `Loc` + virtual bool + ignoreUnsafeBufferInStaticSizedArray(const SourceLocation &Loc) const = 0; + virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix = "") const = 0; diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def index f9bba5d54e9c7..129ce95c1c0e0 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -33,12 +33,12 @@ WARNING_GADGET(Increment) WARNING_GADGET(Decrement) -WARNING_GADGET(ArraySubscript) WARNING_GADGET(PointerArithmetic) WARNING_GADGET(UnsafeBufferUsageAttr) WARNING_GADGET(UnsafeBufferUsageCtorAttr) WARNING_GADGET(DataInvocation) WARNING_GADGET(UniquePtrArrayAccess) +WARNING_OPTIONAL_GADGET(ArraySubscript) WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall) WARNING_OPTIONAL_GADGET(UnsafeFormatAttributedFunctionCall) WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 3764475fbd3df..f4717505e1bdd 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1774,7 +1774,8 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">; def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">; def UnsafeBufferUsageInLibcCall : DiagGroup<"unsafe-buffer-usage-in-libc-call">; def UnsafeBufferUsageInUniquePtrArrayAccess : DiagGroup<"unsafe-buffer-usage-in-unique-ptr-array-access">; -def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess]>; +def UnsafeBufferUsageInStaticSizedArray : DiagGroup<"unsafe-buffer-usage-in-static-sized-array">; +def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer, UnsafeBufferUsageInLibcCall, UnsafeBufferUsageInUniquePtrArrayAccess, UnsafeBufferUsageInStaticSizedArray]>; // Warnings and notes InstallAPI verification. def InstallAPIViolation : DiagGroup<"installapi-violation">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index eb7a608f798b8..1ab3f537d36a3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13472,6 +13472,8 @@ def warn_unsafe_buffer_usage_in_container : Warning< InGroup<UnsafeBufferUsageInContainer>, DefaultIgnore; def warn_unsafe_buffer_usage_unique_ptr_array_access : Warning<"direct access using operator[] on std::unique_ptr<T[]> is unsafe due to lack of bounds checking">, InGroup<UnsafeBufferUsageInUniquePtrArrayAccess>, DefaultIgnore; +def warn_unsafe_buffer_usage_in_static_sized_array : Warning<"direct access on T[N] is unsafe due to the lack of bounds checking">, + InGroup<UnsafeBufferUsageInStaticSizedArray>, DefaultIgnore; #ifndef NDEBUG // Not a user-facing diagnostic. Useful for debugging false negatives in // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits). diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 6bb08102c0345..d7df7d8d46b61 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -667,7 +667,8 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, } static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, - const ASTContext &Ctx) { + const ASTContext &Ctx, + const bool IgnoreStaticSizedArrays) { // FIXME: Proper solution: // - refactor Sema::CheckArrayAccess // - split safe/OOB/unknown decision logic from diagnostics emitting code @@ -689,6 +690,12 @@ static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, return false; } + if (IgnoreStaticSizedArrays) { + // If we made it here, it means a size was found for the var being + // accessed. If it's fixed size, we can ignore it. + return true; + } + Expr::EvalResult EVResult; const Expr *IndexExpr = Node.getIdx(); if (!IndexExpr->isValueDependent() && @@ -1568,6 +1575,7 @@ class ArraySubscriptGadget : public WarningGadget { } static bool matches(const Stmt *S, const ASTContext &Ctx, + const UnsafeBufferUsageHandler *Handler, MatchResult &Result) { const auto *ASE = dyn_cast<ArraySubscriptExpr>(S); if (!ASE) @@ -1578,7 +1586,10 @@ class ArraySubscriptGadget : public WarningGadget { const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx()); bool IsSafeIndex = (Idx && Idx->getValue().isZero()) || isa<ArrayInitIndexExpr>(ASE->getIdx()); - if (IsSafeIndex || isSafeArraySubscript(*ASE, Ctx)) + if (IsSafeIndex || + isSafeArraySubscript( + *ASE, Ctx, + Handler->ignoreUnsafeBufferInStaticSizedArray(S->getBeginLoc()))) return false; Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE)); return true; @@ -2888,6 +2899,10 @@ std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) { bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override { return false; } + bool ignoreUnsafeBufferInStaticSizedArray( + const SourceLocation &Loc) const override { + return false; + } std::string getUnsafeBufferUsageAttributeTextAt( SourceLocation, StringRef WSSuffix = "") const override { return ""; diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 56d7db649afbe..437397d3126c2 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -67,61 +67,60 @@ using namespace clang; //===----------------------------------------------------------------------===// namespace { - class UnreachableCodeHandler : public reachable_code::Callback { - Sema &S; - SourceRange PreviousSilenceableCondVal; - - public: - UnreachableCodeHandler(Sema &s) : S(s) {} - - void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, - SourceRange SilenceableCondVal, SourceRange R1, - SourceRange R2, bool HasFallThroughAttr) override { - // If the diagnosed code is `[[fallthrough]];` and - // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never - // be executed` warning to avoid generating diagnostic twice - if (HasFallThroughAttr && - !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, - SourceLocation())) - return; +class UnreachableCodeHandler : public reachable_code::Callback { + Sema &S; + SourceRange PreviousSilenceableCondVal; - // Avoid reporting multiple unreachable code diagnostics that are - // triggered by the same conditional value. - if (PreviousSilenceableCondVal.isValid() && - SilenceableCondVal.isValid() && - PreviousSilenceableCondVal == SilenceableCondVal) - return; - PreviousSilenceableCondVal = SilenceableCondVal; +public: + UnreachableCodeHandler(Sema &s) : S(s) {} + + void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, + SourceRange SilenceableCondVal, SourceRange R1, + SourceRange R2, bool HasFallThroughAttr) override { + // If the diagnosed code is `[[fallthrough]];` and + // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never + // be executed` warning to avoid generating diagnostic twice + if (HasFallThroughAttr && + !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, + SourceLocation())) + return; - unsigned diag = diag::warn_unreachable; - switch (UK) { - case reachable_code::UK_Break: - diag = diag::warn_unreachable_break; - break; - case reachable_code::UK_Return: - diag = diag::warn_unreachable_return; - break; - case reachable_code::UK_Loop_Increment: - diag = diag::warn_unreachable_loop_increment; - break; - case reachable_code::UK_Other: - break; - } + // Avoid reporting multiple unreachable code diagnostics that are + // triggered by the same conditional value. + if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() && + PreviousSilenceableCondVal == SilenceableCondVal) + return; + PreviousSilenceableCondVal = SilenceableCondVal; + + unsigned diag = diag::warn_unreachable; + switch (UK) { + case reachable_code::UK_Break: + diag = diag::warn_unreachable_break; + break; + case reachable_code::UK_Return: + diag = diag::warn_unreachable_return; + break; + case reachable_code::UK_Loop_Increment: + diag = diag::warn_unreachable_loop_increment; + break; + case reachable_code::UK_Other: + break; + } - S.Diag(L, diag) << R1 << R2; + S.Diag(L, diag) << R1 << R2; - SourceLocation Open = SilenceableCondVal.getBegin(); - if (Open.isValid()) { - SourceLocation Close = SilenceableCondVal.getEnd(); - Close = S.getLocForEndOfToken(Close); - if (Close.isValid()) { - S.Diag(Open, diag::note_unreachable_silence) + SourceLocation Open = SilenceableCondVal.getBegin(); + if (Open.isValid()) { + SourceLocation Close = SilenceableCondVal.getEnd(); + Close = S.getLocForEndOfToken(Close); + if (Close.isValid()) { + S.Diag(Open, diag::note_unreachable_silence) << FixItHint::CreateInsertion(Open, "/* DISABLES CODE */ (") << FixItHint::CreateInsertion(Close, ")"); - } } } - }; + } +}; } // anonymous namespace /// CheckUnreachable - Check for unreachable code. @@ -291,7 +290,8 @@ static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD, return; CFG *cfg = AC.getCFG(); - if (!cfg) return; + if (!cfg) + return; // If the exit block is unreachable, skip processing the function. if (cfg->getExit().pred_empty()) @@ -326,10 +326,9 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock, if (Succ->getBlockID() == Body->getExit().getBlockID()) return true; - if (auto *Catch = - dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { + if (auto *Catch = dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { QualType Caught = Catch->getCaughtType(); - if (Caught.isNull() || // catch (...) catches everything + if (Caught.isNull() || // catch (...) catches everything !E->getSubExpr() || // throw; is considered cuaght by any handler S.handlerCanCatch(Caught, E->getSubExpr()->getType())) // Exception doesn't escape via this path. @@ -348,7 +347,8 @@ static void visitReachableThrows( CFG *BodyCFG, llvm::function_ref<void(const CXXThrowExpr *, CFGBlock &)> Visit) { llvm::BitVector Reachable(BodyCFG->getNumBlockIDs()); - clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), Reachable); + clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), + Reachable); for (CFGBlock *B : *BodyCFG) { if (!Reachable[B->getBlockID()]) continue; @@ -371,8 +371,8 @@ static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc, (isa<CXXDestructorDecl>(FD) || FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) { - if (const auto *Ty = FD->getTypeSourceInfo()->getType()-> - getAs<FunctionProtoType>()) + if (const auto *Ty = + FD->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>()) S.Diag(FD->getLocation(), diag::note_throw_in_dtor) << !isa<CXXDestructorDecl>(FD) << !Ty->hasExceptionSpec() << FD->getExceptionSpecSourceRange(); @@ -389,10 +389,11 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, return; if (BodyCFG->getExit().pred_empty()) return; - visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { - if (throwEscapes(S, Throw, Block, BodyCFG)) - EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); - }); + visitReachableThrows( + BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { + if (throwEscapes(S, Throw, Block, BodyCFG)) + EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); + }); } static bool isNoexcept(const FunctionDecl *FD) { @@ -565,13 +566,14 @@ enum ControlFlowKind { /// will return. static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { CFG *cfg = AC.getCFG(); - if (!cfg) return UnknownFallThrough; + if (!cfg) + return UnknownFallThrough; // The CFG leaves in dead things, and we don't want the dead code paths to // confuse us, so we mark all live things first. llvm::BitVector live(cfg->getNumBlockIDs()); - unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(), - live); + unsigned count = + reachable_code::ScanReachableFromBlock(&cfg->getEntry(), live); bool AddEHEdges = AC.getAddEHEdges(); if (!AddEHEdges && count != cfg->getNumBlockIDs()) @@ -624,7 +626,7 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { // statement (if it exists). CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend(); - for ( ; ri != re ; ++ri) + for (; ri != re; ++ri) if (ri->getAs<CFGStmt>()) break; @@ -798,14 +800,12 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, else ReturnsVoid = FD->getReturnType()->isVoidType(); HasNoReturn = FD->isNoReturn() || FD->hasAttr<InferredNoReturnAttr>(); - } - else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { ReturnsVoid = MD->getReturnType()->isVoidType(); HasNoReturn = MD->hasAttr<NoReturnAttr>(); - } - else if (isa<BlockDecl>(D)) { + } else if (isa<BlockDecl>(D)) { if (const FunctionType *FT = - BlockType->getPointeeType()->getAs<FunctionType>()) { + BlockType->getPointeeType()->getAs<FunctionType>()) { if (FT->getReturnType()->isVoidType()) ReturnsVoid = true; if (FT->getNoReturnAttr()) @@ -817,7 +817,7 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, // Short circuit for compilation speed. if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn)) - return; + return; SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc(); // cpu_dispatch functions permit empty function bodies for ICC compatibility. @@ -894,7 +894,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { typedef ConstEvaluatedExprVisitor<ContainsReference> Inherited; ContainsReference(ASTContext &Context, const DeclRefExpr *Needle) - : Inherited(Context), FoundReference(false), Needle(Needle) {} + : Inherited(Context), FoundReference(false), Needle(Needle) {} void VisitExpr(const Expr *E) { // Stop evaluating if we already have a reference. @@ -917,8 +917,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { QualType VariableTy = VD->getType().getCanonicalType(); - if (VariableTy->isBlockPointerType() && - !VD->hasAttr<BlocksAttr>()) { + if (VariableTy->isBlockPointerType() && !VD->hasAttr<BlocksAttr>()) { S.Diag(VD->getLocation(), diag::note_block_var_fixit_add_initialization) << VD->getDeclName() << FixItHint::CreateInsertion(VD->getLocation(), "__block "); @@ -940,16 +939,16 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { if (Init.empty()) return false; - S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() - << FixItHint::CreateInsertion(Loc, Init); + S.Diag(Loc, diag::note_var_fixit_add_initialization) + << VD->getDeclName() << FixItHint::CreateInsertion(Loc, Init); return true; } /// Create a fixit to remove an if-like statement, on the assumption that its /// condition is CondVal. static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then, - const Stmt *Else, bool CondVal, - FixItHint &Fixit1, FixItHint &Fixit2) { + const Stmt *Else, bool CondVal, FixItHint &Fixit1, + FixItHint &Fixit2) { if (CondVal) { // If condition is always true, remove all but the 'then'. Fixit1 = FixItHint::CreateRemoval( @@ -1016,9 +1015,9 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, // For all binary terminators, branch 0 is taken if the condition is true, // and branch 1 is taken if the condition is false. int RemoveDiagKind = -1; - const char *FixitStr = - S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false") - : (I->Output ? "1" : "0"); + const char *FixitStr = S.getLangOpts().CPlusPlus + ? (I->Output ? "true" : "false") + : (I->Output ? "1" : "0"); FixItHint Fixit1, Fixit2; switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) { @@ -1034,8 +1033,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "if"; Range = IS->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), - I->Output, Fixit1, Fixit2); + CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), I->Output, Fixit1, + Fixit2); break; } case Stmt::ConditionalOperatorClass: { @@ -1044,8 +1043,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "?:"; Range = CO->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), - I->Output, Fixit1, Fixit2); + CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), I->Output, + Fixit1, Fixit2); break; } case Stmt::BinaryOperatorClass: { @@ -1120,13 +1119,13 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, } S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var) - << VD->getDeclName() << IsCapturedByBlock << DiagKind - << Str << I->Output << Range; + << VD->getDeclName() << IsCapturedByBlock << DiagKind << Str + << I->Output << Range; S.Diag(User->getBeginLoc(), diag::note_uninit_var_use) << IsCapturedByBlock << User->getSourceRange(); if (RemoveDiagKind != -1) S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond) - << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; + << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; Diagnosed = true; } @@ -1296,32 +1295,32 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { // Don't care about other unreachable statements. } } - // If there are no unreachable statements, this may be a special - // case in CFG: - // case X: { - // A a; // A has a destructor. - // break; - // } - // // <<<< This place is represented by a 'hanging' CFG block. - // case Y: - continue; + // If there are no unreachable statements, this may be a special + // case in CFG: + // case X: { + // A a; // A has a destructor. + // break; + // } + // // <<<< This place is represented by a 'hanging' CFG block. + // case Y: + continue; } - const Stmt *LastStmt = getLastStmt(*P); - if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { - markFallthroughVisited(AS); - ++AnnotatedCnt; - continue; // Fallthrough annotation, good. - } + const Stmt *LastStmt = getLastStmt(*P); + if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { + markFallthroughVisited(AS); + ++AnnotatedCnt; + continue; // Fallthrough annotation, good. + } - if (!LastStmt) { // This block contains no executable statements. - // Traverse its predecessors. - std::copy(P->pred_begin(), P->pred_end(), - std::back_inserter(BlockQueue)); - continue; - } + if (!LastStmt) { // This block contains no executable statements. + // Traverse its predecessors. + std::copy(P->pred_begin(), P->pred_end(), + std::back_inserter(BlockQueue)); + continue; + } - ++UnannotatedCnt; + ++UnannotatedCnt; } return !!UnannotatedCnt; } @@ -1337,64 +1336,63 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { return true; } - // We don't want to traverse local type declarations. We analyze their - // methods separately. - bool TraverseDecl(Decl *D) override { return true; } - - // We analyze lambda bodies separately. Skip them here. - bool TraverseLambdaExpr(LambdaExpr *LE) override { - // Traverse the captures, but not the body. - for (const auto C : zip(LE->captures(), LE->capture_inits())) - TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); - return true; - } + // We don't want to traverse local type declarations. We analyze their + // methods separately. + bool TraverseDecl(Decl *D) override { return true; } - private: + // We analyze lambda bodies separately. Skip them here. + bool TraverseLambdaExpr(LambdaExpr *LE) override { + // Traverse the captures, but not the body. + for (const auto C : zip(LE->captures(), LE->capture_inits())) + TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); + return true; + } - static const AttributedStmt *asFallThroughAttr(const Stmt *S) { - if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { - if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) - return AS; - } - return nullptr; +private: + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { + if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) + return AS; } + return nullptr; + } - static const Stmt *getLastStmt(const CFGBlock &B) { - if (const Stmt *Term = B.getTerminatorStmt()) - return Term; - for (const CFGElement &Elem : llvm::reverse(B)) - if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) - return CS->getStmt(); - // Workaround to detect a statement thrown out by CFGBuilder: - // case X: {} case Y: - // case X: ; case Y: - if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) - if (!isa<SwitchCase>(SW->getSubStmt())) - return SW->getSubStmt(); + static const Stmt *getLastStmt(const CFGBlock &B) { + if (const Stmt *Term = B.getTerminatorStmt()) + return Term; + for (const CFGElement &Elem : llvm::reverse(B)) + if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) + return CS->getStmt(); + // Workaround to detect a statement thrown out by CFGBuilder: + // case X: {} case Y: + // case X: ; case Y: + if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) + if (!isa<SwitchCase>(SW->getSubStmt())) + return SW->getSubStmt(); - return nullptr; - } + return nullptr; + } - bool FoundSwitchStatements; - AttrStmts FallthroughStmts; - Sema &S; - llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; }; } // anonymous namespace static StringRef getFallthroughAttrSpelling(Preprocessor &PP, SourceLocation Loc) { - TokenValue FallthroughTokens[] = { - tok::l_square, tok::l_square, - PP.getIdentifierInfo("fallthrough"), - tok::r_square, tok::r_square - }; - - TokenValue ClangFallthroughTokens[] = { - tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"), - tok::coloncolon, PP.getIdentifierInfo("fallthrough"), - tok::r_square, tok::r_square - }; + TokenValue FallthroughTokens[] = {tok::l_square, tok::l_square, + PP.getIdentifierInfo("fallthrough"), + tok::r_square, tok::r_square}; + + TokenValue ClangFallthroughTokens[] = {tok::l_square, + tok::l_square, + PP.getIdentifierInfo("clang"), + tok::coloncolon, + PP.getIdentifierInfo("fallthrough"), + tok::r_square, + tok::r_square}; bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17 && !PP.getLangOpts().C23; @@ -1509,13 +1507,12 @@ static bool isInLoop(const ASTContext &Ctx, const ParentMap &PM, static void diagnoseRepeatedUseOfWeak(Sema &S, const sema::FunctionScopeInfo *CurFn, - const Decl *D, - const ParentMap &PM) { + const Decl *D, const ParentMap &PM) { typedef sema::FunctionScopeInfo::WeakObjectProfileTy WeakObjectProfileTy; typedef sema::FunctionScopeInfo::WeakObjectUseMap WeakObjectUseMap; typedef sema::FunctionScopeInfo::WeakUseVector WeakUseVector; typedef std::pair<const Stmt *, WeakObjectUseMap::const_iterator> - StmtUsesPair; + StmtUsesPair; ASTContext &Ctx = S.getASTContext(); @@ -1529,7 +1526,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Find the first read of the weak object. WeakUseVector::const_iterator UI = Uses.begin(), UE = Uses.end(); - for ( ; UI != UE; ++UI) { + for (; UI != UE; ++UI) { if (UI->isUnsafe()) break; } @@ -1586,12 +1583,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. // FIXME: Should we use a common classification enum and the same set of // possibilities all throughout Sema? - enum { - Function, - Method, - Block, - Lambda - } FunctionKind; + enum { Function, Method, Block, Lambda } FunctionKind; if (isa<sema::BlockScopeInfo>(CurFn)) FunctionKind = Block; @@ -1622,12 +1614,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Classify the weak object being accessed for better warning text. // This enum should stay in sync with the cases in // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. - enum { - Variable, - Property, - ImplicitProperty, - Ivar - } ObjectKind; + enum { Variable, Property, ImplicitProperty, Ivar } ObjectKind; const NamedDecl *KeyProp = Key.getProperty(); if (isa<VarDecl>(KeyProp)) @@ -1729,7 +1716,7 @@ class UninitValsDiagReporter : public UninitVariablesHandler { } private: - static bool hasAlwaysUninitializedUse(const UsesVec* vec) { + static bool hasAlwaysUninitializedUse(const UsesVec *vec) { return llvm::any_of(*vec, [](const UninitUse &U) { return U.getKind() == UninitUse::Always || U.getKind() == UninitUse::AfterCall || @@ -1979,10 +1966,10 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { : getNotes(); } - public: +public: ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL) - : S(S), FunLocation(FL), FunEndLocation(FEL), - CurrentFunction(nullptr), Verbose(false) {} + : S(S), FunLocation(FL), FunEndLocation(FEL), CurrentFunction(nullptr), + Verbose(false) {} void setVerbose(bool b) { Verbose = b; } @@ -2077,18 +2064,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { bool ReentrancyMismatch) override { unsigned DiagID = 0; switch (LEK) { - case LEK_LockedSomePredecessors: - DiagID = diag::warn_lock_some_predecessors; - break; - case LEK_LockedSomeLoopIterations: - DiagID = diag::warn_expecting_lock_held_on_loop; - break; - case LEK_LockedAtEndOfFunction: - DiagID = diag::warn_no_unlock; - break; - case LEK_NotLockedAtEndOfFunction: - DiagID = diag::warn_expecting_locked; - break; + case LEK_LockedSomePredecessors: + DiagID = diag::warn_lock_some_predecessors; + break; + case LEK_LockedSomeLoopIterations: + DiagID = diag::warn_expecting_lock_held_on_loop; + break; + case LEK_LockedAtEndOfFunction: + DiagID = diag::warn_no_unlock; + break; + case LEK_NotLockedAtEndOfFunction: + DiagID = diag::warn_expecting_locked; + break; } if (LocEndOfScope.isInvalid()) LocEndOfScope = FunEndLocation; @@ -2134,7 +2121,7 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { break; } PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) - << D << getLockKindFromAccessKind(AK)); + << D << getLockKindFromAccessKind(AK)); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2145,43 +2132,42 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { unsigned DiagID = 0; if (PossibleMatch) { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock_precise; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock_precise; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock_precise; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock_precise; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock_precise; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock_precise; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind - << D - << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << Kind << D << LockName << LK); PartialDiagnosticAt Note(Loc, S.PDiag(diag::note_found_mutex_near_match) << *PossibleMatch); if (Verbose && POK == POK_VarAccess) { @@ -2193,43 +2179,42 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { Warnings.emplace_back(std::move(Warning), getNotes(Note)); } else { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind - << D - << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) + << Kind << D << LockName << LK); if (Verbose && POK == POK_VarAccess) { PartialDiagnosticAt Note(D->getLocation(), S.PDiag(diag::note_guarded_by_declared_here)); @@ -2241,9 +2226,9 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquire_requires_negative_cap) - << Kind << LockName << Neg); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_acquire_requires_negative_cap) + << Kind << LockName << Neg); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2263,22 +2248,20 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleLockAcquiredBefore(StringRef Kind, Name L1Name, Name L2Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before) << Kind << L1Name << L2Name); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_acquired_before) + << Kind << L1Name << L2Name); Warnings.emplace_back(std::move(Warning), getNotes()); } void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); Warnings.emplace_back(std::move(Warning), getNotes()); } - void enterFunction(const FunctionDecl* FD) override { - CurrentFunction = FD; - } + void enterFunction(const FunctionDecl *FD) override { CurrentFunction = FD; } - void leaveFunction(const FunctionDecl* FD) override { + void leaveFunction(const FunctionDecl *FD) override { CurrentFunction = nullptr; } }; @@ -2299,7 +2282,6 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { DiagList Warnings; public: - ConsumedWarningsHandler(Sema &S) : S(S) {} void emitDiagnostics() override { @@ -2313,8 +2295,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnLoopStateMismatch(SourceLocation Loc, StringRef VariableName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) << - VariableName); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) + << VariableName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2324,9 +2306,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_param_return_typestate_mismatch) << VariableName << - ExpectedState << ObservedState); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_param_return_typestate_mismatch) + << VariableName << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2334,16 +2316,18 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_param_typestate_mismatch) + << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } void warnReturnTypestateForUnconsumableType(SourceLocation Loc, StringRef TypeName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_return_typestate_for_unconsumable_type) << TypeName); + PartialDiagnosticAt Warning( + Loc, S.PDiag(diag::warn_return_typestate_for_unconsumable_type) + << TypeName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2351,8 +2335,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_return_typestate_mismatch) + << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2360,8 +2345,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_use_of_temp_in_invalid_state) << MethodName << State); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_use_of_temp_in_invalid_state) + << MethodName << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2369,8 +2355,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) << - MethodName << VariableName << State); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) + << MethodName << VariableName + << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2386,7 +2373,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { namespace { class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { Sema &S; - bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? + bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? // Lists as a string the names of variables in `VarGroupForVD` except for `VD` // itself: @@ -2425,7 +2412,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { public: UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions) - : S(S), SuggestSuggestions(SuggestSuggestions) {} + : S(S), SuggestSuggestions(SuggestSuggestions) {} void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) override { @@ -2602,7 +2589,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { #ifndef NDEBUG if (areDebugNotesRequested()) - for (const DebugNote &Note: DebugNotesByVar[Variable]) + for (const DebugNote &Note : DebugNotesByVar[Variable]) S.Diag(Note.first, diag::note_safe_buffer_debug_mode) << Note.second; #endif } @@ -2629,6 +2616,12 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { return S.Diags.isIgnored(diag::warn_unsafe_buffer_libc_call, Loc); } + bool ignoreUnsafeBufferInStaticSizedArray( + const SourceLocation &Loc) const override { + return S.Diags.isIgnored( + diag::warn_unsafe_buffer_usage_in_static_sized_array, Loc); + } + // Returns the text representation of clang::unsafe_buffer_usage attribute. // `WSSuffix` holds customized "white-space"s, e.g., newline or whilespace // characters. @@ -2698,8 +2691,7 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) MaxCFGBlocksPerFunction(0), NumUninitAnalysisFunctions(0), NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0), NumUninitAnalysisBlockVisits(0), - MaxUninitAnalysisBlockVisitsPerFunction(0) { -} + MaxUninitAnalysisBlockVisitsPerFunction(0) {} // We need this here for unique_ptr with forward declared class. sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default; @@ -2943,7 +2935,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, } void clang::sema::AnalysisBasedWarnings::IssueWarnings( - TranslationUnitDecl *TU) { + TranslationUnitDecl *TU) { if (!TU) return; // This is unexpected, give up quietly. @@ -2957,7 +2949,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // UnsafeBufferUsage analysis settings. bool UnsafeBufferUsageCanEmitSuggestions = S.getLangOpts().CPlusPlus20; - bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. + bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. UnsafeBufferUsageCanEmitSuggestions && DiagOpts.ShowSafeBufferUsageSuggestions; bool UnsafeBufferUsageShouldSuggestSuggestions = @@ -3065,13 +3057,13 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().setAllAlwaysAdd(); } else { AC.getCFGBuildOptions() - .setAlwaysAdd(Stmt::BinaryOperatorClass) - .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) - .setAlwaysAdd(Stmt::BlockExprClass) - .setAlwaysAdd(Stmt::CStyleCastExprClass) - .setAlwaysAdd(Stmt::DeclRefExprClass) - .setAlwaysAdd(Stmt::ImplicitCastExprClass) - .setAlwaysAdd(Stmt::UnaryOperatorClass); + .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) + .setAlwaysAdd(Stmt::BlockExprClass) + .setAlwaysAdd(Stmt::CStyleCastExprClass) + .setAlwaysAdd(Stmt::DeclRefExprClass) + .setAlwaysAdd(Stmt::ImplicitCastExprClass) + .setAlwaysAdd(Stmt::UnaryOperatorClass); } if (EnableLifetimeSafetyAnalysis) { AC.getCFGBuildOptions().AddLifetime = true; @@ -3153,12 +3145,10 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( ++NumUninitAnalysisFunctions; NumUninitAnalysisVariables += stats.NumVariablesAnalyzed; NumUninitAnalysisBlockVisits += stats.NumBlockVisits; - MaxUninitAnalysisVariablesPerFunction = - std::max(MaxUninitAnalysisVariablesPerFunction, - stats.NumVariablesAnalyzed); - MaxUninitAnalysisBlockVisitsPerFunction = - std::max(MaxUninitAnalysisBlockVisitsPerFunction, - stats.NumBlockVisits); + MaxUninitAnalysisVariablesPerFunction = std::max( + MaxUninitAnalysisVariablesPerFunction, stats.NumVariablesAnalyzed); + MaxUninitAnalysisBlockVisitsPerFunction = std::max( + MaxUninitAnalysisBlockVisitsPerFunction, stats.NumBlockVisits); } } } @@ -3196,7 +3186,6 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, D->getBeginLoc())) diagnoseRepeatedUseOfWeak(S, fscope, D, AC.getParentMap()); - // Check for infinite self-recursion in functions if (!Diags.isIgnored(diag::warn_infinite_recursive_function, D->getBeginLoc())) { @@ -3227,8 +3216,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // If we successfully built a CFG for this context, record some more // detail information about it. NumCFGBlocks += cfg->getNumBlockIDs(); - MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction, - cfg->getNumBlockIDs()); + MaxCFGBlocksPerFunction = + std::max(MaxCFGBlocksPerFunction, cfg->getNumBlockIDs()); } else { ++NumFunctionsWithBadCFGs; } @@ -3240,7 +3229,7 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs; unsigned AvgCFGBlocksPerFunction = - !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt; + !NumCFGsBuilt ? 0 : NumCFGBlocks / NumCFGsBuilt; llvm::errs() << NumFunctionsAnalyzed << " functions analyzed (" << NumFunctionsWithBadCFGs << " w/o CFGs).\n" << " " << NumCFGBlocks << " CFG blocks built.\n" @@ -3249,10 +3238,14 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { << " " << MaxCFGBlocksPerFunction << " max CFG blocks per function.\n"; - unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0 - : NumUninitAnalysisVariables/NumUninitAnalysisFunctions; - unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0 - : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions; + unsigned AvgUninitVariablesPerFunction = + !NumUninitAnalysisFunctions + ? 0 + : NumUninitAnalysisVariables / NumUninitAnalysisFunctions; + unsigned AvgUninitBlockVisitsPerFunction = + !NumUninitAnalysisFunctions + ? 0 + : NumUninitAnalysisBlockVisits / NumUninitAnalysisFunctions; llvm::errs() << NumUninitAnalysisFunctions << " functions analyzed for uninitialiazed variables\n" << " " << NumUninitAnalysisVariables << " variables analyzed.\n" >From 58f9857e0c70f3575884a6f7f3a92f46f51131ba Mon Sep 17 00:00:00 2001 From: mxms <[email protected]> Date: Fri, 16 Jan 2026 19:33:44 +0000 Subject: [PATCH 2/5] [clang][-Wunsafe-buffer-usage] Add flag support for -Wunsafe-buffer-usage-in-static-sized-arrays --- ...afe-buffer-usage-in-static-sized-array.cpp | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp new file mode 100644 index 0000000000000..c4813198bbd9a --- /dev/null +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-in-static-sized-array.cpp @@ -0,0 +1,159 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-everything -Wunsafe-buffer-usage \ +// RUN: -Wno-unsafe-buffer-usage-in-static-sized-array \ +// RUN: -fsafe-buffer-usage-suggestions \ +// RUN: -verify %s + +// CHECK-NOT: [-Wunsafe-buffer-usage] +// expected-no-diagnostics + +void foo(unsigned idx) { + int buffer[10]; + buffer[idx] = 0; +} + +int global_buffer[10]; +void foo2(unsigned idx) { global_buffer[idx] = 0; } + +struct Foo { + int member_buffer[10]; + int x; +}; + +void foo2(Foo &f, unsigned idx) { f.member_buffer[idx] = 0; } + +void constant_idx_safe(unsigned idx) { + int buffer[10]; + buffer[9] = 0; +} + +void constant_idx_safe0(unsigned idx) { + int buffer[10]; + buffer[0] = 0; +} + +int array[10]; + +void circular_access_unsigned(unsigned idx) { + array[idx % 10]; + array[idx % 11]; + array[(idx + 3) % 10]; + array[(--idx) % 8]; + array[idx & 9 % 10]; + array[9 & idx % 11]; + array[12 % 10]; +} + +void circular_access_signed(int idx) { array[idx % 10]; } + +void masked_idx1(unsigned long long idx, Foo f) { + // Bitwise and operation + array[idx & 5] = 10; + array[5 & idx] = 12; + array[idx & 11 & 5] = 3; + array[idx & 11] = 20; + array[idx &= 5]; + array[f.x & 5]; + array[5 & f.x]; + array[f.x & (-5)]; +} + +typedef unsigned long long uint64_t; +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; + +void type_conversions(uint64_t idx1, uint32_t idx2, uint8_t idx3) { + array[(uint32_t)idx1 & 3]; + array[idx2 & 3]; + array[idx3 & 3]; +} + +int array2[5]; + +void masked_idx_safe(unsigned long long idx) { + array2[6 & 5]; + array2[6 & idx & (idx + 1) & 5]; +} + +void constant_idx_unsafe(unsigned idx) { + int buffer[10]; + buffer[10] = 0; +} + +void constant_id_string(unsigned idx) { + char safe_char = "abc"[1]; + safe_char = ""[0]; + safe_char = "\0"[0]; + + char abcd[5] = "abc"; + abcd[2]; + + char unsafe_char = "abc"[3]; + unsafe_char = "abc"[-1]; + unsafe_char = ""[1]; + unsafe_char = ""[idx]; +} + +typedef float Float4x4[4][4]; + +float two_dimension_array(Float4x4 &matrix, unsigned idx) { + float a = matrix[0][4]; + + a = matrix[0][3]; + + a = matrix[4][0]; + + a = matrix[idx][0]; + + a = matrix[0][idx]; + + a = matrix[idx][idx]; + + return matrix[1][1]; +} + +typedef float Float2x3x4[2][3][4]; +float multi_dimension_array(Float2x3x4 &matrix) { + float *f = matrix[0][2]; + return matrix[1][2][3]; +} + +char array_strings[][11] = {"Apple", "Banana", "Cherry", "Date", "Elderberry"}; + +char array_string[] = "123456"; + +char access_strings() { + char c = array_strings[0][4]; + c = array_strings[3][10]; + c = array_string[5]; + return c; +} + +struct T { + int array[10]; +}; + +const int index = 1; + +constexpr int get_const(int x) { + if (x < 3) + return ++x; + else + return x + 5; +}; + +void array_indexed_const_expr(unsigned idx) { + int arr[10]; + arr[sizeof(int)] = 5; + + int array[sizeof(T)]; + array[sizeof(int)] = 5; + array[sizeof(T) - 1] = 3; + + int k = arr[6 & 5]; + k = arr[2 << index]; + k = arr[8 << index]; + k = arr[16 >> 1]; + k = arr[get_const(index)]; + k = arr[get_const(5)]; + k = arr[get_const(4)]; +} >From a0f4e48efe50af38c533701ea28ec59615d4d098 Mon Sep 17 00:00:00 2001 From: mxms <[email protected]> Date: Fri, 16 Jan 2026 19:33:44 +0000 Subject: [PATCH 3/5] [clang][-Wunsafe-buffer-usage] Add flag support for -Wunsafe-buffer-usage-in-static-sized-arrays --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 621 ++++++++++++----------- 1 file changed, 314 insertions(+), 307 deletions(-) diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 437397d3126c2..56d7db649afbe 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -67,60 +67,61 @@ using namespace clang; //===----------------------------------------------------------------------===// namespace { -class UnreachableCodeHandler : public reachable_code::Callback { - Sema &S; - SourceRange PreviousSilenceableCondVal; - -public: - UnreachableCodeHandler(Sema &s) : S(s) {} - - void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, - SourceRange SilenceableCondVal, SourceRange R1, - SourceRange R2, bool HasFallThroughAttr) override { - // If the diagnosed code is `[[fallthrough]];` and - // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never - // be executed` warning to avoid generating diagnostic twice - if (HasFallThroughAttr && - !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, - SourceLocation())) - return; + class UnreachableCodeHandler : public reachable_code::Callback { + Sema &S; + SourceRange PreviousSilenceableCondVal; + + public: + UnreachableCodeHandler(Sema &s) : S(s) {} + + void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L, + SourceRange SilenceableCondVal, SourceRange R1, + SourceRange R2, bool HasFallThroughAttr) override { + // If the diagnosed code is `[[fallthrough]];` and + // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never + // be executed` warning to avoid generating diagnostic twice + if (HasFallThroughAttr && + !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr, + SourceLocation())) + return; - // Avoid reporting multiple unreachable code diagnostics that are - // triggered by the same conditional value. - if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() && - PreviousSilenceableCondVal == SilenceableCondVal) - return; - PreviousSilenceableCondVal = SilenceableCondVal; + // Avoid reporting multiple unreachable code diagnostics that are + // triggered by the same conditional value. + if (PreviousSilenceableCondVal.isValid() && + SilenceableCondVal.isValid() && + PreviousSilenceableCondVal == SilenceableCondVal) + return; + PreviousSilenceableCondVal = SilenceableCondVal; - unsigned diag = diag::warn_unreachable; - switch (UK) { - case reachable_code::UK_Break: - diag = diag::warn_unreachable_break; - break; - case reachable_code::UK_Return: - diag = diag::warn_unreachable_return; - break; - case reachable_code::UK_Loop_Increment: - diag = diag::warn_unreachable_loop_increment; - break; - case reachable_code::UK_Other: - break; - } + unsigned diag = diag::warn_unreachable; + switch (UK) { + case reachable_code::UK_Break: + diag = diag::warn_unreachable_break; + break; + case reachable_code::UK_Return: + diag = diag::warn_unreachable_return; + break; + case reachable_code::UK_Loop_Increment: + diag = diag::warn_unreachable_loop_increment; + break; + case reachable_code::UK_Other: + break; + } - S.Diag(L, diag) << R1 << R2; + S.Diag(L, diag) << R1 << R2; - SourceLocation Open = SilenceableCondVal.getBegin(); - if (Open.isValid()) { - SourceLocation Close = SilenceableCondVal.getEnd(); - Close = S.getLocForEndOfToken(Close); - if (Close.isValid()) { - S.Diag(Open, diag::note_unreachable_silence) + SourceLocation Open = SilenceableCondVal.getBegin(); + if (Open.isValid()) { + SourceLocation Close = SilenceableCondVal.getEnd(); + Close = S.getLocForEndOfToken(Close); + if (Close.isValid()) { + S.Diag(Open, diag::note_unreachable_silence) << FixItHint::CreateInsertion(Open, "/* DISABLES CODE */ (") << FixItHint::CreateInsertion(Close, ")"); + } } } - } -}; + }; } // anonymous namespace /// CheckUnreachable - Check for unreachable code. @@ -290,8 +291,7 @@ static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD, return; CFG *cfg = AC.getCFG(); - if (!cfg) - return; + if (!cfg) return; // If the exit block is unreachable, skip processing the function. if (cfg->getExit().pred_empty()) @@ -326,9 +326,10 @@ static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock, if (Succ->getBlockID() == Body->getExit().getBlockID()) return true; - if (auto *Catch = dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { + if (auto *Catch = + dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) { QualType Caught = Catch->getCaughtType(); - if (Caught.isNull() || // catch (...) catches everything + if (Caught.isNull() || // catch (...) catches everything !E->getSubExpr() || // throw; is considered cuaght by any handler S.handlerCanCatch(Caught, E->getSubExpr()->getType())) // Exception doesn't escape via this path. @@ -347,8 +348,7 @@ static void visitReachableThrows( CFG *BodyCFG, llvm::function_ref<void(const CXXThrowExpr *, CFGBlock &)> Visit) { llvm::BitVector Reachable(BodyCFG->getNumBlockIDs()); - clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), - Reachable); + clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), Reachable); for (CFGBlock *B : *BodyCFG) { if (!Reachable[B->getBlockID()]) continue; @@ -371,8 +371,8 @@ static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc, (isa<CXXDestructorDecl>(FD) || FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) { - if (const auto *Ty = - FD->getTypeSourceInfo()->getType()->getAs<FunctionProtoType>()) + if (const auto *Ty = FD->getTypeSourceInfo()->getType()-> + getAs<FunctionProtoType>()) S.Diag(FD->getLocation(), diag::note_throw_in_dtor) << !isa<CXXDestructorDecl>(FD) << !Ty->hasExceptionSpec() << FD->getExceptionSpecSourceRange(); @@ -389,11 +389,10 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, return; if (BodyCFG->getExit().pred_empty()) return; - visitReachableThrows( - BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { - if (throwEscapes(S, Throw, Block, BodyCFG)) - EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); - }); + visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) { + if (throwEscapes(S, Throw, Block, BodyCFG)) + EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD); + }); } static bool isNoexcept(const FunctionDecl *FD) { @@ -566,14 +565,13 @@ enum ControlFlowKind { /// will return. static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { CFG *cfg = AC.getCFG(); - if (!cfg) - return UnknownFallThrough; + if (!cfg) return UnknownFallThrough; // The CFG leaves in dead things, and we don't want the dead code paths to // confuse us, so we mark all live things first. llvm::BitVector live(cfg->getNumBlockIDs()); - unsigned count = - reachable_code::ScanReachableFromBlock(&cfg->getEntry(), live); + unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(), + live); bool AddEHEdges = AC.getAddEHEdges(); if (!AddEHEdges && count != cfg->getNumBlockIDs()) @@ -626,7 +624,7 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) { // statement (if it exists). CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend(); - for (; ri != re; ++ri) + for ( ; ri != re ; ++ri) if (ri->getAs<CFGStmt>()) break; @@ -800,12 +798,14 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, else ReturnsVoid = FD->getReturnType()->isVoidType(); HasNoReturn = FD->isNoReturn() || FD->hasAttr<InferredNoReturnAttr>(); - } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + } + else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { ReturnsVoid = MD->getReturnType()->isVoidType(); HasNoReturn = MD->hasAttr<NoReturnAttr>(); - } else if (isa<BlockDecl>(D)) { + } + else if (isa<BlockDecl>(D)) { if (const FunctionType *FT = - BlockType->getPointeeType()->getAs<FunctionType>()) { + BlockType->getPointeeType()->getAs<FunctionType>()) { if (FT->getReturnType()->isVoidType()) ReturnsVoid = true; if (FT->getNoReturnAttr()) @@ -817,7 +817,7 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, // Short circuit for compilation speed. if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn)) - return; + return; SourceLocation LBrace = Body->getBeginLoc(), RBrace = Body->getEndLoc(); // cpu_dispatch functions permit empty function bodies for ICC compatibility. @@ -894,7 +894,7 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { typedef ConstEvaluatedExprVisitor<ContainsReference> Inherited; ContainsReference(ASTContext &Context, const DeclRefExpr *Needle) - : Inherited(Context), FoundReference(false), Needle(Needle) {} + : Inherited(Context), FoundReference(false), Needle(Needle) {} void VisitExpr(const Expr *E) { // Stop evaluating if we already have a reference. @@ -917,7 +917,8 @@ class ContainsReference : public ConstEvaluatedExprVisitor<ContainsReference> { static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { QualType VariableTy = VD->getType().getCanonicalType(); - if (VariableTy->isBlockPointerType() && !VD->hasAttr<BlocksAttr>()) { + if (VariableTy->isBlockPointerType() && + !VD->hasAttr<BlocksAttr>()) { S.Diag(VD->getLocation(), diag::note_block_var_fixit_add_initialization) << VD->getDeclName() << FixItHint::CreateInsertion(VD->getLocation(), "__block "); @@ -939,16 +940,16 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { if (Init.empty()) return false; - S.Diag(Loc, diag::note_var_fixit_add_initialization) - << VD->getDeclName() << FixItHint::CreateInsertion(Loc, Init); + S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName() + << FixItHint::CreateInsertion(Loc, Init); return true; } /// Create a fixit to remove an if-like statement, on the assumption that its /// condition is CondVal. static void CreateIfFixit(Sema &S, const Stmt *If, const Stmt *Then, - const Stmt *Else, bool CondVal, FixItHint &Fixit1, - FixItHint &Fixit2) { + const Stmt *Else, bool CondVal, + FixItHint &Fixit1, FixItHint &Fixit2) { if (CondVal) { // If condition is always true, remove all but the 'then'. Fixit1 = FixItHint::CreateRemoval( @@ -1015,9 +1016,9 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, // For all binary terminators, branch 0 is taken if the condition is true, // and branch 1 is taken if the condition is false. int RemoveDiagKind = -1; - const char *FixitStr = S.getLangOpts().CPlusPlus - ? (I->Output ? "true" : "false") - : (I->Output ? "1" : "0"); + const char *FixitStr = + S.getLangOpts().CPlusPlus ? (I->Output ? "true" : "false") + : (I->Output ? "1" : "0"); FixItHint Fixit1, Fixit2; switch (Term ? Term->getStmtClass() : Stmt::DeclStmtClass) { @@ -1033,8 +1034,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "if"; Range = IS->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), I->Output, Fixit1, - Fixit2); + CreateIfFixit(S, IS, IS->getThen(), IS->getElse(), + I->Output, Fixit1, Fixit2); break; } case Stmt::ConditionalOperatorClass: { @@ -1043,8 +1044,8 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, Str = "?:"; Range = CO->getCond()->getSourceRange(); RemoveDiagKind = 0; - CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), I->Output, - Fixit1, Fixit2); + CreateIfFixit(S, CO, CO->getTrueExpr(), CO->getFalseExpr(), + I->Output, Fixit1, Fixit2); break; } case Stmt::BinaryOperatorClass: { @@ -1119,13 +1120,13 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use, } S.Diag(Range.getBegin(), diag::warn_sometimes_uninit_var) - << VD->getDeclName() << IsCapturedByBlock << DiagKind << Str - << I->Output << Range; + << VD->getDeclName() << IsCapturedByBlock << DiagKind + << Str << I->Output << Range; S.Diag(User->getBeginLoc(), diag::note_uninit_var_use) << IsCapturedByBlock << User->getSourceRange(); if (RemoveDiagKind != -1) S.Diag(Fixit1.RemoveRange.getBegin(), diag::note_uninit_fixit_remove_cond) - << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; + << RemoveDiagKind << Str << I->Output << Fixit1 << Fixit2; Diagnosed = true; } @@ -1295,32 +1296,32 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { // Don't care about other unreachable statements. } } - // If there are no unreachable statements, this may be a special - // case in CFG: - // case X: { - // A a; // A has a destructor. - // break; - // } - // // <<<< This place is represented by a 'hanging' CFG block. - // case Y: - continue; + // If there are no unreachable statements, this may be a special + // case in CFG: + // case X: { + // A a; // A has a destructor. + // break; + // } + // // <<<< This place is represented by a 'hanging' CFG block. + // case Y: + continue; } - const Stmt *LastStmt = getLastStmt(*P); - if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { - markFallthroughVisited(AS); - ++AnnotatedCnt; - continue; // Fallthrough annotation, good. - } + const Stmt *LastStmt = getLastStmt(*P); + if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) { + markFallthroughVisited(AS); + ++AnnotatedCnt; + continue; // Fallthrough annotation, good. + } - if (!LastStmt) { // This block contains no executable statements. - // Traverse its predecessors. - std::copy(P->pred_begin(), P->pred_end(), - std::back_inserter(BlockQueue)); - continue; - } + if (!LastStmt) { // This block contains no executable statements. + // Traverse its predecessors. + std::copy(P->pred_begin(), P->pred_end(), + std::back_inserter(BlockQueue)); + continue; + } - ++UnannotatedCnt; + ++UnannotatedCnt; } return !!UnannotatedCnt; } @@ -1336,63 +1337,64 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor { return true; } - // We don't want to traverse local type declarations. We analyze their - // methods separately. - bool TraverseDecl(Decl *D) override { return true; } + // We don't want to traverse local type declarations. We analyze their + // methods separately. + bool TraverseDecl(Decl *D) override { return true; } - // We analyze lambda bodies separately. Skip them here. - bool TraverseLambdaExpr(LambdaExpr *LE) override { - // Traverse the captures, but not the body. - for (const auto C : zip(LE->captures(), LE->capture_inits())) - TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); - return true; - } + // We analyze lambda bodies separately. Skip them here. + bool TraverseLambdaExpr(LambdaExpr *LE) override { + // Traverse the captures, but not the body. + for (const auto C : zip(LE->captures(), LE->capture_inits())) + TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C)); + return true; + } -private: - static const AttributedStmt *asFallThroughAttr(const Stmt *S) { - if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { - if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) - return AS; + private: + + static const AttributedStmt *asFallThroughAttr(const Stmt *S) { + if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) { + if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs())) + return AS; + } + return nullptr; } - return nullptr; - } - static const Stmt *getLastStmt(const CFGBlock &B) { - if (const Stmt *Term = B.getTerminatorStmt()) - return Term; - for (const CFGElement &Elem : llvm::reverse(B)) - if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) - return CS->getStmt(); - // Workaround to detect a statement thrown out by CFGBuilder: - // case X: {} case Y: - // case X: ; case Y: - if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) - if (!isa<SwitchCase>(SW->getSubStmt())) - return SW->getSubStmt(); + static const Stmt *getLastStmt(const CFGBlock &B) { + if (const Stmt *Term = B.getTerminatorStmt()) + return Term; + for (const CFGElement &Elem : llvm::reverse(B)) + if (std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>()) + return CS->getStmt(); + // Workaround to detect a statement thrown out by CFGBuilder: + // case X: {} case Y: + // case X: ; case Y: + if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel())) + if (!isa<SwitchCase>(SW->getSubStmt())) + return SW->getSubStmt(); - return nullptr; - } + return nullptr; + } - bool FoundSwitchStatements; - AttrStmts FallthroughStmts; - Sema &S; - llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; + bool FoundSwitchStatements; + AttrStmts FallthroughStmts; + Sema &S; + llvm::SmallPtrSet<const CFGBlock *, 16> ReachableBlocks; }; } // anonymous namespace static StringRef getFallthroughAttrSpelling(Preprocessor &PP, SourceLocation Loc) { - TokenValue FallthroughTokens[] = {tok::l_square, tok::l_square, - PP.getIdentifierInfo("fallthrough"), - tok::r_square, tok::r_square}; - - TokenValue ClangFallthroughTokens[] = {tok::l_square, - tok::l_square, - PP.getIdentifierInfo("clang"), - tok::coloncolon, - PP.getIdentifierInfo("fallthrough"), - tok::r_square, - tok::r_square}; + TokenValue FallthroughTokens[] = { + tok::l_square, tok::l_square, + PP.getIdentifierInfo("fallthrough"), + tok::r_square, tok::r_square + }; + + TokenValue ClangFallthroughTokens[] = { + tok::l_square, tok::l_square, PP.getIdentifierInfo("clang"), + tok::coloncolon, PP.getIdentifierInfo("fallthrough"), + tok::r_square, tok::r_square + }; bool PreferClangAttr = !PP.getLangOpts().CPlusPlus17 && !PP.getLangOpts().C23; @@ -1507,12 +1509,13 @@ static bool isInLoop(const ASTContext &Ctx, const ParentMap &PM, static void diagnoseRepeatedUseOfWeak(Sema &S, const sema::FunctionScopeInfo *CurFn, - const Decl *D, const ParentMap &PM) { + const Decl *D, + const ParentMap &PM) { typedef sema::FunctionScopeInfo::WeakObjectProfileTy WeakObjectProfileTy; typedef sema::FunctionScopeInfo::WeakObjectUseMap WeakObjectUseMap; typedef sema::FunctionScopeInfo::WeakUseVector WeakUseVector; typedef std::pair<const Stmt *, WeakObjectUseMap::const_iterator> - StmtUsesPair; + StmtUsesPair; ASTContext &Ctx = S.getASTContext(); @@ -1526,7 +1529,7 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Find the first read of the weak object. WeakUseVector::const_iterator UI = Uses.begin(), UE = Uses.end(); - for (; UI != UE; ++UI) { + for ( ; UI != UE; ++UI) { if (UI->isUnsafe()) break; } @@ -1583,7 +1586,12 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. // FIXME: Should we use a common classification enum and the same set of // possibilities all throughout Sema? - enum { Function, Method, Block, Lambda } FunctionKind; + enum { + Function, + Method, + Block, + Lambda + } FunctionKind; if (isa<sema::BlockScopeInfo>(CurFn)) FunctionKind = Block; @@ -1614,7 +1622,12 @@ static void diagnoseRepeatedUseOfWeak(Sema &S, // Classify the weak object being accessed for better warning text. // This enum should stay in sync with the cases in // warn_arc_repeated_use_of_weak and warn_arc_possible_repeated_use_of_weak. - enum { Variable, Property, ImplicitProperty, Ivar } ObjectKind; + enum { + Variable, + Property, + ImplicitProperty, + Ivar + } ObjectKind; const NamedDecl *KeyProp = Key.getProperty(); if (isa<VarDecl>(KeyProp)) @@ -1716,7 +1729,7 @@ class UninitValsDiagReporter : public UninitVariablesHandler { } private: - static bool hasAlwaysUninitializedUse(const UsesVec *vec) { + static bool hasAlwaysUninitializedUse(const UsesVec* vec) { return llvm::any_of(*vec, [](const UninitUse &U) { return U.getKind() == UninitUse::Always || U.getKind() == UninitUse::AfterCall || @@ -1966,10 +1979,10 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { : getNotes(); } -public: + public: ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL) - : S(S), FunLocation(FL), FunEndLocation(FEL), CurrentFunction(nullptr), - Verbose(false) {} + : S(S), FunLocation(FL), FunEndLocation(FEL), + CurrentFunction(nullptr), Verbose(false) {} void setVerbose(bool b) { Verbose = b; } @@ -2064,18 +2077,18 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { bool ReentrancyMismatch) override { unsigned DiagID = 0; switch (LEK) { - case LEK_LockedSomePredecessors: - DiagID = diag::warn_lock_some_predecessors; - break; - case LEK_LockedSomeLoopIterations: - DiagID = diag::warn_expecting_lock_held_on_loop; - break; - case LEK_LockedAtEndOfFunction: - DiagID = diag::warn_no_unlock; - break; - case LEK_NotLockedAtEndOfFunction: - DiagID = diag::warn_expecting_locked; - break; + case LEK_LockedSomePredecessors: + DiagID = diag::warn_lock_some_predecessors; + break; + case LEK_LockedSomeLoopIterations: + DiagID = diag::warn_expecting_lock_held_on_loop; + break; + case LEK_LockedAtEndOfFunction: + DiagID = diag::warn_no_unlock; + break; + case LEK_NotLockedAtEndOfFunction: + DiagID = diag::warn_expecting_locked; + break; } if (LocEndOfScope.isInvalid()) LocEndOfScope = FunEndLocation; @@ -2121,7 +2134,7 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { break; } PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) - << D << getLockKindFromAccessKind(AK)); + << D << getLockKindFromAccessKind(AK)); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2132,42 +2145,43 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { unsigned DiagID = 0; if (PossibleMatch) { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock_precise; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock_precise; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock_precise; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock_precise; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock_precise; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock_precise; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) - << Kind << D << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind + << D + << LockName << LK); PartialDiagnosticAt Note(Loc, S.PDiag(diag::note_found_mutex_near_match) << *PossibleMatch); if (Verbose && POK == POK_VarAccess) { @@ -2179,42 +2193,43 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { Warnings.emplace_back(std::move(Warning), getNotes(Note)); } else { switch (POK) { - case POK_VarAccess: - DiagID = diag::warn_variable_requires_lock; - break; - case POK_VarDereference: - DiagID = diag::warn_var_deref_requires_lock; - break; - case POK_FunctionCall: - DiagID = diag::warn_fun_requires_lock; - break; - case POK_PassByRef: - DiagID = diag::warn_guarded_pass_by_reference; - break; - case POK_PtPassByRef: - DiagID = diag::warn_pt_guarded_pass_by_reference; - break; - case POK_ReturnByRef: - DiagID = diag::warn_guarded_return_by_reference; - break; - case POK_PtReturnByRef: - DiagID = diag::warn_pt_guarded_return_by_reference; - break; - case POK_PassPointer: - DiagID = diag::warn_guarded_pass_pointer; - break; - case POK_PtPassPointer: - DiagID = diag::warn_pt_guarded_pass_pointer; - break; - case POK_ReturnPointer: - DiagID = diag::warn_guarded_return_pointer; - break; - case POK_PtReturnPointer: - DiagID = diag::warn_pt_guarded_return_pointer; - break; + case POK_VarAccess: + DiagID = diag::warn_variable_requires_lock; + break; + case POK_VarDereference: + DiagID = diag::warn_var_deref_requires_lock; + break; + case POK_FunctionCall: + DiagID = diag::warn_fun_requires_lock; + break; + case POK_PassByRef: + DiagID = diag::warn_guarded_pass_by_reference; + break; + case POK_PtPassByRef: + DiagID = diag::warn_pt_guarded_pass_by_reference; + break; + case POK_ReturnByRef: + DiagID = diag::warn_guarded_return_by_reference; + break; + case POK_PtReturnByRef: + DiagID = diag::warn_pt_guarded_return_by_reference; + break; + case POK_PassPointer: + DiagID = diag::warn_guarded_pass_pointer; + break; + case POK_PtPassPointer: + DiagID = diag::warn_pt_guarded_pass_pointer; + break; + case POK_ReturnPointer: + DiagID = diag::warn_guarded_return_pointer; + break; + case POK_PtReturnPointer: + DiagID = diag::warn_pt_guarded_return_pointer; + break; } - PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) - << Kind << D << LockName << LK); + PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << Kind + << D + << LockName << LK); if (Verbose && POK == POK_VarAccess) { PartialDiagnosticAt Note(D->getLocation(), S.PDiag(diag::note_guarded_by_declared_here)); @@ -2226,9 +2241,9 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc) override { - PartialDiagnosticAt Warning( - Loc, S.PDiag(diag::warn_acquire_requires_negative_cap) - << Kind << LockName << Neg); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_acquire_requires_negative_cap) + << Kind << LockName << Neg); Warnings.emplace_back(std::move(Warning), getNotes()); } @@ -2248,20 +2263,22 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler { void handleLockAcquiredBefore(StringRef Kind, Name L1Name, Name L2Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_acquired_before) - << Kind << L1Name << L2Name); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_acquired_before) << Kind << L1Name << L2Name); Warnings.emplace_back(std::move(Warning), getNotes()); } void handleBeforeAfterCycle(Name L1Name, SourceLocation Loc) override { - PartialDiagnosticAt Warning( - Loc, S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); + PartialDiagnosticAt Warning(Loc, + S.PDiag(diag::warn_acquired_before_after_cycle) << L1Name); Warnings.emplace_back(std::move(Warning), getNotes()); } - void enterFunction(const FunctionDecl *FD) override { CurrentFunction = FD; } + void enterFunction(const FunctionDecl* FD) override { + CurrentFunction = FD; + } - void leaveFunction(const FunctionDecl *FD) override { + void leaveFunction(const FunctionDecl* FD) override { CurrentFunction = nullptr; } }; @@ -2282,6 +2299,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { DiagList Warnings; public: + ConsumedWarningsHandler(Sema &S) : S(S) {} void emitDiagnostics() override { @@ -2295,8 +2313,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnLoopStateMismatch(SourceLocation Loc, StringRef VariableName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) - << VariableName); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_loop_state_mismatch) << + VariableName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2306,9 +2324,9 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning( - Loc, S.PDiag(diag::warn_param_return_typestate_mismatch) - << VariableName << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_param_return_typestate_mismatch) << VariableName << + ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2316,18 +2334,16 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnParamTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_param_typestate_mismatch) - << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_param_typestate_mismatch) << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } void warnReturnTypestateForUnconsumableType(SourceLocation Loc, StringRef TypeName) override { - PartialDiagnosticAt Warning( - Loc, S.PDiag(diag::warn_return_typestate_for_unconsumable_type) - << TypeName); + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_return_typestate_for_unconsumable_type) << TypeName); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2335,9 +2351,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_return_typestate_mismatch) - << ExpectedState << ObservedState); + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_return_typestate_mismatch) << ExpectedState << ObservedState); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2345,9 +2360,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, - S.PDiag(diag::warn_use_of_temp_in_invalid_state) - << MethodName << State); + PartialDiagnosticAt Warning(Loc, S.PDiag( + diag::warn_use_of_temp_in_invalid_state) << MethodName << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2355,9 +2369,8 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { void warnUseInInvalidState(StringRef MethodName, StringRef VariableName, StringRef State, SourceLocation Loc) override { - PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) - << MethodName << VariableName - << State); + PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) << + MethodName << VariableName << State); Warnings.emplace_back(std::move(Warning), OptionalNotes()); } @@ -2373,7 +2386,7 @@ class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase { namespace { class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { Sema &S; - bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? + bool SuggestSuggestions; // Recommend -fsafe-buffer-usage-suggestions? // Lists as a string the names of variables in `VarGroupForVD` except for `VD` // itself: @@ -2412,7 +2425,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { public: UnsafeBufferUsageReporter(Sema &S, bool SuggestSuggestions) - : S(S), SuggestSuggestions(SuggestSuggestions) {} + : S(S), SuggestSuggestions(SuggestSuggestions) {} void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx) override { @@ -2589,7 +2602,7 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { #ifndef NDEBUG if (areDebugNotesRequested()) - for (const DebugNote &Note : DebugNotesByVar[Variable]) + for (const DebugNote &Note: DebugNotesByVar[Variable]) S.Diag(Note.first, diag::note_safe_buffer_debug_mode) << Note.second; #endif } @@ -2616,12 +2629,6 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { return S.Diags.isIgnored(diag::warn_unsafe_buffer_libc_call, Loc); } - bool ignoreUnsafeBufferInStaticSizedArray( - const SourceLocation &Loc) const override { - return S.Diags.isIgnored( - diag::warn_unsafe_buffer_usage_in_static_sized_array, Loc); - } - // Returns the text representation of clang::unsafe_buffer_usage attribute. // `WSSuffix` holds customized "white-space"s, e.g., newline or whilespace // characters. @@ -2691,7 +2698,8 @@ sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) MaxCFGBlocksPerFunction(0), NumUninitAnalysisFunctions(0), NumUninitAnalysisVariables(0), MaxUninitAnalysisVariablesPerFunction(0), NumUninitAnalysisBlockVisits(0), - MaxUninitAnalysisBlockVisitsPerFunction(0) {} + MaxUninitAnalysisBlockVisitsPerFunction(0) { +} // We need this here for unique_ptr with forward declared class. sema::AnalysisBasedWarnings::~AnalysisBasedWarnings() = default; @@ -2935,7 +2943,7 @@ LifetimeSafetyTUAnalysis(Sema &S, TranslationUnitDecl *TU, } void clang::sema::AnalysisBasedWarnings::IssueWarnings( - TranslationUnitDecl *TU) { + TranslationUnitDecl *TU) { if (!TU) return; // This is unexpected, give up quietly. @@ -2949,7 +2957,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // UnsafeBufferUsage analysis settings. bool UnsafeBufferUsageCanEmitSuggestions = S.getLangOpts().CPlusPlus20; - bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. + bool UnsafeBufferUsageShouldEmitSuggestions = // Should != Can. UnsafeBufferUsageCanEmitSuggestions && DiagOpts.ShowSafeBufferUsageSuggestions; bool UnsafeBufferUsageShouldSuggestSuggestions = @@ -3057,13 +3065,13 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( AC.getCFGBuildOptions().setAllAlwaysAdd(); } else { AC.getCFGBuildOptions() - .setAlwaysAdd(Stmt::BinaryOperatorClass) - .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) - .setAlwaysAdd(Stmt::BlockExprClass) - .setAlwaysAdd(Stmt::CStyleCastExprClass) - .setAlwaysAdd(Stmt::DeclRefExprClass) - .setAlwaysAdd(Stmt::ImplicitCastExprClass) - .setAlwaysAdd(Stmt::UnaryOperatorClass); + .setAlwaysAdd(Stmt::BinaryOperatorClass) + .setAlwaysAdd(Stmt::CompoundAssignOperatorClass) + .setAlwaysAdd(Stmt::BlockExprClass) + .setAlwaysAdd(Stmt::CStyleCastExprClass) + .setAlwaysAdd(Stmt::DeclRefExprClass) + .setAlwaysAdd(Stmt::ImplicitCastExprClass) + .setAlwaysAdd(Stmt::UnaryOperatorClass); } if (EnableLifetimeSafetyAnalysis) { AC.getCFGBuildOptions().AddLifetime = true; @@ -3145,10 +3153,12 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( ++NumUninitAnalysisFunctions; NumUninitAnalysisVariables += stats.NumVariablesAnalyzed; NumUninitAnalysisBlockVisits += stats.NumBlockVisits; - MaxUninitAnalysisVariablesPerFunction = std::max( - MaxUninitAnalysisVariablesPerFunction, stats.NumVariablesAnalyzed); - MaxUninitAnalysisBlockVisitsPerFunction = std::max( - MaxUninitAnalysisBlockVisitsPerFunction, stats.NumBlockVisits); + MaxUninitAnalysisVariablesPerFunction = + std::max(MaxUninitAnalysisVariablesPerFunction, + stats.NumVariablesAnalyzed); + MaxUninitAnalysisBlockVisitsPerFunction = + std::max(MaxUninitAnalysisBlockVisitsPerFunction, + stats.NumBlockVisits); } } } @@ -3186,6 +3196,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, D->getBeginLoc())) diagnoseRepeatedUseOfWeak(S, fscope, D, AC.getParentMap()); + // Check for infinite self-recursion in functions if (!Diags.isIgnored(diag::warn_infinite_recursive_function, D->getBeginLoc())) { @@ -3216,8 +3227,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( // If we successfully built a CFG for this context, record some more // detail information about it. NumCFGBlocks += cfg->getNumBlockIDs(); - MaxCFGBlocksPerFunction = - std::max(MaxCFGBlocksPerFunction, cfg->getNumBlockIDs()); + MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction, + cfg->getNumBlockIDs()); } else { ++NumFunctionsWithBadCFGs; } @@ -3229,7 +3240,7 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs; unsigned AvgCFGBlocksPerFunction = - !NumCFGsBuilt ? 0 : NumCFGBlocks / NumCFGsBuilt; + !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt; llvm::errs() << NumFunctionsAnalyzed << " functions analyzed (" << NumFunctionsWithBadCFGs << " w/o CFGs).\n" << " " << NumCFGBlocks << " CFG blocks built.\n" @@ -3238,14 +3249,10 @@ void clang::sema::AnalysisBasedWarnings::PrintStats() const { << " " << MaxCFGBlocksPerFunction << " max CFG blocks per function.\n"; - unsigned AvgUninitVariablesPerFunction = - !NumUninitAnalysisFunctions - ? 0 - : NumUninitAnalysisVariables / NumUninitAnalysisFunctions; - unsigned AvgUninitBlockVisitsPerFunction = - !NumUninitAnalysisFunctions - ? 0 - : NumUninitAnalysisBlockVisits / NumUninitAnalysisFunctions; + unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0 + : NumUninitAnalysisVariables/NumUninitAnalysisFunctions; + unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0 + : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions; llvm::errs() << NumUninitAnalysisFunctions << " functions analyzed for uninitialiazed variables\n" << " " << NumUninitAnalysisVariables << " variables analyzed.\n" >From be7d92a1f658efac9862cb26cd50f6cba57e7d08 Mon Sep 17 00:00:00 2001 From: mxms <[email protected]> Date: Fri, 16 Jan 2026 19:44:31 +0000 Subject: [PATCH 4/5] Revert/fix --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 56d7db649afbe..ab6fc49ef8233 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2629,6 +2629,12 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { return S.Diags.isIgnored(diag::warn_unsafe_buffer_libc_call, Loc); } + bool ignoreUnsafeBufferInStaticSizedArray( + const SourceLocation &Loc) const override { + return S.Diags.isIgnored( + diag::warn_unsafe_buffer_usage_in_static_sized_array, Loc); + } + // Returns the text representation of clang::unsafe_buffer_usage attribute. // `WSSuffix` holds customized "white-space"s, e.g., newline or whilespace // characters. >From 43e5a05b8a4a7493e8f6878b613d6b76cf9ec507 Mon Sep 17 00:00:00 2001 From: mxms <[email protected]> Date: Fri, 16 Jan 2026 20:53:49 +0000 Subject: [PATCH 5/5] Updated comment --- clang/lib/Analysis/UnsafeBufferUsage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index d7df7d8d46b61..e0b2c9d4b9d9a 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -691,8 +691,8 @@ static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, } if (IgnoreStaticSizedArrays) { - // If we made it here, it means a size was found for the var being - // accessed. If it's fixed size, we can ignore it. + // If we made it here, it means a size was found for the var being accessed + // (either string literal or array). If it's fixed size, we can ignore it. return true; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
