Skip to content

Commit cb418b7

Browse files
author
git apple-llvm automerger
committed
Merge commit 'aba8f320cc13' from llvm.org/main into next
2 parents f7bba0b + aba8f32 commit cb418b7

File tree

8 files changed

+175
-0
lines changed

8 files changed

+175
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===--- AssertEquals.cpp - clang-tidy --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "AssertEquals.h"
10+
11+
#include <map>
12+
#include <string>
13+
14+
using namespace clang::ast_matchers;
15+
16+
namespace clang {
17+
namespace tidy {
18+
namespace objc {
19+
20+
// Mapping from `XCTAssert*Equal` to `XCTAssert*EqualObjects` name.
21+
static const std::map<std::string, std::string> &NameMap() {
22+
static std::map<std::string, std::string> map{
23+
{"XCTAssertEqual", "XCTAssertEqualObjects"},
24+
{"XCTAssertNotEqual", "XCTAssertNotEqualObjects"},
25+
26+
};
27+
return map;
28+
}
29+
30+
void AssertEquals::registerMatchers(MatchFinder *finder) {
31+
for (const auto &pair : NameMap()) {
32+
finder->addMatcher(
33+
binaryOperator(anyOf(hasOperatorName("!="), hasOperatorName("==")),
34+
isExpandedFromMacro(pair.first),
35+
anyOf(hasLHS(hasType(qualType(
36+
hasCanonicalType(asString("NSString *"))))),
37+
hasRHS(hasType(qualType(
38+
hasCanonicalType(asString("NSString *"))))))
39+
40+
)
41+
.bind(pair.first),
42+
this);
43+
}
44+
}
45+
46+
void AssertEquals::check(const ast_matchers::MatchFinder::MatchResult &result) {
47+
for (const auto &pair : NameMap()) {
48+
if (const auto *root = result.Nodes.getNodeAs<BinaryOperator>(pair.first)) {
49+
SourceManager *sm = result.SourceManager;
50+
// The macros are nested two levels, so going up twice.
51+
auto macro_callsite = sm->getImmediateMacroCallerLoc(
52+
sm->getImmediateMacroCallerLoc(root->getBeginLoc()));
53+
diag(macro_callsite, "use " + pair.second + " for comparing objects")
54+
<< FixItHint::CreateReplacement(
55+
clang::CharSourceRange::getCharRange(
56+
macro_callsite,
57+
macro_callsite.getLocWithOffset(pair.first.length())),
58+
pair.second);
59+
}
60+
}
61+
}
62+
63+
} // namespace objc
64+
} // namespace tidy
65+
} // namespace clang
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===--- AssertEquals.h - clang-tidy ----------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_OBJCASSERTEQUALS_H_
10+
#define THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_OBJCASSERTEQUALS_H_
11+
12+
#include "../ClangTidyCheck.h"
13+
#include "clang/ASTMatchers/ASTMatchFinder.h"
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace objc {
18+
19+
/// Warn if XCTAssertEqual() or XCTAssertNotEqual() is used with at least one
20+
/// operands of type NSString*.
21+
///
22+
/// For the user-facing documentation see:
23+
/// http://clang.llvm.org/extra/clang-tidy/checks/objc-assert-equals.html
24+
class AssertEquals final : public ClangTidyCheck {
25+
public:
26+
AssertEquals(StringRef Name, ClangTidyContext *Context)
27+
: ClangTidyCheck(Name, Context) {}
28+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
29+
return LangOpts.ObjC;
30+
}
31+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
32+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
33+
};
34+
35+
} // namespace objc
36+
} // namespace tidy
37+
} // namespace clang
38+
39+
#endif // THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_CLANG_TIDY_OBJC_OBJCASSERTEQUALS_H_

clang-tools-extra/clang-tidy/objc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
44
)
55

