llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-analysis @llvm/pr-subscribers-clangd Author: Discookie (Discookie) <details> <summary>Changes</summary> This checker implements a null-pointer analysis checker using the data-flow framework. In particular, the checker finds cases where the pointer's nullability is checked after it has been dereferenced: ``` int f(int *ptr) { *ptr += 20; // note: one of the locations where the pointer's value cannot be null if (ptr) { *ptr += 42; // warning: pointer value is checked even though it cannot be null at this point return *ptr; } return 0; ``` It currently recognizes the following operations: - Dereference and arrow operators - Pointer-to-boolean cast - Assigning &obj, nullptr and other values --- Notable limitations: The checker only supports C++ due to the limitations of the framework (llvm/llvm-project#<!-- -->65301). Function calls taking a reference of a pointer are not handled yet. ``void external(int *&ref);`` The note tag is only displayed at one location, and not all operations are supported or displayed. More examples and limitations in the [checker docs](https://github.com/Discookie/llvm-project/blob/reverse-null/clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst). --- Results on open-source projects: https://codechecker-demo.eastus.cloudapp.azure.com/Default/runs?run=Discookie_reverse-null The reference-of-ptr limitation leads to a class of false positives across the tested projects [(example)](https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=libwebm_libwebm-1.0.0.27_Discookie_reverse-null&detection-status=New&report-id=3497414&report-hash=e75b81fcd825616e0ec904ffef1c99ac&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Flibwebm%2Fmkvparser.cpp). But the checker also found quite a few true positives, on LLVM: [1](https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=llvm-project_llvmorg-12.0.0_Discookie_reverse-null&report-id=3503712&report-hash=63f82f7e62ecfea1c53c1896c29b2ef2&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Fllvm-project%2Fclang%2Finclude%2Fclang%2FBasic%2FDiagnostic.h&is-unique=off&diff-type=New&review-status=Confirmed%20bug) [2](https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=%2aDiscookie%2a&detection-status=New&report-id=3503771&report-hash=1f5c708ee2cfe736068e8f8a3fdc8634&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Fllvm-project%2Fclang%2Flib%2FAnalysis%2FCalledOnceCheck.cpp&page=3&is-unique=off&diff-type=New&review-status=Unreviewed) [3](https://codechecker-demo.eastus.cloudapp.azure.com/Default/report-detail?run=%2aDiscookie%2a&detection-status=New&report-id=3503770&report-hash=53c9082086ce7d320cf5e5aab78dbab6&report-filepath=%2Flocal%2Fpersistent_docker%2FCSA-measurements-driver-2923%2Fmeasurements_workspace%2Fllvm-project%2Fllvm%2Flib%2FMC%2FMCSymbol.cpp&page=3&is-unique=off&diff-type=New&review-status=Unreviewed) and a couple more, listed [here](https://github.com/Discookie/llvm-project/pull/1#issuecomment-1979214081). --- This checker corresponds to the the CERT rule [EXP34-C, Do not dereference null pointers](https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers#EXP34C.Donotdereferencenullpointers-NoncompliantCodeExample.2), example 3. --- Patch is 63.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84166.diff 12 Files Affected: - (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3) - (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1) - (added) clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.cpp (+175) - (added) clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.h (+37) - (modified) clang-tools-extra/clangd/TidyProvider.cpp (+2-1) - (added) clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst (+158) - (added) clang-tools-extra/test/clang-tidy/checkers/bugprone/null-check-after-dereference.cpp (+330) - (added) clang/include/clang/Analysis/FlowSensitive/Models/NullPointerAnalysisModel.h (+112) - (modified) clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt (+1) - (added) clang/lib/Analysis/FlowSensitive/Models/NullPointerAnalysisModel.cpp (+629) - (modified) clang/unittests/Analysis/FlowSensitive/CMakeLists.txt (+1) - (added) clang/unittests/Analysis/FlowSensitive/NullPointerAnalysisModelTest.cpp (+336) ``````````diff diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index a8a23b045f80bb..ddd708dd513355 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -48,6 +48,7 @@ #include "NoEscapeCheck.h" #include "NonZeroEnumToBoolConversionCheck.h" #include "NotNullTerminatedResultCheck.h" +#include "NullCheckAfterDereferenceCheck.h" #include "OptionalValueConversionCheck.h" #include "ParentVirtualCallCheck.h" #include "PosixReturnCheck.h" @@ -180,6 +181,8 @@ class BugproneModule : public ClangTidyModule { CheckFactories.registerCheck<PosixReturnCheck>("bugprone-posix-return"); CheckFactories.registerCheck<ReservedIdentifierCheck>( "bugprone-reserved-identifier"); + CheckFactories.registerCheck<NullCheckAfterDereferenceCheck>( + "bugprone-null-check-after-dereference"); CheckFactories.registerCheck<SharedPtrArrayMismatchCheck>( "bugprone-shared-ptr-array-mismatch"); CheckFactories.registerCheck<SignalHandlerCheck>("bugprone-signal-handler"); diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 1cd6fb207d7625..5dbe761cb810e7 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -44,6 +44,7 @@ add_clang_library(clangTidyBugproneModule NoEscapeCheck.cpp NonZeroEnumToBoolConversionCheck.cpp NotNullTerminatedResultCheck.cpp + NullCheckAfterDereferenceCheck.cpp OptionalValueConversionCheck.cpp ParentVirtualCallCheck.cpp PosixReturnCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.cpp new file mode 100644 index 00000000000000..1ba4edc1eaf0e4 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.cpp @@ -0,0 +1,175 @@ +//===--- NullCheckAfterDereferenceCheck.cpp - clang-tidy-------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NullCheckAfterDereferenceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/ControlFlowContext.h" +#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" +#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" +#include "clang/Analysis/FlowSensitive/DataflowLattice.h" +#include "clang/Analysis/FlowSensitive/Models/NullPointerAnalysisModel.h" +#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/Any.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include <memory> +#include <vector> + +namespace clang::tidy::bugprone { + +using ast_matchers::MatchFinder; +using dataflow::NullPointerAnalysisModel; +using dataflow::NullCheckAfterDereferenceDiagnoser; + +static constexpr llvm::StringLiteral FuncID("fun"); + +struct ExpandedResult { + SourceLocation WarningLoc; + std::optional<SourceLocation> DerefLoc; +}; + +using ExpandedResultType = std::pair<std::vector<ExpandedResult>, + std::vector<ExpandedResult>>; + +static std::optional<ExpandedResultType> analyzeFunction( + const FunctionDecl &FuncDecl) { + using dataflow::ControlFlowContext; + using dataflow::DataflowAnalysisState; + using llvm::Expected; + + ASTContext &ASTCtx = FuncDecl.getASTContext(); + + if (FuncDecl.getBody() == nullptr) { + return std::nullopt; + } + + Expected<ControlFlowContext> Context = + ControlFlowContext::build(FuncDecl, *FuncDecl.getBody(), ASTCtx); + if (!Context) + return std::nullopt; + + dataflow::DataflowAnalysisContext AnalysisContext( + std::make_unique<dataflow::WatchedLiteralsSolver>()); + dataflow::Environment Env(AnalysisContext, FuncDecl); + NullPointerAnalysisModel Analysis(ASTCtx); + NullCheckAfterDereferenceDiagnoser Diagnoser; + NullCheckAfterDereferenceDiagnoser::ResultType Diagnostics; + + using LatticeState = DataflowAnalysisState<NullPointerAnalysisModel::Lattice>; + using DetailMaybeLatticeStates = std::vector<std::optional<LatticeState>>; + + auto DiagnoserImpl = [&ASTCtx, &Diagnoser, &Diagnostics]( + const CFGElement &Elt, + const LatticeState &S) mutable -> void { + auto EltDiagnostics = Diagnoser.diagnose(ASTCtx, &Elt, S.Env); + llvm::move(EltDiagnostics.first, + std::back_inserter(Diagnostics.first)); + llvm::move(EltDiagnostics.second, + std::back_inserter(Diagnostics.second)); + }; + + Expected<DetailMaybeLatticeStates> + BlockToOutputState = dataflow::runDataflowAnalysis( + *Context, Analysis, Env, DiagnoserImpl); + + if (llvm::Error E = BlockToOutputState.takeError()) { + llvm::dbgs() << "Dataflow analysis failed: " << llvm::toString(std::move(E)) + << ".\n"; + return std::nullopt; + } + + ExpandedResultType ExpandedDiagnostics; + + llvm::transform(Diagnostics.first, + std::back_inserter(ExpandedDiagnostics.first), + [&](SourceLocation WarningLoc) -> ExpandedResult { + if (auto Val = Diagnoser.WarningLocToVal[WarningLoc]; + auto DerefExpr = Diagnoser.ValToDerefLoc[Val]) { + return {WarningLoc, DerefExpr->getBeginLoc()}; + } + + return {WarningLoc, std::nullopt}; + }); + + llvm::transform(Diagnostics.second, + std::back_inserter(ExpandedDiagnostics.second), + [&](SourceLocation WarningLoc) -> ExpandedResult { + if (auto Val = Diagnoser.WarningLocToVal[WarningLoc]; + auto DerefExpr = Diagnoser.ValToDerefLoc[Val]) { + return {WarningLoc, DerefExpr->getBeginLoc()}; + } + + return {WarningLoc, std::nullopt}; + }); + + return ExpandedDiagnostics; +} + +void NullCheckAfterDereferenceCheck::registerMatchers(MatchFinder *Finder) { + using namespace ast_matchers; + + auto hasPointerValue = + hasDescendant(NullPointerAnalysisModel::ptrValueMatcher()); + Finder->addMatcher( + decl(anyOf(functionDecl(unless(isExpansionInSystemHeader()), + // FIXME: Remove the filter below when lambdas are + // well supported by the check. + unless(hasDeclContext(cxxRecordDecl(isLambda()))), + hasBody(hasPointerValue)), + cxxConstructorDecl(hasAnyConstructorInitializer( + withInitializer(hasPointerValue))))) + .bind(FuncID), + this); +} + +void NullCheckAfterDereferenceCheck::check( + const MatchFinder::MatchResult &Result) { + if (Result.SourceManager->getDiagnostics().hasUncompilableErrorOccurred()) + return; + + const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>(FuncID); + assert(FuncDecl && "invalid FuncDecl matcher"); + if (FuncDecl->isTemplated()) + return; + + if (const auto Diagnostics = analyzeFunction(*FuncDecl)) { + const auto& [CheckWhenNullLocations, CheckAfterDereferenceLocations] = + *Diagnostics; + + for (const auto [WarningLoc, DerefLoc] : CheckAfterDereferenceLocations) { + diag(WarningLoc, + "pointer value is checked even though " + "it cannot be null at this point"); + + if (DerefLoc) { + diag(*DerefLoc, + "one of the locations where the pointer's value cannot be null", + DiagnosticIDs::Note); + } + } + + for (const auto [WarningLoc, DerefLoc] : CheckWhenNullLocations) { + diag(WarningLoc, + "pointer value is checked but it can only be null at this point"); + + if (DerefLoc) { + diag(*DerefLoc, + "one of the locations where the pointer's value can only be null", + DiagnosticIDs::Note); + } + } + } +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.h b/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.h new file mode 100644 index 00000000000000..e5ac27e79deb11 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/NullCheckAfterDereferenceCheck.h @@ -0,0 +1,37 @@ +//===--- NullCheckAfterDereferenceCheck.h - clang-tidy ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NULLCHECKAFTERDEREFERENCECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NULLCHECKAFTERDEREFERENCECHECK_H + +#include "../ClangTidyCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang::tidy::bugprone { + +/// Finds checks for pointer nullability after a pointer has already been +/// dereferenced, using the data-flow framework. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/null-check-after-dereference.html +class NullCheckAfterDereferenceCheck : public ClangTidyCheck { +public: + NullCheckAfterDereferenceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + // The data-flow framework does not support C because of AST differences. + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NULLCHECKAFTERDEREFERENCECHECK_H diff --git a/clang-tools-extra/clangd/TidyProvider.cpp b/clang-tools-extra/clangd/TidyProvider.cpp index b658a80559937c..cf7b4dff16070b 100644 --- a/clang-tools-extra/clangd/TidyProvider.cpp +++ b/clang-tools-extra/clangd/TidyProvider.cpp @@ -219,9 +219,10 @@ TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) { "-bugprone-use-after-move", // Alias for bugprone-use-after-move. "-hicpp-invalid-access-moved", - // Check uses dataflow analysis, which might hang/crash unexpectedly on + // Checks use dataflow analysis, which might hang/crash unexpectedly on // incomplete code. "-bugprone-unchecked-optional-access", + "-bugprone-null-check-after-dereference", // ----- Performance problems ----- diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst new file mode 100644 index 00000000000000..90e8897d7817ed --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/null-check-after-dereference.rst @@ -0,0 +1,158 @@ +.. title:: clang-tidy - bugprone-null-check-after-dereference + +bugprone-null-check-after-dereference +===================================== + +.. note:: + + This check uses a flow-sensitive static analysis to produce its + results. Therefore, it may be more resource intensive (RAM, CPU) than the + average clang-tidy check. + +This check identifies redundant pointer null-checks, by finding cases where the +pointer cannot be null at the location of the null-check. + +Redundant null-checks can signal faulty assumptions about the current value of +a pointer at different points in the program. Either the null-check is +redundant, or there could be a null-pointer dereference earlier in the program. + +.. code-block:: c++ + + int f(int *ptr) { + *ptr = 20; // note: one of the locations where the pointer's value cannot be null + // ... + if (ptr) { // bugprone: pointer is checked even though it cannot be null at this point + return *ptr; + } + return 0; + } + +Supported pointer operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pointer null-checks +------------------- + +The checker currently supports null-checks on pointers that use +``operator bool``, such as when being used as the condition +for an `if` statement. + +.. code-block:: c++ + + int f(int *ptr) { + if (ptr) { + if (ptr) { // bugprone: pointer is re-checked after its null-ness is already checked. + return *ptr; + } + + return ptr ? *ptr : 0; // bugprone: pointer is re-checked after its null-ness is already checked. + } + return 0; + } + +Pointer dereferences +-------------------- + +Pointer star- and arrow-dereferences are supported. + +.. code-block:: c++ + + struct S { + int val; + }; + + void f(int *ptr, S *wrapper) { + *ptr = 20; + wrapper->val = 15; + } + +Null-pointer and other value assignments +---------------------------------------- + +The checker supports assigning various values to pointers, making them *null* +or *non-null*. The checker also supports passing pointers of a pointer to +external functions. + +.. code-block:: c++ + + extern int *external(); + extern void refresh(int **ptr_ptr); + + int f() { + int *ptr_null = nullptr; + if (ptr_null) { // bugprone: pointer is checked where it cannot be non-null. + return *ptr_null; + } + + int *ptr = external(); + if (ptr) { // safe: external() could return either nullable or nonnull pointers. + return *ptr; + } + + int *ptr2 = external(); + *ptr2 = 20; + refresh(&ptr2); + if (ptr2) { // safe: pointer could be changed by refresh(). + return *ptr2; + } + return 0; + } + +Limitations +~~~~~~~~~~~ + +The check only supports C++ due to limitations in the data-flow framework. + +The check currently does not output the locations of the previous +dereferences, even in simple cases. + +The annotations ``_nullable`` and ``_nonnull`` are not supported. + +.. code-block:: c++ + + extern int *_nonnull external_nonnull(); + + int annotations() { + int *ptr = external_nonnull(); + + return ptr ? *ptr : 0; // false-negative: pointer is known to be non-null. + } + +Function calls taking a pointer value as a reference or a pointer-to-pointer are not supported. + +.. code-block:: c++ + extern int *external(); + extern void refresh_ref(int *&ptr); + extern void refresh_ptr(int **ptr); + + int extern_ref() { + int *ptr = external(); + *ptr = 20; + + refresh_ref(ptr); + refresh_ptr(&ptr); + + return ptr ? *ptr : 0; // false-positive: pointer could be changed by refresh_ref(). + } + +Note tags are currently appended to a single location, even if all paths ensure a pointer is not null. + +.. code-block:: c++ + int branches(int *p, bool b) { + if (b) { + *p = 42; // true-positive: note-tag appended here + } else { + *p = 20; // false-positive: note tag not appended here + } + + return ptr ? *ptr : 0; + } + +Declarations and some other operations are not supported by note tags yet. This can sometimes result in erroneous note tags being shown instead of the correct one. + +.. code-block:: c++ + int note_tags() { + int *ptr = nullptr; // false-negative: note tag not shown + + return ptr ? *ptr : 0; + } diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/null-check-after-dereference.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/null-check-after-dereference.cpp new file mode 100644 index 00000000000000..21e9eff4290f7a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/null-check-after-dereference.cpp @@ -0,0 +1,330 @@ +// RUN: %check_clang_tidy %s bugprone-null-check-after-dereference %t + +struct S { + int a; +}; + +int warning_deref(int *p) { + *p = 42; + + if (p) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point [bugprone-null-check-after-dereference] + // CHECK-MESSAGES: :[[@LINE-4]]:3: note: one of the locations where the pointer's value cannot be null + // FIXME: If there's a direct path, make the error message more precise, ie. remove `one of the locations` + *p += 20; + return *p; + } else { + return 0; + } +} + +int warning_member(S *q) { + q->a = 42; + + if (q) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-4]]:3: note: one of the locations where the pointer's value cannot be null + q->a += 20; + return q->a; + } else { + return 0; + } +} + +int negative_warning(int *p) { + *p = 42; + + if (!p) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-4]]:3: note: one of the locations where the pointer's value cannot be null + return 0; + } else { + *p += 20; + return *p; + } +} + +int no_warning(int *p, bool b) { + if (b) { + *p = 42; + } + + if (p) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + *p += 20; + return *p; + } else { + return 0; + } +} + +int else_branch_warning(int *p, bool b) { + if (b) { + *p = 42; + } else { + *p = 20; + } + + if (p) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-7]]:5: note: one of the locations where the pointer's value cannot be null + return 0; + } else { + *p += 20; + return *p; + } +} + +int two_branches_warning(int *p, bool b) { + if (b) { + *p = 42; + } + + if (!b) { + *p = 20; + } + + if (p) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-9]]:5: note: one of the locations where the pointer's value cannot be null + return 0; + } else { + *p += 20; + return *p; + } +} + +int two_branches_reversed(int *p, bool b) { + if (!b) { + *p = 42; + } + + if (b) { + *p = 20; + } + + if (p) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-9]]:5: note: one of the locations where the pointer's value cannot be null + return 0; + } else { + *p += 20; + return *p; + } +} + + +int regular_assignment(int *p, int *q) { + *p = 42; + q = p; + + if (q) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + // CHECK-MESSAGES: :[[@LINE-5]]:3: note: one of the locations where the pointer's value cannot be null + *p += 20; + return *p; + } else { + return 0; + } +} + +int nullptr_assignment(int *nullptr_param, bool b) { + *nullptr_param = 42; + int *nullptr_assigned; + + if (b) { + nullptr_assigned = nullptr; + } else { + nullptr_assigned = nullptr_param; + } + + if (nullptr_assigned) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at this point + *nullptr_assigned = 20; + return *nullptr_assigned; + } else { + return 0; + } +} + +extern int *fncall(); +extern void refresh_ref(int *&ptr); +extern void refresh_ptr(int **ptr); + +int fncall_reassignment(int *fncall_reassigned) { + *fncall_reassigned = 42; + + fncall_reassigned = fncall(); + + if (fncall_reassigned) { + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:7: warning: pointer value is checked even though it cannot be null at... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/84166 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits