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

>From 7b0272121336a36b68e8e2782bb2b6e7007c4c1b 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 Conflicting Global Accesses checker

    This checker 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 +
 .../ConflictingGlobalAccessesCheck.cpp        | 825 ++++++++++++++++++
 .../bugprone/ConflictingGlobalAccessesCheck.h |  35 +
 .../clang-tidy/cert/CERTTidyModule.cpp        |   6 +
 .../clang-tidy/utils/ExecutionVisitor.h       | 190 ++++
 clang-tools-extra/docs/ReleaseNotes.rst       |  14 +
 .../bugprone/conflicting-global-accesses.rst  |  72 ++
 .../docs/clang-tidy/checks/list.rst           |   3 +
 .../bugprone/conflicting-global-accesses.cpp  | 599 +++++++++++++
 10 files changed, 1748 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.h
 create mode 100644 clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/bugprone/conflicting-global-accesses.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/bugprone/conflicting-global-accesses.cpp

diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp 
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b780a85bdf3fe..aa966a2278ca9 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -20,6 +20,7 @@
 #include "CastingThroughVoidCheck.h"
 #include "ChainedComparisonCheck.h"
 #include "ComparePointerToMemberVirtualFunctionCheck.h"
+#include "ConflictingGlobalAccessesCheck.h"
 #include "CopyConstructorInitCheck.h"
 #include "CrtpConstructorAccessibilityCheck.h"
 #include "DanglingHandleCheck.h"
@@ -127,6 +128,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-chained-comparison");
     CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
         "bugprone-compare-pointer-to-member-virtual-function");
