https://github.com/ConcreteCactus updated 
https://github.com/llvm/llvm-project/pull/130421

From c01a4c0c255bd52bd07a850b9d593128263d4db1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81ron=20H=C3=A1rn=C3=A1si?= <aron.harn...@gmail.com>
Date: Fri, 22 Nov 2024 21:43:04 +0100
Subject: [PATCH] [clang-tidy] Added bugprone-unsequenced-global-accesses check

    This check attempts to detect unsequenced accesses to global
    variables. It recurses into function calls in the same translation unit,
    and can handle fields on global structs/unions.
---
 .../bugprone/BugproneTidyModule.cpp           |   3 +
 .../clang-tidy/bugprone/CMakeLists.txt        |   1 +
 .../UnsequencedGlobalAccessesCheck.cpp        | 747 ++++++++++++++++++
 .../bugprone/UnsequencedGlobalAccessesCheck.h |  35 +
 .../clang-tidy/cert/CERTTidyModule.cpp        |   6 +
 .../clang-tidy/utils/ExecutionVisitor.h       | 195 +++++
 clang-tools-extra/docs/ReleaseNotes.rst       |  14 +
 .../bugprone/unsequenced-global-accesses.rst  |  86 ++
 .../docs/clang-tidy/checks/cert/exp30-c.rst   |  10 +
 .../docs/clang-tidy/checks/cert/exp50-cpp.rst |  10 +
 .../docs/clang-tidy/checks/list.rst           |   3 +
 .../bugprone/unsequenced-global-accesses.cpp  | 601 ++++++++++++++
 12 files changed, 1711 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.h
 create mode 100644 clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsequenced-global-accesses.rst
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cert/exp30-c.rst
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/cert/exp50-cpp.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsequenced-global-accesses.cpp

diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp 
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 64f4a524daf0d..db94933b8bd5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -95,6 +95,7 @@
 #include "UnintendedCharOstreamOutputCheck.h"
 #include "UniquePtrArrayMismatchCheck.h"
 #include "UnsafeFunctionsCheck.h"
+#include "UnsequencedGlobalAccessesCheck.h"
 #include "UnusedLocalNonTrivialVariableCheck.h"
 #include "UnusedRaiiCheck.h"
 #include "UnusedReturnValueCheck.h"
@@ -128,6 +129,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-chained-comparison");
     CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
         "bugprone-compare-pointer-to-member-virtual-function");
+    CheckFactories.registerCheck<UnsequencedGlobalAccessesCheck>(
+        "bugprone-unsequenced-global-accesses");
     CheckFactories.registerCheck<CopyConstructorInitCheck>(
         "bugprone-copy-constructor-init");
     CheckFactories.registerCheck<DanglingHandleCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index d862794cde323..8f361ba9eb5f3 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -96,6 +96,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   UnhandledSelfAssignmentCheck.cpp
   UniquePtrArrayMismatchCheck.cpp
   UnsafeFunctionsCheck.cpp
+  UnsequencedGlobalAccessesCheck.cpp
   UnusedLocalNonTrivialVariableCheck.cpp
   UnusedRaiiCheck.cpp
   UnusedReturnValueCheck.cpp
