Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -17,6 +17,7 @@ #include "BranchCloneCheck.h" #include "CopyConstructorInitCheck.h" #include "DanglingHandleCheck.h" +#include "DoNotReferAtomicTwiceCheck.h" #include "DynamicStaticInitializersCheck.h" #include "ExceptionEscapeCheck.h" #include "FoldInitTypeCheck.h" @@ -87,6 +88,8 @@ "bugprone-copy-constructor-init"); CheckFactories.registerCheck( "bugprone-dangling-handle"); + CheckFactories.registerCheck( + "bugprone-do-not-refer-atomic-twice"); CheckFactories.registerCheck( "bugprone-dynamic-static-initializers"); CheckFactories.registerCheck( Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -12,6 +12,7 @@ BugproneTidyModule.cpp CopyConstructorInitCheck.cpp DanglingHandleCheck.cpp + DoNotReferAtomicTwiceCheck.cpp DynamicStaticInitializersCheck.cpp ExceptionEscapeCheck.cpp FoldInitTypeCheck.cpp Index: clang-tools-extra/clang-tidy/bugprone/DoNotReferAtomicTwiceCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/DoNotReferAtomicTwiceCheck.h @@ -0,0 +1,34 @@ +//===--- DoNotReferAtomicTwiceCheck.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_DONOTREFERATOMICTWICECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DONOTREFERATOMICTWICECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds atomic variable which is referred twice in an expression. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-do-not-refer-atomic-twice.html +class DoNotReferAtomicTwiceCheck : public ClangTidyCheck { +public: + DoNotReferAtomicTwiceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_DONOTREFERATOMICTWICECHECK_H Index: clang-tools-extra/clang-tidy/bugprone/DoNotReferAtomicTwiceCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/DoNotReferAtomicTwiceCheck.cpp @@ -0,0 +1,62 @@ +//===--- DoNotReferAtomicTwiceCheck.cpp - clang-tidy ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DoNotReferAtomicTwiceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +const auto BinOp = anyOf( + hasAncestor(cxxOperatorCallExpr(allOf( + anyOf(hasOverloadedOperatorName("+"), hasOverloadedOperatorName("-"), + hasOverloadedOperatorName("*"), hasOverloadedOperatorName("/"), + hasOverloadedOperatorName("%"), hasOverloadedOperatorName("="), + hasOverloadedOperatorName("&&"), hasOverloadedOperatorName("||"), + hasOverloadedOperatorName("&"), hasOverloadedOperatorName("|"), + hasOverloadedOperatorName("^")), + unless(hasDescendant(atomicExpr())), + hasArgument( + 1, hasDescendant(declRefExpr(to(varDecl(equalsBoundNode("atomic")))) + .bind("rhs"))) + + ))), + hasAncestor(binaryOperator( + unless(hasDescendant(atomicExpr())), + hasRHS(hasDescendant(declRefExpr(to(varDecl(equalsBoundNode("atomic")))) + .bind("rhs")))))); + +void DoNotReferAtomicTwiceCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + declRefExpr( + + anyOf(hasType(hasUnqualifiedDesugaredType(atomicType())), + hasType(recordDecl(hasName("::std::atomic")))), + to(varDecl().bind("atomic")), + + BinOp, unless(equalsBoundNode("rhs"))), + this); +} + +void DoNotReferAtomicTwiceCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedVar = Result.Nodes.getNodeAs("atomic"); + const auto *MatchedRef = Result.Nodes.getNodeAs("rhs"); + if (!MatchedRef || !MatchedVar) + return; + diag(MatchedRef->getExprLoc(), + "Do not refer to atomic variable '%0' twice in an expression") + << MatchedVar->getName(); +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "../bugprone/BadSignalToKillThreadCheck.h" +#include "../bugprone/DoNotReferAtomicTwiceCheck.h" #include "../bugprone/ReservedIdentifierCheck.h" #include "../bugprone/SignalHandlerCheck.h" #include "../bugprone/SignedCharMisuseCheck.h" @@ -89,6 +90,8 @@ // CON CheckFactories.registerCheck( "cert-con36-c"); + CheckFactories.registerCheck( + "cert-con40-c"); // DCL CheckFactories.registerCheck("cert-dcl03-c"); CheckFactories.registerCheck( Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,11 @@ Finds structs that are inefficiently packed or aligned, and recommends packing and/or aligning of said structs as needed. +- New :doc:`bugprone-do-not-refer-atomic-twice + ` check. + + Finds atomic variables which are referred twice in an expression. + - New :doc:`cppcoreguidelines-prefer-member-initializer ` check. @@ -118,6 +123,11 @@ Finds functions registered as signal handlers that call non asynchronous-safe functions. +- New alias :doc:`cert-con40-c + ` to + :doc:`bugprone-do-not-refer-atomic-twice + ` was added. + - New :doc:`cert-sig30-c ` check. Index: clang-tools-extra/docs/clang-tidy/checks/bugprone-do-not-refer-atomic-twice.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/bugprone-do-not-refer-atomic-twice.rst @@ -0,0 +1,17 @@ +.. title:: clang-tidy - bugprone-do-not-refer-atomic-twice + +bugprone-do-not-refer-atomic-twice +================================== + +Finds atomic variable which is referred twice in an expression. + +.. code-block:: c + + atomic_int n = ATOMIC_VAR_INIT(0); + int compute_sum(void) { + return n * (n + 1) / 2; + } + +This check corresponds to the CERT C Coding Standard rule +`CON40-C. Do not refer to an atomic variable twice in an expression +`_. Index: clang-tools-extra/docs/clang-tidy/checks/cert-con40-c.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/cert-con40-c.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cert-con40-c +.. meta:: + :http-equiv=refresh: 5;URL=bugprone-do-not-refer-atomic-twice.html + +cert-con40-c +============ + +The cert-con40-c check is an alias, please see +`bugprone-do-not-refer-atomic-twice `_ +for more information. Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -55,6 +55,7 @@ `bugprone-branch-clone `_, `bugprone-copy-constructor-init `_, "Yes" `bugprone-dangling-handle `_, + `bugprone-do-not-refer-atomic-twice `_, `bugprone-dynamic-static-initializers `_, `bugprone-exception-escape `_, `bugprone-fold-init-type `_, @@ -103,6 +104,7 @@ `bugprone-unused-return-value `_, `bugprone-use-after-move `_, `bugprone-virtual-near-miss `_, "Yes" + `cert-con40-c `_, `cert-dcl21-cpp `_, `cert-dcl50-cpp `_, `cert-dcl58-cpp `_, Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-do-not-refer-atomic-twice.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/bugprone-do-not-refer-atomic-twice.cpp @@ -0,0 +1,129 @@ +// RUN: %check_clang_tidy %s bugprone-do-not-refer-atomic-twice %t +#define _Bool bool +typedef _Atomic _Bool atomic_bool; +typedef _Atomic int atomic_int; +#define offsetof(type, member) 0 + +namespace std { +template +struct atomic { + atomic(T t) {} + T operator=(const T t) { return t; } + T operator+(const T t) { return t; } + T operator*(const T t) { return t; } + void operator+=(const T t) {} + T operator++(const T t) { return t; } + operator T() const { return 0; } +}; +} // namespace std + +atomic_bool b = false; +atomic_int n = 0; +_Atomic int n2 = 0; +_Atomic(int) n3 = 0; +std::atomic n4(0); + +struct S { + atomic_bool b; + atomic_int n; + _Atomic int n2; + _Atomic(int) n3; + std::atomic n4(); +}; + +void warn1() { + n = (n + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: Do not refer to atomic variable 'n' twice in an expression [bugprone-do-not-refer-atomic-twice] + n2 = (n2 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: Do not refer to atomic variable 'n2' twice in an expression [bugprone-do-not-refer-atomic-twice] + n3 = (n3 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: Do not refer to atomic variable 'n3' twice in an expression [bugprone-do-not-refer-atomic-twice] + n4 = (n4 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: Do not refer to atomic variable 'n4' twice in an expression [bugprone-do-not-refer-atomic-twice] + b = b && true; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Do not refer to atomic variable 'b' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +int warn2_1() { + return n * (n + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Do not refer to atomic variable 'n' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +int warn2_2() { + return n2 * (n2 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Do not refer to atomic variable 'n2' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +int warn2_3() { + return n3 * (n3 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Do not refer to atomic variable 'n3' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +int warn2_4() { + return n4 * (n4 + 1) / 2; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: Do not refer to atomic variable 'n4' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +int warn2_5() { + return (b && true) || b; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: Do not refer to atomic variable 'b' twice in an expression [bugprone-do-not-refer-atomic-twice] +} + +void good1() { + n = 12; + n2 = 12; + n3 = 12; + n4 = 12; + b = true; +} + +int good2_1() { + return n; +} + +int good2_2() { + return n2; +} + +int good2_3() { + return n3; +} + +int good2_4() { + return n4; +} + +bool good2_5() { + return b; +} + +void good3() { + n += 12; + n2 += 12; + n3 += 12; + n4 += 12; + b ^= 1; +} + +void good4() { + n++; + n2++; + n3++; + n4++; +} + +void good5() { + int a = sizeof(atomic_int); + int a2 = sizeof(_Atomic int); + int a3 = sizeof(_Atomic(int)); + int a4 = sizeof(std::atomic); + int a5 = sizeof(atomic_bool); +} + +void good6() { + int a = offsetof(S, b); + int a2 = offsetof(S, n); + int a3 = offsetof(S, n2); + int a4 = offsetof(S, n3); + int a5 = offsetof(S, n4); +}