+    CheckFactories.registerCheck<ConflictingGlobalAccessesCheck>(
+        "bugprone-conflicting-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 e310ea9c94543..f04adca18a5ca 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -16,6 +16,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   CastingThroughVoidCheck.cpp
   ChainedComparisonCheck.cpp
   ComparePointerToMemberVirtualFunctionCheck.cpp
+  ConflictingGlobalAccessesCheck.cpp
   CopyConstructorInitCheck.cpp
   CrtpConstructorAccessibilityCheck.cpp
   DanglingHandleCheck.cpp
diff --git 
a/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.cpp
new file mode 100644
index 0000000000000..f48822ee8153b
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.cpp
@@ -0,0 +1,825 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ConflictingGlobalAccessesCheck.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,
+  AkUncheckedRead,
+  AkUncheckedWrite,
+  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;
+static constexpr TraversalResultKind TrUncheckedWrite = 1 << AkUncheckedWrite;
+
+// 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;
+  // Pairings that are not reportable: Read-Read, Read-Write,
+  // Read-UncheckedRead, Write-Write, UncheckedRead-UncheckedRead.
+
+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;
+  bool isReportedByWunsequenced() 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, bool StartUnchecked = false);
+
+  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);
+  bool VisitVarDecl(VarDecl *VD);
+
+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;
+
+  // Accesses that are inside functions or default parameters are not checked 
by
+  // -Wunsequenced, in those cases we consider everything unchecked.
+  bool IsGloballyUnchecked;
+
+  // Same as the HandleMutableFunctionParametersAsWrites option.
+  bool IsWritePossibleThroughFunctionParam;
+
+  void addGlobal(DeclarationName Name, SourceLocation Loc, bool IsWrite,
+                 bool IsUnchecked);
+  void addGlobal(const DeclRefExpr *DR, bool IsWrite, bool IsUnchecked);
+  void addField(DeclarationName Name, FieldIndexArray FieldIndices,
+                SourceLocation Loc, bool IsWrite, bool IsUnchecked);
+  bool handleModified(const Expr *Modified, bool IsUnchecked);
+  bool handleModifiedVariable(const DeclRefExpr *DE, bool IsUnchecked);
+  bool handleAccessedObject(const Expr *E, bool IsWrite, bool IsUnchecked);
+  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();
+}
+
+ConflictingGlobalAccessesCheck::ConflictingGlobalAccessesCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      HandleMutableFunctionParametersAsWrites(
+          Options.get("HandleMutableFunctionParametersAsWrites", false)) {}
+
+void ConflictingGlobalAccessesCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HandleMutableFunctionParametersAsWrites",
+                HandleMutableFunctionParametersAsWrites);
+}
+
+void ConflictingGlobalAccessesCheck::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 ConflictingGlobalAccessesCheck::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(), /*StartUnchecked*/ true);
+        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(), /*StartUnchecked*/ true);
+        continue;
+      }
+      Visitor.startTraversal(Arg);
+    }
+  }
+
+  const llvm::SmallVector<TraversalAggregation> &Globals =
+      Visitor.getGlobalsFound();
+
+  for (uint32_t I = 0; I < Globals.size(); I++) {
+    if (Globals[I].shouldBeReported()) {
+      diag(E->getBeginLoc(), "read/write conflict on global variable " +
+                                 Globals[I].getDeclName().getAsString());
+    }
+  }
+  const llvm::SmallVector<ObjectTraversalAggregation> &ObjectGlobals =
+      Visitor.getObjectGlobalsFound();
+  for (uint32_t I = 0; I < ObjectGlobals.size(); I++) {
+    if (ObjectGlobals[I].shouldBeReported()) {
+      diag(E->getBeginLoc(), "read/write conflict on the field of the global "
+                             "object " +
+                                 ObjectGlobals[I].getDeclName().getAsString());
+    }
+  }
+}
+
+GlobalRWVisitor::GlobalRWVisitor(bool IsWritePossibleThroughFunctionParam)
+    : TraversalIndex(0), IsGloballyUnchecked(false),
+      IsWritePossibleThroughFunctionParam(IsWritePossibleThroughFunctionParam) 
{
+}
+
+void GlobalRWVisitor::startTraversal(const Expr *E, bool StartUnchecked) {
+  TraversalIndex++;
+  IsGloballyUnchecked = StartUnchecked;
+
+  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, /*IsUnchecked*/ false);
+  }
+  if (isGlobalDecl(VD)) {
+    addGlobal(VD->getDeclName(), VD->getBeginLoc(), /*IsWrite*/ false,
+              /*IsUnchecked*/ false);
+    return true;
+  }
+  return true;
+}
+
+bool GlobalRWVisitor::VisitMemberExpr(MemberExpr *ME) {
+  return handleAccessedObject(ME, /*IsWrite*/ false, /*IsUnchecked*/ false);
+}
+
+bool GlobalRWVisitor::handleModifiedVariable(const DeclRefExpr *DR,
+                                             bool IsUnchecked) {
+  const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
+  if (!VD) {
+    return true;
+  }
+
+  if (isGlobalDecl(VD)) {
+    addGlobal(VD->getDeclName(), VD->getBeginLoc(), /*IsWrite*/ true,
+              IsUnchecked);
+    return false;
+  }
+  return true;
+}
+
+bool GlobalRWVisitor::handleAccessedObject(const Expr *E, bool IsWrite,
+                                           bool IsUnchecked) {
+  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,
+           IsUnchecked);
+  return false;
+}
+
+bool GlobalRWVisitor::handleModified(const Expr *Modified, bool IsUnchecked) {
+  assert(Modified);
+
+  if (isVariable(Modified)) {
+    return handleModifiedVariable(dyn_cast<DeclRefExpr>(Modified), 
IsUnchecked);
+  }
+
+  return handleAccessedObject(Modified, /*IsWrite*/ true, IsUnchecked);
+}
+
+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(), /*IsUnchecked*/ false);
+  }
+
+  // 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(), /*IsUnchecked*/ false);
+  }
+
+  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(), /*IsUnchecked*/ true)) {
+          continue;
+        }
+      }
+    }
+
+    if (const auto *RefType = dyn_cast_if_present<ReferenceType>(
+            FT->getParamType(I).getTypePtrOrNull())) {
+      if (RefType->getPointeeType().isConstQualified()) {
+        continue;
+      }
+
+      if (handleModified(Arg, /*IsUnchecked*/ true)) {
+        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);
+}
+
+bool GlobalRWVisitor::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;
+}
+
+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, bool IsUnchecked) {
+  AccessKind Access = (IsGloballyUnchecked || isInFunction() || IsUnchecked)
+                          ? (IsWrite ? AkUncheckedWrite : AkUncheckedRead)
+                          : (IsWrite ? AkWrite : AkRead);
+  for (uint32_t I = 0; I < GlobalsFound.size(); I++) {
+    if (GlobalsFound[I].getDeclName() == Name) {
+      GlobalsFound[I].addGlobalRW(Loc, Access, TraversalIndex);
+      return;
+    }
+  }
+
+  GlobalsFound.emplace_back(Name, Loc, Access, TraversalIndex);
+}
+
+void GlobalRWVisitor::addField(DeclarationName Name,
+                               FieldIndexArray FieldIndices, SourceLocation 
Loc,
+                               bool IsWrite, bool IsUnchecked) {
+  AccessKind Access = (IsGloballyUnchecked || isInFunction() || IsUnchecked)
+                          ? (IsWrite ? AkUncheckedWrite : AkUncheckedRead)
+                          : (IsWrite ? AkWrite : AkRead);
+  for (uint32_t I = 0; I < ObjectGlobalsFound.size(); I++) {
+    if (ObjectGlobalsFound[I].getDeclName() == Name) {
+      ObjectGlobalsFound[I].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 (isReportedByWunsequenced()) {
+    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;
+  }
+  // We know that the current state is not reported by -Wunsequenced, so
+  // either one of the parts has only unchecked accesses, or both parts have
+  // only reads.
+  switch (Access) {
+  case AkWrite:
+  case AkUncheckedWrite: {
+    if (OtherPart.Kind & (TrRead | TrWrite)) {
+      MainPart = OtherPart;
+    }
+    OtherPart = TraversalResult(Index, Loc, Access);
+    break;
+  }
+  case AkRead: {
+    if (!(MainPart.Kind & TrWrite) &&
+        (OtherPart.Kind & (TrWrite | TrUncheckedWrite))) {
+      MainPart = OtherPart;
+    }
+    OtherPart = TraversalResult(Index, Loc, Access);
+    break;
+  }
+  case AkUncheckedRead:
+  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 | TrUncheckedWrite);
+}
+
+bool TraversalAggregation::isReportedByWunsequenced() const {
+  return ((MainPart.Kind | OtherPart.Kind) & TrWrite) &&
+         (MainPart.Kind & (TrWrite | TrRead)) &&
+         (OtherPart.Kind & (TrWrite | TrRead));
+}
+
+bool TraversalAggregation::shouldBeReported() const {
+  return hasConflictingOperations() && !isReportedByWunsequenced();
+}
+
+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 (uint16_t I = 0; I < FieldIndices.size(); I++) {
+    bool IsUnion = (FieldIndices[I] & FiUnion) != 0;
+    uint16_t FieldKey = FieldIndices[I] & ~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();
+  for (; FieldIt != Node->Fields.end(); 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();
+  for (; FieldIt != Fields.end(); 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();
+  for (; FieldIt != Fields.end(); FieldIt++) {
+    if (FieldIt->first != ExceptIndex) {
+      FieldIt->second->addFieldToAll(Loc, Access, Index);
+    }
+  }
+}
+
+} // namespace clang::tidy::bugprone
diff --git 
a/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.h 
b/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.h
new file mode 100644
index 0000000000000..f421135694df9
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ConflictingGlobalAccessesCheck.h
@@ -0,0 +1,35 @@
+//===--- ConflictingGlobalAccessesCheck.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_CONFLICTINGGLOBALACCESSES\
+CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CONFLICTINGGLOBALACCESSES\
+CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds conflicting accesses on global variables.
+class ConflictingGlobalAccessesCheck : public ClangTidyCheck {
+public:
+  ConflictingGlobalAccessesCheck(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 cc092a9627c5f..69d1690f03f19 100644
--- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "../bugprone/BadSignalToKillThreadCheck.h"
+#include "../bugprone/ConflictingGlobalAccessesCheck.h"
 #include "../bugprone/PointerArithmeticOnPolymorphicObjectCheck.h"
 #include "../bugprone/ReservedIdentifierCheck.h"
 #include "../bugprone/SignalHandlerCheck.h"
@@ -262,6 +263,9 @@ class CERTModule : public ClangTidyModule {
     CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
     CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
         "cert-err61-cpp");
+    // EXP
+    CheckFactories.registerCheck<bugprone::ConflictingGlobalAccessesCheck>(
+        "cert-exp50-cpp");
     // MEM
     CheckFactories.registerCheck<DefaultOperatorNewAlignmentCheck>(
         "cert-mem57-cpp");
@@ -301,6 +305,8 @@ class CERTModule : public ClangTidyModule {
         "cert-err33-c");
     CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
     // EXP
+    CheckFactories.registerCheck<bugprone::ConflictingGlobalAccessesCheck>(
+        "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..423e6113376df
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/utils/ExecutionVisitor.h
@@ -0,0 +1,190 @@
+//===--- 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->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 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 761c1d3a80359..fd3dc42b921ab 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -113,6 +113,12 @@ New checks
   pointer and store it as class members without handle the copy and move
   constructors and the assignments.
 
+- New :doc: `bugprone-conflicting-global-accesses
+  <clang-tidy/checks/bugprone/conflicting-global-accesses>` check.
+
+  Finds unsequenced conflicting 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.
 
@@ -128,6 +134,14 @@ New checks
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
+- New `cert-exp30-c <cert/exp30-c>` alias for 
+  `bugprone-conflicting-global-accesses
+  <clang-tidy/checks/bugprone/conflicting-global-accesses>`.
+
+- New `cert-exp50-cpp <cert/exp50-cpp>` alias for 
+  `bugprone-conflicting-global-accesses
+  <clang-tidy/checks/bugprone/conflicting-global-accesses>`.
+
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/bugprone/conflicting-global-accesses.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/conflicting-global-accesses.rst
new file mode 100644
index 0000000000000..a461590296dcc
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/bugprone/conflicting-global-accesses.rst
@@ -0,0 +1,72 @@
+.. title:: clang-tidy - bugprone-conflicting-global-accesses
+
+bugprone-conflicting-global-accesses
+====================================
+
+Finds conflicting accesses on global variables.
+
+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 checker is similar to the -Wunsequenced clang warning,
+however it only looks at global variables and therefore can find conflicting
+actions recursively inside functions as well.
+
+For example::
+
+    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. ::
+
+    int globalVar = 0;
+
+    int incFun() {
+      globalVar++;
+      return globalVar;
+    }
+
+    int main() {
+      return globalVar + incFun(); // This is not detected by -Wunsequenced.
+    }
+
+This checker attempts to detect such cases. It recurses into functions that are
+inside the same translation unit. It also attempts not to flag cases that are
+already covered by -Wunsequenced. Global unions and structs are also handled.
+For example::
+    
+    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
+
+    It's possible to enable handling mutable reference and pointer function
+    parameters as writes using the HandleMutableFunctionParametersAsWrites
+    option. For example:
+
+    void func(int& a);
+
+    int globalVar;
+    func(globalVar); // <- this could be a write to globalVar.
+
+    This option is disabled by default.
+
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 18f1467285fab..6d4b4a0a8de84 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"
@@ -172,9 +173,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/conflicting-global-accesses.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/conflicting-global-accesses.cpp
new file mode 100644
index 0000000000000..d0454f42df58a
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/conflicting-global-accesses.cpp
@@ -0,0 +1,599 @@
+// RUN: %check_clang_tidy -std=c++98 -check-suffixes=,PRE-CPP11,PRE-CPP17 %s 
bugprone-conflicting-global-accesses %t
+// RUN: %check_clang_tidy -std=c++11,c++14 
-check-suffixes=,POST-CPP11,PRE-CPP17 %s bugprone-conflicting-global-accesses %t
+// RUN: %check_clang_tidy -std=c++17-or-later -check-suffixes=,POST-CPP11 %s 
bugprone-conflicting-global-accesses %t
+// RUN: %check_clang_tidy -std=c++17-or-later 
-check-suffixes=,POST-CPP11,PARAM %s bugprone-conflicting-global-accesses %t 
-config="{CheckOptions: 
{bugprone-conflicting-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);
+}
+
+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 usecase.
+    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);
+
+    // 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