diff --git 
a/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.cpp
new file mode 100644
index 0000000000000..e275df1c40b4c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.cpp
@@ -0,0 +1,747 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "UnsequencedGlobalAccessesCheck.h"
+
+#include "../utils/ExecutionVisitor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+// An AccesKind represents one access to a global variable.
+//
+// The unchecked versions represent reads/writes that are not handled by
+// -Wunsequenced. (e.g. accesses inside functions).
+enum AccessKind : uint8_t {
+  AkRead = 0,
+  AkWrite,
+  AkLast
+};
+
+static constexpr uint8_t AkCount = AkLast;
+
+// The TraversalResultKind represents a set of accesses.
+// Bits are corresponding to the AccessKind enum values.
+using TraversalResultKind = uint8_t;
+static constexpr TraversalResultKind TrInvalid = 0;
+static constexpr TraversalResultKind TrRead = 1 << AkRead;
+static constexpr TraversalResultKind TrWrite = 1 << AkWrite;
+
+// To represent fields in structs or unions we use numbered FieldIndices. The
+// FieldIndexArray represents one field inside a global struct/union system.
+// The FieldIndexArray can be thought of as a path inside a tree.
+using FieldIndex = uint16_t;
+static constexpr FieldIndex FiUnion = 0x8000;
+
+// Note: This bit signals whether the field is a *field of* a struct or a
+// union, not whether the type of the field itself is a struct or a union.
+using FieldIndexArray = SmallVector<FieldIndex>;
+
+/// One traversal recurses into one side of a binary expression or one
+/// parameter of a function call. At least two of these traversals are used to
+/// find conflicting accesses.
+///
+/// A TraversalResult represents one traversal.
+struct TraversalResult {
+  int IndexCreated; // We use indices to keep track of which
+                    // traversal we are in currently. The current
+                    // index is stored in GlobalRWVisitor with the
+                    // name TraversalIndex.
+  SourceLocation Loc[AkCount];
+  TraversalResultKind Kind;
+
+  TraversalResult();
+  TraversalResult(int Index, SourceLocation Loc, AccessKind Access);
+  void addNewAccess(SourceLocation Loc, AccessKind Access);
+};
+
+/// The result of a number of traversals.
+class TraversalAggregation {
+  DeclarationName DeclName; // The name of the global variable being checked.
+
+  // We only store the result of two traversals as two conflicting accesses
+  // are enough to detect undefined behavior. The two stored TraversalResults
+  // have different traversal indices.
+  //
+  // Note: Sometimes multiple traversals are merged into one
+  // TraversalResult.
+  TraversalResult MainPart, OtherPart;
+  // Double reads are not reportable.
+
+public:
+  TraversalAggregation();
+  TraversalAggregation(DeclarationName Name, SourceLocation Loc,
+                       AccessKind Access, int Index);
+  void addGlobalRW(SourceLocation Loc, AccessKind Access, int Index);
+  DeclarationName getDeclName() const;
+
+  bool isValid() const;
+
+  // If there is a conflict and that conflict isn't reported by -Wunsequenced
+  // then we report the conflict.
+  bool shouldBeReported() const;
+  bool hasConflictingOperations() const;
+
+private:
+  bool hasTwoAccesses() const;
+};
+
+/// The ObjectAccessTree stores the TraversalAggregations of one global
+/// struct/union. Because each field can be handled as a single variable, the
+/// tree stores one TraversalAggregation for every field.
+///
+/// Note: structs, classes, and unions are called objects in the code.
+struct ObjectAccessTree {
+  using FieldMap = llvm::DenseMap<int, std::unique_ptr<ObjectAccessTree>>;
+  TraversalAggregation OwnAccesses;
+
+  // In a union, new fields should inherit from UnionTemporalAccesses
+  // instead of OwnAccesses. That's because an access to a field of a union is
+  // also an access to every other field of the same union.
+  TraversalAggregation UnionTemporalAccesses;
+
+  // We try to be lazy and only store fields that are actually accessed.
+  FieldMap Fields;
+  bool IsUnion;
+
+  ObjectAccessTree(TraversalAggregation Own);
+
+  void addFieldToAll(SourceLocation Loc, AccessKind Access, int Index);
+  void addFieldToAllExcept(uint16_t ExceptIndex, SourceLocation Loc,
+                           AccessKind Access, int Index);
+};
+
+/// This object is the root of all ObjectAccessTrees.
+class ObjectTraversalAggregation {
+  DeclarationName DeclName; // The name of the global struct/union.
+  ObjectAccessTree AccessTree;
+
+public:
+  ObjectTraversalAggregation(DeclarationName Name, SourceLocation Loc,
+                             FieldIndexArray FieldIndices, AccessKind Access,
+                             int Index);
+  void addFieldRW(SourceLocation Loc, FieldIndexArray FieldIndices,
+                  AccessKind Access, int Index);
+  DeclarationName getDeclName() const;
+  bool shouldBeReported() const;
+
+private:
+  bool shouldBeReportedRec(const ObjectAccessTree *Node) const;
+};
+
+using utils::ExecutionVisitor;
+
+/// GlobalRWVisitor (global read write visitor) does all the traversals.
+class GlobalRWVisitor : public ExecutionVisitor<GlobalRWVisitor> {
+public:
+  GlobalRWVisitor(bool IsWritePossibleThroughFunctionParam);
+
+  // startTraversal is called to start a new traversal. It increments the
+  // TraversalIndex, which in turn will generate new TraversalResults.
+  void startTraversal(const Expr *E);
+
+  const llvm::SmallVector<TraversalAggregation> &getGlobalsFound() const;
+
+  const llvm::SmallVector<ObjectTraversalAggregation> &
+  getObjectGlobalsFound() const;
+
+  // RecursiveASTVisitor overrides
+  bool VisitDeclRefExpr(DeclRefExpr *S);
+  bool VisitUnaryOperator(UnaryOperator *Op);
+  bool VisitBinaryOperator(BinaryOperator *Op);
+  bool VisitCallExpr(CallExpr *CE);
+  bool VisitCXXConstructExpr(CXXConstructExpr *CE);
+  bool VisitMemberExpr(MemberExpr *ME);
+
+private:
+  void visitFunctionLikeExprArgs(const FunctionProtoType *FT,
+                                 CallExpr::const_arg_range Arguments);
+  void visitCallExprArgs(const CallExpr *CE);
+  void visitConstructExprArgs(const CXXConstructExpr *CE);
+
+  llvm::SmallVector<TraversalAggregation> GlobalsFound;
+  llvm::SmallVector<ObjectTraversalAggregation> ObjectGlobalsFound;
+
+  // The TraversalIndex is used to differentiate between two sides of a binary
+  // expression or the parameters of a function. Every traversal represents
+  // one such expression and the TraversalIndex is incremented between them.
+  int TraversalIndex;
+
+  // Same as the HandleMutableFunctionParametersAsWrites option.
+  bool IsWritePossibleThroughFunctionParam;
+
+  void addGlobal(DeclarationName Name, SourceLocation Loc, bool IsWrite);
+  void addGlobal(const DeclRefExpr *DR, bool IsWrite);
+  void addField(DeclarationName Name, FieldIndexArray FieldIndices,
+                SourceLocation Loc, bool IsWrite);
+  bool handleModified(const Expr *Modified);
+  bool handleModifiedVariable(const DeclRefExpr *DE);
+  bool handleAccessedObject(const Expr *E, bool IsWrite);
+  bool isVariable(const Expr *E);
+};
+
+AST_MATCHER_P(BinaryOperator, unsequencedBinaryOperator, const LangStandard *,
+              LangStd) {
+  assert(LangStd);
+
+  const BinaryOperator *Op = &Node;
+
+  const BinaryOperator::Opcode Code = Op->getOpcode();
+  if (Code == BO_LAnd || Code == BO_LOr || Code == BO_Comma)
+    return false;
+
+  if (Op->isAssignmentOp() && isa<DeclRefExpr>(Op->getLHS()))
+    return false;
+
+  if (LangStd->isCPlusPlus17() &&
+      (Code == BO_Shl || Code == BO_Shr || Code == BO_PtrMemD ||
+       Code == BO_PtrMemI || Op->isAssignmentOp()))
+    return false;
+
+  return true;
+}
+} // namespace
+
+static bool isGlobalDecl(const VarDecl *VD) {
+  return VD && VD->hasGlobalStorage() && VD->getLocation().isValid() &&
+         !VD->getType().isConstQualified();
+}
+
+UnsequencedGlobalAccessesCheck::UnsequencedGlobalAccessesCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      HandleMutableFunctionParametersAsWrites(
+          Options.get("HandleMutableFunctionParametersAsWrites", false)) {}
+
+void UnsequencedGlobalAccessesCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HandleMutableFunctionParametersAsWrites",
+                HandleMutableFunctionParametersAsWrites);
+}
+
+void UnsequencedGlobalAccessesCheck::registerMatchers(MatchFinder *Finder) {
+
+  const LangStandard *LangStd =
+      &LangStandard::getLangStandardForKind(getLangOpts().LangStd);
+
+  auto BinaryOperatorMatcher = unsequencedBinaryOperator(LangStd);
+
+  Finder->addMatcher(
+      stmt(traverse(TK_AsIs, 
binaryOperator(BinaryOperatorMatcher).bind("gw"))),
+      this);
+
+  // Array subscript expressions became sequenced in C++17
+  if (!LangStd->isCPlusPlus17())
+    Finder->addMatcher(stmt(traverse(TK_AsIs, 
arraySubscriptExpr().bind("gw"))),
+                       this);
+
+  Finder->addMatcher(stmt(traverse(TK_AsIs, callExpr().bind("gw"))), this);
+
+  if (!LangStd->isCPlusPlus11())
+    Finder->addMatcher(stmt(traverse(TK_AsIs, initListExpr().bind("gw"))),
+                       this);
+
+  Finder->addMatcher(stmt(traverse(TK_AsIs, cxxConstructExpr().bind("gw"))),
+                     this);
+}
+
+void UnsequencedGlobalAccessesCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const Expr *E = Result.Nodes.getNodeAs<Expr>("gw");
+
+  GlobalRWVisitor Visitor(HandleMutableFunctionParametersAsWrites);
+  if (const auto *Op = dyn_cast<BinaryOperator>(E)) {
+    Visitor.startTraversal(Op->getLHS());
+    Visitor.startTraversal(Op->getRHS());
+
+  } else if (const auto *CE = dyn_cast<CallExpr>(E)) {
+    // A CallExpr will include its defaulted arguments as well.
+    for (const Expr *Arg : CE->arguments()) {
+      // For some reason, calling TraverseStmt on Arg directly
+      // doesn't recurse when Arg is a default argument.
+      if (const auto *DA = dyn_cast<CXXDefaultArgExpr>(Arg)) {
+        Visitor.startTraversal(DA->getExpr());
+        continue;
+      }
+      Visitor.startTraversal(Arg);
+    }
+  } else if (const auto *AS = dyn_cast<ArraySubscriptExpr>(E)) {
+    Visitor.startTraversal(AS->getLHS());
+    Visitor.startTraversal(AS->getRHS());
+  } else if (const auto *IL = dyn_cast<InitListExpr>(E)) {
+    llvm::SmallVector<const InitListExpr *> NestedInitializers;
+    NestedInitializers.push_back(IL);
+    while (!NestedInitializers.empty()) {
+      const InitListExpr *CurrentIL =
+          NestedInitializers[NestedInitializers.size() - 1];
+      NestedInitializers.pop_back();
+
+      for (const auto *I : CurrentIL->inits()) {
+        if (const InitListExpr *Nested = dyn_cast<InitListExpr>(I)) {
+          NestedInitializers.push_back(Nested);
+          continue;
+        }
+
+        Visitor.startTraversal(I);
+      }
+    }
+  } else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
+    for (const Expr *Arg : CE->arguments()) {
+      // For some reason, calling TraverseStmt on Arg directly
+      // doesn't recurse when Arg is a default argument.
+      if (const auto *DA = dyn_cast<CXXDefaultArgExpr>(Arg)) {
+        Visitor.startTraversal(DA->getExpr());
+        continue;
+      }
+      Visitor.startTraversal(Arg);
+    }
+  }
+
+  const llvm::SmallVector<TraversalAggregation> &Globals =
+      Visitor.getGlobalsFound();
+
+  for (const TraversalAggregation& Global : Globals)
+    if (Global.shouldBeReported())
+      diag(E->getBeginLoc(), "read/write conflict on global variable " +
+                                 Global.getDeclName().getAsString());
+
+  const llvm::SmallVector<ObjectTraversalAggregation> &ObjectGlobals =
+      Visitor.getObjectGlobalsFound();
+
+  for (const ObjectTraversalAggregation& ObjectGlobal : ObjectGlobals)
+    if (ObjectGlobal.shouldBeReported())
+      diag(E->getBeginLoc(), "read/write conflict on the field of the global "
+                             "object " +
+                                 ObjectGlobal.getDeclName().getAsString());
+}
+
+GlobalRWVisitor::GlobalRWVisitor(bool IsWritePossibleThroughFunctionParam)
+    : TraversalIndex(0),
+      IsWritePossibleThroughFunctionParam(IsWritePossibleThroughFunctionParam) 
{
+}
+
+void GlobalRWVisitor::startTraversal(const Expr *E) {
+  TraversalIndex++;
+  traverseExecution(const_cast<Expr *>(E));
+}
+
+bool GlobalRWVisitor::isVariable(const Expr *E) {
+  const Type *T = E->getType().getTypePtrOrNull();
+  if (!T)
+    return false;
+
+  return isa<DeclRefExpr>(E) && (!T->isRecordType() || T->isUnionType());
+}
+
+bool GlobalRWVisitor::VisitDeclRefExpr(DeclRefExpr *DR) {
+  const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
+  if (!VD)
+    return true;
+
+  if (!isVariable(DR))
+    return handleAccessedObject(DR, /*IsWrite*/ false);
+
+  if (isGlobalDecl(VD)) {
+    addGlobal(VD->getDeclName(), VD->getBeginLoc(), /*IsWrite*/ false);
+    return true;
+  }
+  return true;
+}
+
+bool GlobalRWVisitor::VisitMemberExpr(MemberExpr *ME) {
+  return handleAccessedObject(ME, /*IsWrite*/ false);
+}
+
+bool GlobalRWVisitor::handleModifiedVariable(const DeclRefExpr *DR) {
+  const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
+  if (!VD)
+    return true;
+
+  if (isGlobalDecl(VD)) {
+    addGlobal(VD->getDeclName(), VD->getBeginLoc(), /*IsWrite*/ true);
+    return false;
+  }
+
+  return true;
+}
+
+bool GlobalRWVisitor::handleAccessedObject(const Expr *E, bool IsWrite) {
+  const Expr *CurrentNode = E;
+  int NodeCount = 0;
+  while (isa<MemberExpr>(CurrentNode)) {
+    const MemberExpr *CurrentField = dyn_cast<MemberExpr>(CurrentNode);
+
+    if (CurrentField->isArrow())
+      return true;
+
+    const ValueDecl *Decl = CurrentField->getMemberDecl();
+    if (!isa<FieldDecl>(Decl))
+      return true;
+
+    CurrentNode = CurrentField->getBase();
+    NodeCount++;
+  }
+
+  const DeclRefExpr *Base = dyn_cast<DeclRefExpr>(CurrentNode);
+  if (!Base)
+    return true;
+
+  const VarDecl *BaseDecl = dyn_cast<VarDecl>(Base->getDecl());
+  if (!BaseDecl)
+    return true;
+
+  if (!isGlobalDecl(BaseDecl))
+    return true;
+
+  FieldIndexArray FieldIndices(NodeCount);
+  CurrentNode = E;
+  while (isa<MemberExpr>(CurrentNode)) {
+    const MemberExpr *CurrentField = dyn_cast<MemberExpr>(CurrentNode);
+    const FieldDecl *Decl = dyn_cast<FieldDecl>(CurrentField->getMemberDecl());
+    assert(Decl);
+
+    FieldIndices[NodeCount - 1] = Decl->getFieldIndex();
+    const RecordDecl *Record = Decl->getParent();
+    assert(Record);
+
+    if (Record->isUnion())
+      FieldIndices[NodeCount - 1] |= FiUnion;
+
+    CurrentNode = CurrentField->getBase();
+    NodeCount--;
+  }
+
+  addField(BaseDecl->getDeclName(), FieldIndices, Base->getBeginLoc(), 
IsWrite);
+  return false;
+}
+
+bool GlobalRWVisitor::handleModified(const Expr *Modified) {
+  assert(Modified);
+
+  if (isVariable(Modified))
+    return handleModifiedVariable(dyn_cast<DeclRefExpr>(Modified));
+
+  return handleAccessedObject(Modified, /*IsWrite*/ true);
+}
+
+bool GlobalRWVisitor::VisitUnaryOperator(UnaryOperator *Op) {
+  UnaryOperator::Opcode Code = Op->getOpcode();
+  if (Code == UO_PostInc || Code == UO_PostDec || Code == UO_PreInc ||
+      Code == UO_PreDec)
+    return handleModified(Op->getSubExpr());
+
+  // Ignore the AddressOf operator as it doesn't read the variable.
+  if (Code == UO_AddrOf && isa<DeclRefExpr>(Op->getSubExpr()))
+    return false;
+
+  return true;
+}
+
+bool GlobalRWVisitor::VisitBinaryOperator(BinaryOperator *Op) {
+  if (Op->isAssignmentOp())
+    return handleModified(Op->getLHS());
+
+  return true;
+}
+
+void GlobalRWVisitor::visitFunctionLikeExprArgs(
+    const FunctionProtoType *FT, CallExpr::const_arg_range Arguments) {
+
+  uint32_t I = 0;
+  auto ArgumentsEnd = Arguments.end();
+  for (auto It = Arguments.begin(); It != ArgumentsEnd; It++, I++) {
+    const Expr *Arg = *It;
+
+    if (I >= FT->getNumParams())
+      continue;
+
+    if (const auto *Op = dyn_cast<UnaryOperator>(Arg)) {
+      if (Op->getOpcode() != UO_AddrOf)
+        continue;
+
+      if (const auto *PtrType = dyn_cast_if_present<PointerType>(
+              FT->getParamType(I).getTypePtrOrNull())) {
+        if (PtrType->getPointeeType().isConstQualified())
+          continue;
+
+        if (handleModified(Op->getSubExpr()))
+          continue;
+      }
+    }
+
+    if (const auto *RefType = dyn_cast_if_present<ReferenceType>(
+            FT->getParamType(I).getTypePtrOrNull())) {
+      if (RefType->getPointeeType().isConstQualified())
+        continue;
+
+      if (handleModified(Arg))
+        continue;
+    }
+  }
+}
+
+void GlobalRWVisitor::visitCallExprArgs(const CallExpr *CE) {
+  const Type *CT = CE->getCallee()->getType().getTypePtrOrNull();
+  if (const auto *PT = dyn_cast_if_present<PointerType>(CT))
+    CT = PT->getPointeeType().getTypePtrOrNull();
+
+  const auto *ProtoType = dyn_cast_if_present<FunctionProtoType>(CT);
+  if (!ProtoType)
+    return;
+
+  visitFunctionLikeExprArgs(ProtoType, CE->arguments());
+}
+
+void GlobalRWVisitor::visitConstructExprArgs(const CXXConstructExpr *E) {
+  const FunctionDecl *Decl = E->getConstructor();
+  const Type *T = Decl->getType().getTypePtrOrNull();
+  if (!T)
+    return;
+
+  const auto *FT = dyn_cast<FunctionProtoType>(T);
+  if (!FT)
+    return;
+
+  visitFunctionLikeExprArgs(FT, E->arguments());
+}
+
+bool GlobalRWVisitor::VisitCallExpr(CallExpr *CE) {
+
+  if (IsWritePossibleThroughFunctionParam || isa<CXXOperatorCallExpr>(CE))
+    visitCallExprArgs(CE);
+
+  return ExecutionVisitor::VisitCallExpr(CE);
+}
+
+bool GlobalRWVisitor::VisitCXXConstructExpr(CXXConstructExpr *CE) {
+  if (IsWritePossibleThroughFunctionParam)
+    visitConstructExprArgs(CE);
+
+  return ExecutionVisitor::VisitCXXConstructExpr(CE);
+}
+
+const llvm::SmallVector<TraversalAggregation> &
+GlobalRWVisitor::getGlobalsFound() const {
+  return GlobalsFound;
+}
+
+const llvm::SmallVector<ObjectTraversalAggregation> &
+GlobalRWVisitor::getObjectGlobalsFound() const {
+  return ObjectGlobalsFound;
+}
+
+void GlobalRWVisitor::addGlobal(DeclarationName Name, SourceLocation Loc,
+                                bool IsWrite) {
+  AccessKind Access = IsWrite ? AkWrite : AkRead;
+
+  for (TraversalAggregation& Global : GlobalsFound) {
+    if (Global.getDeclName() == Name) {
+      Global.addGlobalRW(Loc, Access, TraversalIndex);
+      return;
+    }
+  }
+
+  GlobalsFound.emplace_back(Name, Loc, Access, TraversalIndex);
+}
+
+void GlobalRWVisitor::addField(DeclarationName Name,
+                               FieldIndexArray FieldIndices, SourceLocation 
Loc,
+                               bool IsWrite) {
+  AccessKind Access = IsWrite ? AkWrite : AkRead;
+
+  for (ObjectTraversalAggregation& ObjectGlobal : ObjectGlobalsFound) {
+    if (ObjectGlobal.getDeclName() == Name) {
+      ObjectGlobal.addFieldRW(Loc, FieldIndices, Access, TraversalIndex);
+      return;
+    }
+  }
+
+  ObjectGlobalsFound.emplace_back(Name, Loc, FieldIndices, Access,
+                                  TraversalIndex);
+}
+
+static TraversalResultKind akToTr(AccessKind Ak) { return 1 << Ak; }
+
+TraversalAggregation::TraversalAggregation() {}
+
+TraversalAggregation::TraversalAggregation(DeclarationName Name,
+                                           SourceLocation Loc,
+                                           AccessKind Access, int Index)
+    : DeclName(Name), MainPart(Index, Loc, Access) {}
+
+void TraversalAggregation::addGlobalRW(SourceLocation Loc, AccessKind Access,
+                                       int Index) {
+  if (!isValid()) {
+    MainPart = TraversalResult(Index, Loc, Access);
+    return;
+  }
+
+  if (MainPart.IndexCreated == Index) {
+    MainPart.addNewAccess(Loc, Access);
+    return;
+  }
+
+  if (!hasTwoAccesses()) {
+    OtherPart = TraversalResult(Index, Loc, Access);
+    return;
+  }
+
+  if (OtherPart.IndexCreated == Index) {
+    OtherPart.addNewAccess(Loc, Access);
+    return;
+  }
+
+  switch (Access) {
+  case AkWrite: {
+    if (OtherPart.Kind & (TrRead | TrWrite))
+      MainPart = OtherPart;
+
+    OtherPart = TraversalResult(Index, Loc, Access);
+    break;
+  }
+  case AkRead: {
+    if (!(MainPart.Kind & TrWrite) &&
+        (OtherPart.Kind & TrWrite))
+      MainPart = OtherPart;
+    OtherPart = TraversalResult(Index, Loc, Access);
+    break;
+  }
+  default: {
+    break;
+  }
+  }
+}
+
+bool TraversalAggregation::isValid() const {
+  return MainPart.Kind != TrInvalid;
+}
+
+DeclarationName TraversalAggregation::getDeclName() const { return DeclName; }
+
+bool TraversalAggregation::hasTwoAccesses() const {
+  return OtherPart.Kind != TrInvalid;
+}
+
+bool TraversalAggregation::hasConflictingOperations() const {
+  return hasTwoAccesses() &&
+         ((MainPart.Kind | OtherPart.Kind) & TrWrite);
+}
+
+bool TraversalAggregation::shouldBeReported() const {
+  return hasConflictingOperations();
+}
+
+TraversalResult::TraversalResult() : IndexCreated(-1), Kind(TrInvalid) {}
+
+TraversalResult::TraversalResult(int Index, SourceLocation Location,
+                                 AccessKind Access)
+    : IndexCreated(Index), Kind(akToTr(Access)) {
+  Loc[Access] = Location;
+}
+
+void TraversalResult::addNewAccess(SourceLocation NewLoc, AccessKind Access) {
+  Kind |= 1 << Access;
+  Loc[Access] = NewLoc;
+}
+
+ObjectTraversalAggregation::ObjectTraversalAggregation(
+    DeclarationName Name, SourceLocation Loc, FieldIndexArray FieldIndices,
+    AccessKind Access, int Index)
+    : DeclName(Name), AccessTree(TraversalAggregation()) {
+  addFieldRW(Loc, FieldIndices, Access, Index);
+}
+
+void ObjectTraversalAggregation::addFieldRW(SourceLocation Loc,
+                                            FieldIndexArray FieldIndices,
+                                            AccessKind Access, int Index) {
+  ObjectAccessTree *CurrentNode = &AccessTree;
+  for (FieldIndex FIndex : FieldIndices) {
+    bool IsUnion = (FIndex & FiUnion) != 0;
+    uint16_t FieldKey = FIndex & ~FiUnion;
+
+    ObjectAccessTree *PrevNode = CurrentNode;
+    ObjectAccessTree::FieldMap::iterator It =
+        CurrentNode->Fields.find(FieldKey);
+
+    if (It == CurrentNode->Fields.end()) {
+      CurrentNode =
+          new ObjectAccessTree(IsUnion ? CurrentNode->UnionTemporalAccesses
+                                       : CurrentNode->OwnAccesses);
+      PrevNode->Fields[FieldKey] =
+          std::unique_ptr<ObjectAccessTree>(CurrentNode);
+    } else {
+      CurrentNode = It->second.get();
+    }
+
+    if (IsUnion) {
+      if (!PrevNode->IsUnion) {
+        PrevNode->IsUnion = IsUnion; // Setting the parent of the
+                                     // field instead of the field
+                                     // itself.
+        PrevNode->UnionTemporalAccesses = PrevNode->OwnAccesses;
+      }
+      PrevNode->addFieldToAllExcept(FieldKey, Loc, Access, Index);
+    }
+  }
+  CurrentNode->addFieldToAll(Loc, Access, Index);
+}
+
+bool ObjectTraversalAggregation::shouldBeReported() const {
+  return shouldBeReportedRec(&AccessTree);
+}
+
+bool ObjectTraversalAggregation::shouldBeReportedRec(
+    const ObjectAccessTree *Node) const {
+  if (Node->OwnAccesses.hasConflictingOperations())
+    return true;
+
+  ObjectAccessTree::FieldMap::const_iterator FieldIt = Node->Fields.begin();
+  ObjectAccessTree::FieldMap::const_iterator FieldsEnd = Node->Fields.end();
+  for (; FieldIt != FieldsEnd; FieldIt++)
+    if (shouldBeReportedRec(FieldIt->second.get()))
+      return true;
+
+  return false;
+}
+
+DeclarationName ObjectTraversalAggregation::getDeclName() const {
+  return DeclName;
+}
+
+ObjectAccessTree::ObjectAccessTree(TraversalAggregation Own)
+    : OwnAccesses(Own), IsUnion(false) {}
+
+void ObjectAccessTree::addFieldToAll(SourceLocation Loc, AccessKind Access,
+                                     int Index) {
+  OwnAccesses.addGlobalRW(Loc, Access, Index);
+  UnionTemporalAccesses.addGlobalRW(Loc, Access, Index);
+
+  FieldMap::iterator FieldIt = Fields.begin();
+  FieldMap::iterator FieldsEnd = Fields.end();
+
+  for (; FieldIt != FieldsEnd; FieldIt++)
+    FieldIt->second->addFieldToAll(Loc, Access, Index);
+}
+
+void ObjectAccessTree::addFieldToAllExcept(uint16_t ExceptIndex,
+                                           SourceLocation Loc,
+                                           AccessKind Access, int Index) {
+
+  UnionTemporalAccesses.addGlobalRW(Loc, Access, Index);
+
+  FieldMap::const_iterator FieldIt = Fields.begin();
+  FieldMap::iterator FieldsEnd = Fields.end();
+
+  for (; FieldIt != FieldsEnd; FieldIt++)
+    if (FieldIt->first != ExceptIndex)
+      FieldIt->second->addFieldToAll(Loc, Access, Index);
+}
+
+} // namespace clang::tidy::bugprone
diff --git 
a/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.h 
b/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.h
new file mode 100644
index 0000000000000..64947efe4b306
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsequencedGlobalAccessesCheck.h
@@ -0,0 +1,35 @@
+//===--- UnsequencedGlobalAccessesCheck.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_UNSEQUENCEDGLOBALACCESSES\
+CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSEQUENCEDGLOBALACCESSES\
+CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds conflicting accesses on global variables.
+class UnsequencedGlobalAccessesCheck : public ClangTidyCheck {
+public:
+  UnsequencedGlobalAccessesCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+private:
+  bool HandleMutableFunctionParametersAsWrites;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif
diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp 
b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
index 6614b10d4ce40..135aae7e54df8 100644
--- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -19,6 +19,7 @@
 #include "../bugprone/SuspiciousMemoryComparisonCheck.h"
 #include "../bugprone/UnhandledSelfAssignmentCheck.h"
 #include "../bugprone/UnsafeFunctionsCheck.h"
+#include "../bugprone/UnsequencedGlobalAccessesCheck.h"
 #include "../bugprone/UnusedReturnValueCheck.h"
 #include "../concurrency/ThreadCanceltypeAsynchronousCheck.h"
 #include "../google/UnnamedNamespaceInHeaderCheck.h"
@@ -261,6 +262,9 @@ class CERTModule : public ClangTidyModule {
     CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
     CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
         "cert-err61-cpp");
+    // EXP
+    CheckFactories.registerCheck<bugprone::UnsequencedGlobalAccessesCheck>(
+        "cert-exp50-cpp");
     // MEM
     CheckFactories.registerCheck<DefaultOperatorNewAlignmentCheck>(
         "cert-mem57-cpp");
@@ -299,6 +303,8 @@ class CERTModule : public ClangTidyModule {
         "cert-err33-c");
     CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
     // EXP
+    CheckFactories.registerCheck<bugprone::UnsequencedGlobalAccessesCheck>(
+        "cert-exp30-c");
     CheckFactories.registerCheck<bugprone::SuspiciousMemoryComparisonCheck>(
         "cert-exp42-c");
     // FLP
diff --git a/clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h 
b/clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h
new file mode 100644
index 0000000000000..992330d6f0a77
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h
@@ -0,0 +1,195 @@
+//===--- ExecutionVisitor.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_UTILS_EXECUTIONVISITOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXECUTIONVISITOR_H
+
+#include "clang/AST/RecursiveASTVisitor.h"
+
+namespace clang::tidy::utils {
+
+/// Helper class that can be used to traverse all statements (including
+/// expressions) that can execute while executing a given statement.
+template <typename T> class ExecutionVisitor : public RecursiveASTVisitor<T> {
+public:
+  ExecutionVisitor() : IsInFunction(false) {}
+
+protected:
+  void traverseExecution(Stmt *S) {
+    FunctionsToBeChecked.clear();
+    IsInFunction = false;
+    RecursiveASTVisitor<T>::TraverseStmt(S);
+
+    // We keep a list of functions to be checked during traversal so that they
+    // are not checked multiple times. If this weren't the case, we would get
+    // infinite recursion on recursive functions.
+    traverseFunctionsToBeChecked();
+  }
+
+  bool isInFunction() const { return IsInFunction; }
+
+  void checkFunctionLater(const FunctionDecl *FD) {
+    if (!FD->hasBody())
+      return;
+
+    for (const FunctionDecl *Fun : FunctionsToBeChecked)
+      if (Fun->getDeclName() == FD->getDeclName())
+        return;
+
+    FunctionsToBeChecked.push_back(FD);
+  }
+
+  void checkDestructorLater(const CXXRecordDecl *D) {
+    if (!D->hasDefinition() || D->hasIrrelevantDestructor())
+      return;
+
+    const CXXMethodDecl *Destructor = D->getDestructor();
+    checkFunctionLater(static_cast<const FunctionDecl *>(Destructor));
+
+    // We recurse into struct/class members and base classes, as their
+    // destructors will run as well.
+
+    for (const FieldDecl *F : D->fields()) {
+      const Type *FieldType = F->getType().getTypePtrOrNull();
+      if (!FieldType) {
+        continue;
+      }
+
+      const CXXRecordDecl *FieldRecordDecl = FieldType->getAsCXXRecordDecl();
+      if (!FieldRecordDecl)
+        continue;
+
+      checkDestructorLater(FieldRecordDecl);
+    }
+
+    for (const CXXBaseSpecifier Base : D->bases()) {
+      const Type *BaseType = Base.getType().getTypePtrOrNull();
+      if (!BaseType)
+        continue;
+
+      const CXXRecordDecl *BaseRecordDecl = BaseType->getAsCXXRecordDecl();
+      if (!BaseRecordDecl)
+        continue;
+
+      checkDestructorLater(BaseRecordDecl);
+    }
+  }
+
+public:
+  bool VisitCallExpr(CallExpr *CE) {
+    if (!isa_and_nonnull<FunctionDecl>(CE->getCalleeDecl()))
+      return true;
+
+    const auto *FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
+
+    if (const auto *DD = dyn_cast<CXXDestructorDecl>(FD)) {
+      const CXXRecordDecl *Parent = DD->getParent();
+      checkDestructorLater(Parent);
+      return true;
+    }
+
+    checkFunctionLater(FD);
+    return true;
+  }
+
+  bool VisitVarDecl(VarDecl *VD) {
+    if (VD->isStaticLocal())
+      return true;
+
+    const Type *DT = VD->getType().getTypePtrOrNull();
+    if (!DT)
+      return true;
+
+    const CXXRecordDecl *DeleteDecl = DT->getAsCXXRecordDecl();
+    if (!DeleteDecl)
+      return true;
+
+    checkDestructorLater(DeleteDecl);
+    return true;
+  }
+
+  bool VisitCXXConstructExpr(CXXConstructExpr *CE) {
+    const CXXConstructorDecl *CD = CE->getConstructor();
+
+    checkFunctionLater(static_cast<const FunctionDecl *>(CD));
+
+    // If we are traversing a function, then all the temporary and 
non-temporary
+    // objects will have their destructor called at the end of the scope. So
+    // better traverse the destructors as well.
+    if (isInFunction()) {
+      const CXXRecordDecl *RD = CD->getParent();
+      checkDestructorLater(RD);
+    }
+    return true;
+  }
+
+  bool VisitCXXDeleteExpr(CXXDeleteExpr *DE) {
+    const Type *DeleteType = DE->getDestroyedType().getTypePtrOrNull();
+    if (!DeleteType)
+      return true;
+
+    const CXXRecordDecl *DeleteDecl = DeleteType->getAsCXXRecordDecl();
+    if (!DeleteDecl)
+      return true;
+
+    checkDestructorLater(DeleteDecl);
+    return true;
+  }
+
+  bool VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) {
+    return false;
+  }
+  bool VisitOffsetOfExpr(OffsetOfExpr *OE) { return false; }
+
+private:
+  void traverseFunctionsToBeChecked() {
+    IsInFunction = true;
+
+    // We could find more functions to be checked while checking functions.
+    // Because a simple iterator could get invalidated, we index into the 
array.
+    for (size_t I = 0; I < FunctionsToBeChecked.size(); ++I) {
+      const FunctionDecl *Func = FunctionsToBeChecked[I];
+
+      if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(Func))
+        for (const CXXCtorInitializer *Init : Constructor->inits())
+          RecursiveASTVisitor<T>::TraverseStmt(Init->getInit());
+
+      // Look at the function parameters as well. They get destroyed at the end
+      // of the scope.
+      for (const ParmVarDecl *Param : Func->parameters()) {
+        const Type *ParamType = Param->getType().getTypePtrOrNull();
+        if (!ParamType)
+          continue;
+
+        const CXXRecordDecl *TypeDecl = ParamType->getAsCXXRecordDecl();
+        if (!TypeDecl)
+          continue;
+
+        checkDestructorLater(TypeDecl);
+      }
+
+      // The hasBody check should happen before we add the function to the
+      // array.
+      assert(Func->hasBody());
+      RecursiveASTVisitor<T>::TraverseStmt(Func->getBody());
+    }
+  }
+
+  // Will be true if we are traversing function/constructor/destructor bodies
+  // that can be called from the original starting point of the traversal.
+  bool IsInFunction;
+
+  // We check inside functions only if the functions hasn't already been 
checked
+  // during the current traversal. We use this array to check if the function 
is
+  // already registered to be checked.
+  llvm::SmallVector<const FunctionDecl *> FunctionsToBeChecked;
+};
+
+} // namespace clang::tidy::utils
+
+#endif
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 882ee0015df17..b65b547319b0b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -130,6 +130,12 @@ New checks
   Finds setter-like member functions that take a pointer parameter and set a
   reference member of the same class with the pointed value.
 