66
add_clang_library(clangTidyObjCModule
7+
AssertEquals.cpp
78
AvoidNSErrorInitCheck.cpp
89
DeallocInCategoryCheck.cpp
910
ForbiddenSubclassingCheck.cpp

clang-tools-extra/clang-tidy/objc/ObjCTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../ClangTidy.h"
1010
#include "../ClangTidyModule.h"
1111
#include "../ClangTidyModuleRegistry.h"
12+
#include "AssertEquals.h"
1213
#include "AvoidNSErrorInitCheck.h"
1314
#include "DeallocInCategoryCheck.h"
1415
#include "ForbiddenSubclassingCheck.h"
@@ -28,6 +29,8 @@ class ObjCModule : public ClangTidyModule {
2829
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
2930
CheckFactories.registerCheck<AvoidNSErrorInitCheck>(
3031
"objc-avoid-nserror-init");
32+
CheckFactories.registerCheck<AssertEquals>("objc-assert-equals");
33+
3134
CheckFactories.registerCheck<DeallocInCategoryCheck>(
3235
"objc-dealloc-in-category");
3336
CheckFactories.registerCheck<ForbiddenSubclassingCheck>(

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ Clang-Tidy Checks
261261
`mpi-buffer-deref <mpi-buffer-deref.html>`_, "Yes"
262262
`mpi-type-mismatch <mpi-type-mismatch.html>`_, "Yes"
263263
`objc-avoid-nserror-init <objc-avoid-nserror-init.html>`_,
264+
`objc-assert-equals <objc-assert-equals.html>`_, "Yes"
264265
`objc-dealloc-in-category <objc-dealloc-in-category.html>`_,
265266
`objc-forbidden-subclassing <objc-forbidden-subclassing.html>`_,
266267
`objc-missing-hash <objc-missing-hash.html>`_,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.. title:: clang-tidy - objc-assert-equals
2+
3+
objc-assert-equals
4+
==================
5+
6+
Finds improper usages of `XCTAssertEqual` and `XCTAssertNotEqual` and replaces
7+
them with `XCTAssertEqualObjects` or `XCTAssertNotEqualObjects`.
8+
9+
This makes tests less fragile, as many improperly rely on pointer equality for
10+
strings that have equal values. This assumption is not guarantted by the
11+
language.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_OBJC_ASSERT_XCTESTASSERTIONS_H_
2+
#define THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_OBJC_ASSERT_XCTESTASSERTIONS_H_
3+
4+
#define _XCTPrimitiveAssertEqual(test, expression1, expressionStr1, \
5+
expression2, expressionStr2, ...) \
6+
({ \
7+
__typeof__(expression1) expressionValue1 = (expression1); \
8+
__typeof__(expression2) expressionValue2 = (expression2); \
9+
if (expressionValue1 != expressionValue2) { \
10+
} \
11+
})
12+
13+
#define _XCTPrimitiveAssertEqualObjects(test, expression1, expressionStr1, \
14+
expression2, expressionStr2, ...) \
15+
({ \
16+
__typeof__(expression1) expressionValue1 = (expression1); \
17+
__typeof__(expression2) expressionValue2 = (expression2); \
18+
if (expressionValue1 != expressionValue2) { \
19+
} \
20+
})
21+
22+
#define XCTAssertEqual(expression1, expression2, ...) \
23+
_XCTPrimitiveAssertEqual(nil, expression1, @ #expression1, expression2, \
24+
@ #expression2, __VA_ARGS__)
25+
26+
#define XCTAssertEqualObjects(expression1, expression2, ...) \
27+
_XCTPrimitiveAssertEqualObjects(nil, expression1, @ #expression1, \
28+
expression2, @ #expression2, __VA_ARGS__)
29+
30+
#endif // THIRD_PARTY_LLVM_LLVM_PROJECT_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_OBJC_ASSERT_XCTESTASSERTIONS_H_
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %check_clang_tidy %s objc-assert-equals %t -- -- -I %S/Inputs/objc-assert
2+
#include "XCTestAssertions.h"
3+
// Can't reference NSString directly so we use this getStr() instead.
4+
__typeof(@"abc") getStr() {
5+
return @"abc";
6+
}
7+
void foo() {
8+
XCTAssertEqual(getStr(), @"abc");
9+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use XCTAssertEqualObjects for comparing objects
10+
// CHECK-FIXES: XCTAssertEqualObjects(getStr(), @"abc");
11+
XCTAssertEqual(@"abc", @"abc");
12+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use XCTAssertEqualObjects for comparing objects
13+
// CHECK-FIXES: XCTAssertEqualObjects(@"abc", @"abc");
14+
XCTAssertEqual(@"abc", getStr());
15+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use XCTAssertEqualObjects for comparing objects
16+
// CHECK-FIXES: XCTAssertEqualObjects(@"abc", getStr());
17+
XCTAssertEqual(getStr(), getStr());
18+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use XCTAssertEqualObjects for comparing objects
19+
// CHECK-FIXES: XCTAssertEqualObjects(getStr(), getStr());
20+
// Primitive types should be ok
21+
XCTAssertEqual(123, 123);
22+
XCTAssertEqual(123.0, 123.45);
23+
// FIXME: This is the case where we don't diagnose properly.
24+
// XCTAssertEqual(@"abc" != @"abc", @"xyz" != @"xyz")
25+
}

0 commit comments

Comments
 (0)