+- New :doc: `bugprone-unsequenced-global-accesses
+  <clang-tidy/checks/bugprone/unsequenced-global-accesses>` check.
+
+  Finds unsequenced actions (i.e. unsequenced write and read/write) 
+  on global variables nested in functions in the same translation unit.
+
 - New :doc:`bugprone-unintended-char-ostream-output
   <clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
 
@@ -151,6 +157,14 @@ New checks
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
+- New `cert-exp30-c <cert/exp30-c>` alias for 
+  `bugprone-unsequenced-global-accesses
+  <clang-tidy/checks/bugprone/unsequenced-global-accesses>`.
+
+- New `cert-exp50-cpp <cert/exp50-cpp>` alias for 
+  `bugprone-unsequenced-global-accesses
+  <clang-tidy/checks/bugprone/unsequenced-global-accesses>`.
+
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsequenced-global-accesses.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsequenced-global-accesses.rst
new file mode 100644
index 0000000000000..fa2730452825b
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unsequenced-global-accesses.rst
@@ -0,0 +1,86 @@
+.. title:: clang-tidy - bugprone-unsequenced-global-accesses
+
+bugprone-unsequenced-global-accesses
+====================================
+
+Finds unsequenced actions (i.e. unsequenced write and read/write)
+on global variables nested in functions in the same translation unit.
+
+Modifying twice or reading and modifying a memory location without a
+defined sequence of the operations is either undefined behavior or has
+unspecified order. This check is similar to the `-Wunsequenced` Clang warning,
+however it only looks at global variables and therefore can find unsequenced
+actions recursively inside function calls as well.
+
+For example code-block:: c++
+
+    int a = 0;
+    int b = (a++) - a; // This is flagged by -Wunsequenced.
+
+However global variables allow for more complex scenarios that
+`-Wunsequenced` doesn't detect. E.g. code-block:: c++
+
+    int globalVar = 0;
+
+    int incFun() {
+      globalVar++;
+      return globalVar;
+    }
+
+    int main() {
+      return globalVar + incFun(); // This is not detected by -Wunsequenced.
+    }
+
+This check attempts to detect such cases. It recurses into functions that are
+inside the same translation unit. The flagged cases can overlap with
+`-Wunsequenced`. Global unions and structs are also handled.
+For example code-block:: c++
+    
+    typedef struct {
+        int A;
+        float B;
+    } IntAndFloat;
+
+    IntAndFloat GlobalIF;
+
+    int globalIFGetSum() {
+        int sum = GlobalIF.A + (int)GlobalIF.B;
+        GlobalIF = (IntAndFloat){};
+        return sum;
+    }
+
+    int main() {
+        // The following printf could give different results on different
+        // compilers.
+        printf("sum: %i, int: %i", globalIFGetSum(), GlobalIF.A);
+    }
+
+Options
+~~~~~~~
+
+.. option:: HandleMutableFunctionParametersAsWrites
+
+When `true`, treat function calls with mutable reference or pointer parameters
+as writes to the parameter.
+
+The default value is `false`.
+
+For example, the following code block will get flagged if
+`HandleMutableFunctionParametersAsWrites` is `true` code-block:: c++
+
+    void func(int& a);
+    int globalVar;
+
+    int main() {
+        int a = globalVar + func(globalVar);
+        // func could write to globalVar here
+    }
+
+When `HandleMutableFunctionParametersAsWrites` is set to `true`, the
+``func(globalVar)`` call is treated as a write to `globalVar`. Because no
+sequencing is defined for the `+` operator, a write to `globalVar`
+inside `func`, would be undefined behavior.
+
+When `HandleMutableFunctionParametersAsWrites` is set to `false` the expression
+does not get flagged as the call expression is only treated as a read from
+`globalVar`.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/exp30-c.rst 
b/clang-tools-extra/docs/clang-tidy/checks/cert/exp30-c.rst
new file mode 100644
index 0000000000000..3854b6596e2cc
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/exp30-c.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-exp30-c
+.. meta::
+   :http-equiv=refresh: 5;URL=../bugprone/unsequenced-global-accesses.html
+
+cert-exp30-c
+==============
+
+The `cert-exp30-c` check is an alias, please see
+:doc:`bugprone-unsequenced-global-accesses 
<../bugprone/unsequenced-global-accesses>`
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert/exp50-cpp.rst 
b/clang-tools-extra/docs/clang-tidy/checks/cert/exp50-cpp.rst
new file mode 100644
index 0000000000000..342b29a26ed98
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cert/exp50-cpp.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-exp50-cpp
+.. meta::
+   :http-equiv=refresh: 5;URL=../bugprone/unsequenced-global-accesses.html
+
+cert-con54-cpp
+==============
+
+The `cert-exp50-cpp` check is an alias, please see
+:doc:`bugprone-unsequenced-global-accesses 
<../bugprone/unsequenced-global-accesses>`
+for more information.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5a79d61b1fd7e..ff689402615ce 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -87,6 +87,7 @@ Clang-Tidy Checks
    :doc:`bugprone-capturing-this-in-member-variable 
<bugprone/capturing-this-in-member-variable>`,
    :doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
    :doc:`bugprone-chained-comparison <bugprone/chained-comparison>`,
+   :doc:`bugprone-conflicting-global-accesses 
<bugprone/conflicting-global-accesses>`,
    :doc:`bugprone-compare-pointer-to-member-virtual-function 
<bugprone/compare-pointer-to-member-virtual-function>`,
    :doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, 
"Yes"
    :doc:`bugprone-crtp-constructor-accessibility 
<bugprone/crtp-constructor-accessibility>`, "Yes"
@@ -173,9 +174,11 @@ Clang-Tidy Checks
    :doc:`cert-env33-c <cert/env33-c>`,
    :doc:`cert-err33-c <cert/err33-c>`,
    :doc:`cert-err34-c <cert/err34-c>`,
+   :doc:`cert-exp30-c <cert/exp30-c>`,
    :doc:`cert-err52-cpp <cert/err52-cpp>`,
    :doc:`cert-err58-cpp <cert/err58-cpp>`,
    :doc:`cert-err60-cpp <cert/err60-cpp>`,
+   :doc:`cert-exp50-cpp <cert/exp50-cpp>`,
    :doc:`cert-flp30-c <cert/flp30-c>`,
    :doc:`cert-mem57-cpp <cert/mem57-cpp>`,
    :doc:`cert-msc50-cpp <cert/msc50-cpp>`,
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsequenced-global-accesses.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsequenced-global-accesses.cpp
new file mode 100644
index 0000000000000..901b19a6fb5a7
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unsequenced-global-accesses.cpp
@@ -0,0 +1,601 @@
+// RUN: %check_clang_tidy -std=c++98 -check-suffixes=,PRE-CPP11,PRE-CPP17 %s 
bugprone-unsequenced-global-accesses %t
+// RUN: %check_clang_tidy -std=c++11,c++14 
-check-suffixes=,POST-CPP11,PRE-CPP17 %s bugprone-unsequenced-global-accesses %t
+// RUN: %check_clang_tidy -std=c++17-or-later -check-suffixes=,POST-CPP11 %s 
bugprone-unsequenced-global-accesses %t
+// RUN: %check_clang_tidy -std=c++17-or-later 
-check-suffixes=,POST-CPP11,PARAM %s bugprone-unsequenced-global-accesses %t 
-config="{CheckOptions: 
{bugprone-unsequenced-global-accesses.HandleMutableFunctionParametersAsWrites: 
true}}"
+
+#if __cplusplus > 199711L
+    // Used to exclude code that would give compiler errors on older standards.
+    #define POST_CPP11
+#endif
+
+int GlobalVarA;
+
+int incGlobalVarA(void) {
+    GlobalVarA++;
+    return 0;
+}
+
+int getGlobalVarA(void) {
+    return GlobalVarA;
+}
+
+int undefinedFunc1(int);
+
+int testFunc1(void) {
+
+    int B = getGlobalVarA() + incGlobalVarA();
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable GlobalVarA
+    (void)B;
+
+    return GlobalVarA + incGlobalVarA();
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: read/write conflict on global 
variable GlobalVarA
+
+    return GlobalVarA + undefinedFunc1(incGlobalVarA());
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: read/write conflict on global 
variable GlobalVarA
+
+}
+
+int addAll(int A, int B, int C, int D) {
+    return A + B + C + D;
+}
+
+int testFunc2(void) {
+    int B;
+    (void)B;
+    // Make sure the order does not affect the outcome
+
+    B = getGlobalVarA() + (GlobalVarA++);
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: read/write conflict on global 
variable GlobalVarA
+
+    B = (GlobalVarA++) + getGlobalVarA();
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: read/write conflict on global 
variable GlobalVarA
+
+    B = incGlobalVarA() + GlobalVarA;
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: read/write conflict on global 
variable GlobalVarA
+
+    B = addAll(GlobalVarA++, getGlobalVarA(), 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: read/write conflict on global 
variable GlobalVarA
+
+    B = addAll(getGlobalVarA(), GlobalVarA++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: read/write conflict on global 
variable GlobalVarA
+
+    // This is already checked by the unsequenced clang warning, so we don't
+    // want to warn about this.
+    return GlobalVarA + (++GlobalVarA);
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: read/write conflict on global 
variable GlobalVarA
+}
+
+int testFunc3(void) {
+
+    // Make sure double reads are not flagged
+    int B = GlobalVarA + GlobalVarA; (void)B;
+    B = GlobalVarA + getGlobalVarA();
+
+    return GlobalVarA - GlobalVarA;
+}
+
+bool testFunc4(void) {
+
+    // Make sure || and && operators are not flagged
+    bool B = GlobalVarA || (GlobalVarA++);
+    if(GlobalVarA && (GlobalVarA--)) {
+
+        B = GlobalVarA || (GlobalVarA++) + getGlobalVarA();
+        // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: read/write conflict on 
global variable GlobalVarA
+
+        return (++GlobalVarA) || B || getGlobalVarA();
+    }
+
+    int C = GlobalVarA << incGlobalVarA(); (void)C;
+    // CHECK-MESSAGES-PRE-CPP17: :[[@LINE-1]]:13: warning: read/write conflict 
on global variable GlobalVarA
+
+    return false;
+}
+
+int incArg(int& P) {
+    P++;
+    return 0;
+}
+
+int incArgPtr(int* P) {
+    (*P)++;
+    return 0;
+}
+
+int incAndAddFn(int* A, int B) {
+    return (*A)++ + B;
+}
+
+int incAndAddBothPtrFn(int *A, int* B) {
+    return (*A)++ + (*B)++;
+}
+
+int testFunc5() {
+
+    // Also check if statements
+
+    if(GlobalVarA > incGlobalVarA()) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: read/write conflict on global 
variable GlobalVarA
+
+        return 1;
+    }
+
+    if(addAll(GlobalVarA, incArg(GlobalVarA), 0, 0)) {
+    // CHECK-MESSAGES-PARAM: :[[@LINE-1]]:8: warning: read/write conflict on 
global variable GlobalVarA
+        return 1;
+    }
+
+    if(addAll(GlobalVarA, incArgPtr(&GlobalVarA), 0, 0)) {
+    // CHECK-MESSAGES-PARAM: :[[@LINE-1]]:8: warning: read/write conflict on 
global variable GlobalVarA
+        return 2;
+    }
+
+    if(addAll(GlobalVarA, 0, incGlobalVarA(), 0)) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: read/write conflict on global 
variable GlobalVarA
+        return 2;
+    }
+
+    // Shouldn't warn here, as the value gets copied before the
+    // addition/increment happens.
+    int C = incAndAddFn(&GlobalVarA, GlobalVarA); (void)C;
+
+    // -Wunsequenced doesn't warn here. Neither do we. Not sure if we should
+    // cover this case.
+    incAndAddBothPtrFn(&GlobalVarA, &GlobalVarA);
+
+    return 0;
+}
+
+void *memset(void* S, int C, unsigned int N);
+int* GlobalPtrA;
+
+int* incGlobalPtrA() {
+    GlobalPtrA++;
+    return GlobalPtrA;
+}
+
+typedef struct TwoStringStruct {
+    char* A;
+    char* B;
+} TwoStringStruct;
+
+TwoStringStruct* TwoStringPtr;
+
+struct TwoStringStruct* incTwoStringPtr() {
+    TwoStringPtr++;
+    return TwoStringPtr;
+}
+
+int testFunc6() {
+
+    // Shouldn't warn here as the write takes place after the expression is
+    // evaluated.
+    GlobalVarA = GlobalVarA + 1;
+    GlobalVarA = incGlobalVarA();
+
+    // Also check the assignment expression, array element assignment, and
+    // pointer dereference lvalues.
+    int A = (GlobalVarA = 1) + incGlobalVarA(); (void)A;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable GlobalVarA
+    
+    int Array[] = {1, 2, 3};
+    Array[GlobalVarA] = incGlobalVarA();
+    // CHECK-MESSAGES-PRE-CPP17: :[[@LINE-1]]:5: warning: read/write conflict 
on global variable GlobalVarA
+    
+    *(Array + GlobalVarA) = incGlobalVarA();
+    // CHECK-MESSAGES-PRE-CPP17: :[[@LINE-1]]:5: warning: read/write conflict 
on global variable GlobalVarA
+
+    *(Array + GlobalVarA) = getGlobalVarA();
+    // This is fine
+    
+    // Should also check the array subscript operator
+
+    int B = (Array + GlobalVarA)[incGlobalVarA()]; (void)B;
+    // CHECK-MESSAGES-PRE-CPP17: :[[@LINE-1]]:13: warning: read/write conflict 
on global variable GlobalVarA
+
+    int C = (Array + GlobalVarA)[getGlobalVarA()]; (void)C;
+    // This is also fine
+    
+    // Shouldn't warn here as the clang warning takes care of it.
+    return addAll(GlobalVarA, getGlobalVarA(), GlobalVarA++, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: read/write conflict on global 
variable GlobalVarA
+
+    // Shouldn't warn here as the ampersand operator doesn't read the variable.
+    return addAll(&GlobalVarA == &A ? 1 : 0, 1, incGlobalVarA(), 0);
+
+    memset(incGlobalPtrA(), 0, *GlobalPtrA);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable GlobalPtrA
+
+    // Shouldn't warn here as sizeof doesn't read the value.
+    memset(incGlobalPtrA(), 0, sizeof(*GlobalPtrA));
+
+    memset(incTwoStringPtr(), 0, (int)TwoStringPtr->A[0]);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable TwoStringPtr
+}
+
+class TestClass1 {
+public:
+    static int StaticVar1;
+
+    int incStaticVar1() {
+        StaticVar1++;
+        return 0;
+    }
+
+    int getStaticVar1() {
+        return StaticVar1;
+    }
+
+    int testClass1MemberFunc1() {
+        
+        return incStaticVar1() + getStaticVar1();
+        // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: read/write conflict on 
global variable StaticVar1
+
+    }
+
+    TestClass1 operator++() {
+        incStaticVar1();
+        return *this;
+    }
+
+    operator int() {
+        return StaticVar1;
+    }
+
+    int operator[](int N) {
+        return N;
+    }
+};
+
+void testFunc7() {
+    TestClass1 Obj;
+    addAll(TestClass1::StaticVar1, Obj.incStaticVar1(), 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable StaticVar1
+    addAll(TestClass1::StaticVar1, (Obj.incStaticVar1()), 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable StaticVar1
+    addAll(TestClass1::StaticVar1, (Obj.incStaticVar1(), 0), 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable StaticVar1
+    addAll(TestClass1::StaticVar1, ++Obj, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable StaticVar1
+    
+    TestClass1 Objects[3];
+    int A = (Objects + Objects[0].getStaticVar1())[TestClass1::StaticVar1++]; 
(void)A;
+    // CHECK-MESSAGES-PRE-CPP17: :[[@LINE-1]]:13: warning: read/write conflict 
on global variable StaticVar1
+}
+
+struct {
+    int VarA;
+    int VarB;
+    struct {
+        int VarC;
+        int VarD;
+    } StructA;
+} GlobalStruct;
+
+struct {
+    int VarA;
+    union {
+        struct {
+            int VarB;
+            int VarC;
+        } StructA;
+        int VarD;
+    } UnionA;
+    int VarE;
+} ComplexGlobalStruct;
+
+struct QuiteComplexStruct {
+    int VarA;
+    union {
+        union {
+            int VarB;
+            int VarC;
+            struct QuiteComplexStruct* PtrA;
+        } UnionB;
+        int VarD;
+    } UnionA;
+    int VarE;
+} QuiteComplexGlobalStruct;
+
+union {
+    int VarA;
+    struct {
+        int VarB, VarC;
+    } StructA;
+} GlobalUnion;
+
+
+void testFunc8() {
+
+    // Check if unions and structs are handled properly
+
+    addAll(GlobalStruct.VarA, GlobalStruct.VarB++, 0, 0);
+    addAll(GlobalStruct.StructA.VarD, GlobalStruct.VarA++, 0, 0);
+    addAll(GlobalStruct.StructA.VarC, GlobalStruct.StructA.VarD++, 
GlobalStruct.VarB++, GlobalStruct.VarA++);
+
+    addAll(GlobalStruct.VarA, (GlobalStruct.VarA++, 0), 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object GlobalStruct
+    addAll(ComplexGlobalStruct.UnionA.VarD, 
ComplexGlobalStruct.UnionA.StructA.VarC++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object ComplexGlobalStruct
+    addAll(ComplexGlobalStruct.UnionA.StructA.VarB, 
ComplexGlobalStruct.UnionA.VarD++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object ComplexGlobalStruct
+
+    addAll(ComplexGlobalStruct.UnionA.StructA.VarB, 
ComplexGlobalStruct.UnionA.StructA.VarC++, 0, 0);
+
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.VarC, 
QuiteComplexGlobalStruct.UnionA.VarD++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object QuiteComplexGlobalStruct
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.VarC, 
QuiteComplexGlobalStruct.UnionA.UnionB.VarB++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object QuiteComplexGlobalStruct
+
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.VarC, 
QuiteComplexGlobalStruct.UnionA.UnionB.VarB++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object QuiteComplexGlobalStruct
+
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.PtrA->VarA, 
QuiteComplexGlobalStruct.UnionA.UnionB.VarB++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object QuiteComplexGlobalStruct
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.PtrA->VarA, 
QuiteComplexGlobalStruct.VarA++, 0, 0);
+
+    addAll(QuiteComplexGlobalStruct.UnionA.UnionB.PtrA->VarA, 
(long)QuiteComplexGlobalStruct.UnionA.UnionB.PtrA++, 0, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object QuiteComplexGlobalStruct
+
+    addAll(GlobalUnion.VarA, 0, GlobalUnion.StructA.VarB++, 0);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on the 
field of the global object GlobalUnion
+    
+#ifdef POST_CPP11
+    addAll(GlobalStruct.StructA.VarD, (GlobalStruct.StructA = {}, 0), 0, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object GlobalStruct
+    addAll(GlobalStruct.StructA.VarC, (GlobalStruct = {}, 0), 0, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object GlobalStruct
+
+    addAll(GlobalStruct.VarA, (GlobalStruct.StructA = {}, 0), 0, 0);
+
+    addAll((GlobalStruct.StructA = {}, 1), (GlobalStruct = {}, 0), 0, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object GlobalStruct
+    addAll((GlobalStruct.StructA = {}, 1), (GlobalStruct.VarA++, 0), 
GlobalStruct.StructA.VarD, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object GlobalStruct
+ 
+    addAll((ComplexGlobalStruct.UnionA = {}, 0), 
ComplexGlobalStruct.UnionA.VarD++, 0, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object ComplexGlobalStruct
+    
+    addAll(ComplexGlobalStruct.UnionA.StructA.VarB, 
(ComplexGlobalStruct.UnionA.StructA = {}, 0), 0, 0);
+    // CHECK-MESSAGES-POST-CPP11: :[[@LINE-1]]:5: warning: read/write conflict 
on the field of the global object ComplexGlobalStruct
+#endif
+}
+
+
+int GlobalVarB;
+
+int incGlobalVarB() {
+    ++GlobalVarB;
+    return GlobalVarB;
+}
+
+struct TwoValue {
+    int A, B;
+};
+
+// Check initializers
+void testFunc9() {
+    int Arr[] = { incGlobalVarB(), incGlobalVarB() }; (void)Arr;
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:17: warning: read/write conflict 
on global variable GlobalVarB
+    
+    TwoValue Ts1 = { incGlobalVarB(), GlobalVarB }; (void)Ts1;
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:20: warning: read/write conflict 
on global variable GlobalVarB
+    
+    Ts1 = (TwoValue){ GlobalVarB, incGlobalVarB() };
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:21: warning: read/write conflict 
on global variable GlobalVarB
+    
+    TwoValue TsArr[] = { { incGlobalVarB(), 0 }, { 0, GlobalVarB } }; 
(void)TsArr;
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:24: warning: read/write conflict 
on global variable GlobalVarB
+    
+    TwoValue TsArr2[4] = { [1].A = incGlobalVarB(), [3] = { .B = 0, .A = 
GlobalVarB } }; (void)TsArr2;
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:26: warning: read/write conflict 
on global variable GlobalVarB
+
+    TwoValue Ts2 = { .A = incGlobalVarB(), .B = GlobalVarB }; (void)Ts2;
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:20: warning: read/write conflict 
on global variable GlobalVarB
+}
+
+class InstanceCountedClass {
+public:
+    static int InstanceCount;
+    InstanceCountedClass() {
+        InstanceCount++;
+    }
+
+    ~InstanceCountedClass() {
+        InstanceCount--;
+    }
+};
+
+int InstanceCountedClass::InstanceCount = 0;
+
+class GlobalDefaultFieldClass {
+public:
+    GlobalDefaultFieldClass(int PA, int PB) : A(PA), B(PB) { (void)A; (void)B; 
}
+    GlobalDefaultFieldClass() : A(GlobalVarB), B(0) {}
+private:
+    int A, B;
+};
+
+class NestedInstanceCountedClass {
+    InstanceCountedClass C2;
+public:
+    NestedInstanceCountedClass();
+};
+
+class InstanceCountedBaseClass : public InstanceCountedClass {
+public:
+    InstanceCountedBaseClass();
+};
+
+class NestedGlobalDefaultFieldClass {
+    GlobalDefaultFieldClass G;
+};
+
+class GlobalDefaultFieldBaseClass : public GlobalDefaultFieldClass {
+};
+
+class DestructCountedClass {
+public:
+    static int DestructCount;
+    ~DestructCountedClass() {
+        DestructCount++;
+    }
+
+    operator int() {
+        return 1;
+    }
+};
+
+int DestructCountedClass::DestructCount = 0;
+
+int createAndDestructTestClass4() {
+    NestedInstanceCountedClass Test; (void)Test;
+    return InstanceCountedClass::InstanceCount;
+}
+
+template <class T>
+int destructParam(T Param) {
+    (void)Param;
+    return 0;
+}
+
+int temporaryDestroy() {
+    int K = 42 + DestructCountedClass(); 
+    // Destructor gets called here.
+    return K;
+}
+
+// Check constructors/destructors
+void testFunc10() {
+    InstanceCountedClass* TestArr[] = { new InstanceCountedClass(), new 
InstanceCountedClass() };
+    // CHECK-MESSAGES-PRE-CPP11: :[[@LINE-1]]:39: warning: read/write conflict 
on global variable InstanceCount
+    (void)TestArr;
+    
+    InstanceCountedClass TestArr2[2]; // I'm not sure about this. Is this 
sequenced?
+
+    InstanceCountedClass* NewTestArr = new InstanceCountedClass[2]; // Is this 
sequenced?
+    
+    GlobalDefaultFieldClass Simple1(GlobalVarB, incGlobalVarB());
+    // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: read/write conflict on global 
variable GlobalVarB
+
+    GlobalDefaultFieldClass Simple2(GlobalVarB, GlobalVarB);
+    // This is fine
+    
+    int A = InstanceCountedClass::InstanceCount + (delete TestArr[0], 1); 
(void)A;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable InstanceCount
+
+    int B = InstanceCountedClass::InstanceCount + 
(TestArr[1]->~InstanceCountedClass(), 1);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable InstanceCount
+    (void)B;
+    
+    NestedInstanceCountedClass* TestArr3 = new NestedInstanceCountedClass[2];
+
+    int C = InstanceCountedClass::InstanceCount + (delete &TestArr3[0], 1); 
(void)C;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable InstanceCount
+
+    int D = InstanceCountedClass::InstanceCount + 
(TestArr3[1].~NestedInstanceCountedClass(), 1); (void)D;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable InstanceCount
+
+    delete[] NewTestArr; // Same thing. Is this sequenced?
+    
+    bool E = InstanceCountedClass::InstanceCount == 
createAndDestructTestClass4(); (void)E;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: read/write conflict on global 
variable InstanceCount
+
+    InstanceCountedClass Test4;
+    int F  = InstanceCountedClass::InstanceCount + destructParam(Test4); 
(void)F;
+    // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: read/write conflict on global 
variable InstanceCount
+
+    int G = incGlobalVarB() + (GlobalDefaultFieldClass(), 1); (void)G;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable GlobalVarB
+    
+    InstanceCountedBaseClass* BadDestructor = new InstanceCountedBaseClass();
+    int H = InstanceCountedClass::InstanceCount + (delete BadDestructor, 1); 
(void)H;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable InstanceCount
+    int I = incGlobalVarB() + (NestedGlobalDefaultFieldClass(), 1); (void)I;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable GlobalVarB
+    
+    int J = incGlobalVarB() + (GlobalDefaultFieldBaseClass(), 1); (void)J;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable GlobalVarB
+    
+    int K = DestructCountedClass::DestructCount + DestructCountedClass(); 
(void)K;
+    // The temporary should be destroyed at the end of the full-expression, so
+    // this should be fine.
+
+    int L = DestructCountedClass::DestructCount + temporaryDestroy(); (void)L;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: read/write conflict on global 
variable DestructCount
+
+}
+
+void functionWithDefaultParam(int A, int B = GlobalVarB) {
+    (void)(A + B);
+}
+
+void testFunc11() {
+    functionWithDefaultParam(GlobalVarB);
+    // This is fine
+
+    functionWithDefaultParam(incGlobalVarB());
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable GlobalVarB
+
+    functionWithDefaultParam(incGlobalVarB(), GlobalVarB);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: read/write conflict on global 
variable GlobalVarB
+
+    functionWithDefaultParam(incGlobalVarB(), 0);
+    // This is fine
+}
+
+// Check default parameters
+class DefaultConstructorArgClass {
+public:
+    DefaultConstructorArgClass(void* A, int B, int C = GlobalVarB++);
+    DefaultConstructorArgClass(int A, int B, int C = incGlobalVarB());
+};
+
+void testFunc12() {
+    DefaultConstructorArgClass TestObj1(0, 0, 0);
+    // This is fine
+    
+    DefaultConstructorArgClass TestObj2((void*)0, GlobalVarB, incGlobalVarB());
+    // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: read/write conflict on global 
variable GlobalVarB
+
+    DefaultConstructorArgClass TestObj3((void*)0, GlobalVarB);
+    // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: read/write conflict on global 
variable GlobalVarB
+    
+    DefaultConstructorArgClass TestObj4(0, GlobalVarB);
+    // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: read/write conflict on global 
variable GlobalVarB
+}
+
+class ConstructorArgWritingClass {
+public:
+    ConstructorArgWritingClass(int& A) {
+        A++;
+    }
+    ConstructorArgWritingClass(int* A) {
+        (*A)++;
+    }
+
+    operator int() {
+        return 1;
+    }
+};
+
+// Check constructor argument writes
+int functionThatPassesReferenceToCtor() {
+    ConstructorArgWritingClass Ref(GlobalVarB);
+
+    return Ref;
+}
+
+int functionThatPassesPtrToCtor() {
+    ConstructorArgWritingClass Ptr(&GlobalVarB);
+
+    return Ptr;
+}
+
+void testFunc13() {
+    int A = functionThatPassesReferenceToCtor() + GlobalVarB; (void)A;
+    // CHECK-MESSAGES-PARAM: :[[@LINE-1]]:13: warning: read/write conflict on 
global variable GlobalVarB
+    
+    int B = functionThatPassesPtrToCtor() + GlobalVarB; (void)B;
+    // CHECK-MESSAGES-PARAM: :[[@LINE-1]]:13: warning: read/write conflict on 
global variable GlobalVarB
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to