aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'clang/unittests')
-rw-r--r--clang/unittests/AST/ASTImporterTest.cpp117
-rw-r--r--clang/unittests/AST/TypePrinterTest.cpp65
-rw-r--r--clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp13
-rw-r--r--clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp20
-rw-r--r--clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp17
-rw-r--r--clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp11
-rw-r--r--clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp94
-rw-r--r--clang/unittests/Analysis/FlowSensitive/TransferTest.cpp134
-rw-r--r--clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp331
-rw-r--r--clang/unittests/Analysis/LifetimeSafetyTest.cpp376
-rw-r--r--clang/unittests/Basic/DiagnosticTest.cpp8
-rw-r--r--clang/unittests/Basic/SarifTest.cpp8
-rw-r--r--clang/unittests/CMakeLists.txt1
-rw-r--r--clang/unittests/DependencyScanning/CMakeLists.txt11
-rw-r--r--clang/unittests/DependencyScanning/DependencyScanningFilesystemTest.cpp (renamed from clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp)37
-rw-r--r--clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp97
-rw-r--r--clang/unittests/Driver/DXCModeTest.cpp5
-rw-r--r--clang/unittests/Driver/ToolChainTest.cpp1
-rw-r--r--clang/unittests/Format/ConfigParseTest.cpp30
-rw-r--r--clang/unittests/Format/FormatTest.cpp68
-rw-r--r--clang/unittests/Format/FormatTestObjC.cpp26
-rw-r--r--clang/unittests/Format/FormatTestVerilog.cpp15
-rw-r--r--clang/unittests/Format/IntegerLiteralSeparatorTest.cpp27
-rw-r--r--clang/unittests/Format/QualifierFixerTest.cpp2
-rw-r--r--clang/unittests/Format/TokenAnnotatorTest.cpp27
-rw-r--r--clang/unittests/Frontend/ASTUnitTest.cpp6
-rw-r--r--clang/unittests/Frontend/CompilerInstanceTest.cpp1
-rw-r--r--clang/unittests/Frontend/UtilsTest.cpp1
-rw-r--r--clang/unittests/Lex/PPCallbacksTest.cpp56
-rw-r--r--clang/unittests/Sema/CMakeLists.txt1
-rw-r--r--clang/unittests/Sema/SemaNoloadLookupTest.cpp1
-rw-r--r--clang/unittests/Serialization/ForceCheckFileInputTest.cpp1
-rw-r--r--clang/unittests/Serialization/LoadSpecLazilyTest.cpp1
-rw-r--r--clang/unittests/Serialization/ModuleCacheTest.cpp1
-rw-r--r--clang/unittests/Serialization/NoCommentsTest.cpp1
-rw-r--r--clang/unittests/Serialization/PreambleInNamedModulesTest.cpp1
-rw-r--r--clang/unittests/Serialization/VarDeclConstantInitTest.cpp1
-rw-r--r--clang/unittests/Tooling/CMakeLists.txt3
-rw-r--r--clang/unittests/Tooling/DependencyScannerTest.cpp (renamed from clang/unittests/Tooling/DependencyScanning/DependencyScannerTest.cpp)85
-rw-r--r--clang/unittests/Tooling/RangeSelectorTest.cpp46
-rw-r--r--clang/unittests/Tooling/SourceCodeTest.cpp4
-rw-r--r--clang/unittests/Tooling/Syntax/TokensTest.cpp1
-rw-r--r--clang/unittests/Tooling/Syntax/TreeTestBase.cpp1
43 files changed, 1638 insertions, 115 deletions
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 4c7ea5e..1647906 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -3204,6 +3204,57 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) {
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
}
+TEST_P(ImportDecl, CycleInAutoTemplateSpec) {
+ MatchVerifier<Decl> Verifier;
+ const char *Code = R"(
+ template <class _CharT>
+ struct basic_string {
+ using value_type = _CharT;
+ };
+
+ template<typename T>
+ struct basic_string_view {
+ using value_type = T;
+ };
+
+ using string_view = basic_string_view<char>;
+ using string = basic_string<char>;
+
+ template<typename T>
+ struct span {
+ };
+
+ template <typename StringT>
+ auto StrCatT(span<const StringT> pieces) {
+ basic_string<typename StringT::value_type> result;
+ return result;
+ }
+
+ string StrCat(span<const string_view> pieces) {
+ return StrCatT(pieces);
+ }
+
+ string StrCat(span<const string> pieces) {
+ return StrCatT(pieces);
+ }
+
+ template <typename T>
+ auto declToImport(T pieces) {
+ return StrCat(pieces);
+ }
+
+ void test() {
+ span<const string> pieces;
+ auto result = declToImport(pieces);
+ }
+)";
+ // This test reproduces the StrCatT recursion pattern with concepts and span
+ // that may cause infinite recursion during AST import due to circular
+ // dependencies
+ testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
+ functionTemplateDecl(hasName("declToImport")));
+}
+
TEST_P(ImportExpr, ConceptNoRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
@@ -3300,6 +3351,72 @@ TEST_P(ImportExpr, ConceptNestedNonInstantiationDependentRequirement) {
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
+TEST_P(ImportExpr, ImportSubstNonTypeTemplateParmPackExpr) {
+ MatchVerifier<Decl> Verifier;
+ const char *Code = R"(
+ template<auto ...> struct X {};
+ template<typename ...> struct Z {};
+
+ template<int ...N> struct E {
+ template<int ...M> using B = Z<X<N, M>...>;
+ template<int M1, int M2> E(B<M1, M2>);
+ };
+ using declToImport = E<1, 3>;
+ )";
+ testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
+ typedefNameDecl(hasName("declToImport")));
+}
+
+TEST_P(ImportExpr, ImportCXXParenListInitExpr) {
+ MatchVerifier<Decl> Verifier;
+ const char *Code = R"(
+ struct Node {
+ int val;
+ double d;
+ };
+ Node* declToImport() { return new Node(2, 3.14); }
+ )";
+ testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
+ functionDecl(hasName("declToImport")));
+}
+
+TEST_P(ImportExpr, ImportPseudoObjectExpr) {
+ MatchVerifier<Decl> Verifier;
+ const char *Code = R"(
+ namespace std {
+ struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering less, equal, greater;
+ };
+ constexpr strong_ordering strong_ordering::less{-1},
+ strong_ordering::equal{0}, strong_ordering::greater{1};
+ }
+
+ struct A {
+ std::strong_ordering operator<=>(const A&) const;
+ };
+ struct B {
+ bool operator==(const B&) const;
+ bool operator<(const B&) const;
+ };
+
+ template<typename T> struct Cmp : T {
+ std::strong_ordering operator<=>(const Cmp&) const = default;
+ };
+
+ void use(...);
+ void declToImport() {
+ use(
+ Cmp<A>() <=> Cmp<A>(),
+ Cmp<B>() <=> Cmp<B>()
+ );
+ }
+ )";
+ testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
+ functionDecl(hasName("declToImport")));
+}
+
class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase {
public:
static constexpr auto DefaultCode = R"(
diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index ca0380b..3cadf9b 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -295,3 +295,68 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
}
}
+
+TEST(TypePrinter, NestedNameSpecifiers) {
+ constexpr char Code[] = R"cpp(
+ void level1() {
+ struct Inner {
+ Inner(int) {
+ struct {
+ union {} u;
+ } imem;
+ }
+ };
+ }
+ )cpp";
+
+ // Types scoped immediately inside a function don't print the function name in
+ // their scope.
+ ASSERT_TRUE(PrintedTypeMatches(
+ Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),
+ "struct (unnamed)", [](PrintingPolicy &Policy) {
+ Policy.FullyQualifiedName = true;
+ Policy.AnonymousTagLocations = false;
+ }));
+
+ ASSERT_TRUE(PrintedTypeMatches(
+ Code, {}, varDecl(hasName("imem"), hasType(qualType().bind("id"))),
+ "struct (unnamed)", [](PrintingPolicy &Policy) {
+ Policy.FullyQualifiedName = false;
+ Policy.AnonymousTagLocations = false;
+ }));
+
+ // Further levels of nesting print the entire scope.
+ ASSERT_TRUE(PrintedTypeMatches(
+ Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),
+ "union level1()::Inner::Inner(int)::(anonymous struct)::(unnamed)",
+ [](PrintingPolicy &Policy) {
+ Policy.FullyQualifiedName = true;
+ Policy.AnonymousTagLocations = false;
+ }));
+
+ ASSERT_TRUE(PrintedTypeMatches(
+ Code, {}, fieldDecl(hasName("u"), hasType(qualType().bind("id"))),
+ "union (unnamed)", [](PrintingPolicy &Policy) {
+ Policy.FullyQualifiedName = false;
+ Policy.AnonymousTagLocations = false;
+ }));
+}
+
+TEST(TypePrinter, NestedNameSpecifiersTypedef) {
+ constexpr char Code[] = R"cpp(
+ typedef union {
+ struct {
+ struct {
+ unsigned int baz;
+ } bar;
+ };
+ } foo;
+ )cpp";
+
+ ASSERT_TRUE(PrintedTypeMatches(
+ Code, {}, fieldDecl(hasName("bar"), hasType(qualType().bind("id"))),
+ "struct foo::(anonymous struct)::(unnamed)", [](PrintingPolicy &Policy) {
+ Policy.FullyQualifiedName = true;
+ Policy.AnonymousTagLocations = false;
+ }));
+}
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 8a957864..5d45235 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -26,6 +26,19 @@ TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesInFile) {
EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO"))));
}
+static std::string constructMacroName(llvm::StringRef A, llvm::StringRef B) {
+ return (A + "_" + B).str();
+}
+
+TEST_P(ASTMatchersTest, IsExpandedFromMacro_ConstructedMacroName) {
+ StringRef input = R"cc(
+#define MY_MACRO(a) (4 + (a))
+ void Test() { MY_MACRO(4); }
+ )cc";
+ auto matcher = isExpandedFromMacro(constructMacroName("MY", "MACRO"));
+ EXPECT_TRUE(matches(input, binaryOperator(matcher)));
+}
+
TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesNested) {
StringRef input = R"cc(
#define MY_MACRO(a) (4 + (a))
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 3fcb558..108b32e 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -2353,6 +2353,26 @@ TEST_P(ASTMatchersTest, ReferenceTypeLocTest_BindsToAnyRvalueReferenceTypeLoc) {
EXPECT_TRUE(matches("float&& r = 3.0;", matcher));
}
+TEST_P(ASTMatchersTest, ArrayTypeLocTest_BindsToAnyArrayTypeLoc) {
+ auto matcher = varDecl(hasName("x"), hasTypeLoc(arrayTypeLoc()));
+ EXPECT_TRUE(matches("int x[3];", matcher));
+ EXPECT_TRUE(matches("float x[3];", matcher));
+ EXPECT_TRUE(matches("char x[3];", matcher));
+ EXPECT_TRUE(matches("void* x[3];", matcher));
+ EXPECT_TRUE(matches("const int x[3] = {1, 2, 3};", matcher));
+ EXPECT_TRUE(matches("int x[3][4];", matcher));
+ EXPECT_TRUE(matches("void foo(int x[]);", matcher));
+ EXPECT_TRUE(matches("int a[] = {1, 2}; void foo() {int x[a[0]];}", matcher));
+}
+
+TEST_P(ASTMatchersTest, ArrayTypeLocTest_DoesNotBindToNonArrayTypeLoc) {
+ auto matcher = varDecl(hasName("x"), hasTypeLoc(arrayTypeLoc()));
+ EXPECT_TRUE(notMatches("int x;", matcher));
+ EXPECT_TRUE(notMatches("float x;", matcher));
+ EXPECT_TRUE(notMatches("char x;", matcher));
+ EXPECT_TRUE(notMatches("void* x;", matcher));
+}
+
TEST_P(ASTMatchersTest,
TemplateSpecializationTypeLocTest_BindsToVarDeclTemplateSpecialization) {
if (!GetParam().isCXX()) {
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 8fc9a66d..d171d47 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -2091,4 +2091,21 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerToMemberOperator) {
EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
}
+TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsPointerToPointer) {
+ {
+ const std::string Code = "void f(int**); void g() { int* ip; f(&ip); }";
+ auto AST = buildASTFromCode(Code);
+ auto Results =
+ match(withEnclosingCompound(declRefTo("ip")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+ {
+ const std::string Code = "void f(void**); void g() { void* ip; f(&ip); }";
+ auto AST = buildASTFromCode(Code);
+ auto Results =
+ match(withEnclosingCompound(declRefTo("ip")), AST->getASTContext());
+ EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+ }
+}
+
} // namespace clang
diff --git a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
index 8863011..60925543 100644
--- a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
@@ -149,9 +149,18 @@ recordState(Elements=8, Branches=2, Joins=1)
enterElement(return b ? p : q;)
transfer()
recordState(Elements=9, Branches=2, Joins=1)
+enterElement([Parm: q] (Lifetime ends))
+transfer()
+recordState(Elements=10, Branches=2, Joins=1)
+enterElement([Parm: p] (Lifetime ends))
+transfer()
+recordState(Elements=11, Branches=2, Joins=1)
+enterElement([Parm: b] (Lifetime ends))
+transfer()
+recordState(Elements=12, Branches=2, Joins=1)
enterBlock(0, false)
-recordState(Elements=9, Branches=2, Joins=1)
+recordState(Elements=12, Branches=2, Joins=1)
endAnalysis()
)");
diff --git a/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp b/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
index 2e528ed..8ea2015 100644
--- a/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp
@@ -1358,6 +1358,8 @@ bool operator!=(const Status &lhs, const Status &rhs);
Status OkStatus();
Status InvalidArgumentError(const char *);
+} // namespace absl
+
#endif // STATUS_H
)cc";
@@ -1370,6 +1372,8 @@ constexpr const char StatusOrDefsHeader[] = R"cc(
#include "std_type_traits.h"
#include "std_utility.h"
+namespace absl {
+
template <typename T> struct StatusOr;
namespace internal_statusor {
@@ -2232,6 +2236,95 @@ using testing::AssertionResult;
#endif // TESTING_DEFS_H
)cc";
+constexpr const char StdUniquePtrHeader[] = R"cc(
+namespace std {
+
+ template <typename T>
+ struct default_delete {};
+
+ template <typename T, typename D = default_delete<T>>
+ class unique_ptr {
+ public:
+ using element_type = T;
+ using deleter_type = D;
+
+ constexpr unique_ptr();
+ constexpr unique_ptr(nullptr_t) noexcept;
+ unique_ptr(unique_ptr&&);
+ explicit unique_ptr(T*);
+ template <typename U, typename E>
+ unique_ptr(unique_ptr<U, E>&&);
+
+ ~unique_ptr();
+
+ unique_ptr& operator=(unique_ptr&&);
+ template <typename U, typename E>
+ unique_ptr& operator=(unique_ptr<U, E>&&);
+ unique_ptr& operator=(nullptr_t);
+
+ void reset(T* = nullptr) noexcept;
+ T* release();
+ T* get() const;
+
+ T& operator*() const;
+ T* operator->() const;
+ explicit operator bool() const noexcept;
+ };
+
+ template <typename T, typename D>
+ class unique_ptr<T[], D> {
+ public:
+ T* get() const;
+ T& operator[](size_t i);
+ const T& operator[](size_t i) const;
+ };
+
+ template <typename T, typename... Args>
+ unique_ptr<T> make_unique(Args&&...);
+
+ template <class T, class D>
+ void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
+
+ template <class T1, class D1, class T2, class D2>
+ bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+ template <class T1, class D1, class T2, class D2>
+ bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
+
+ template <class T, class D>
+ bool operator==(const unique_ptr<T, D>& x, nullptr_t) noexcept;
+ template <class T, class D>
+ bool operator==(nullptr_t, const unique_ptr<T, D>& y) noexcept;
+ template <class T, class D>
+ bool operator!=(const unique_ptr<T, D>& x, nullptr_t) noexcept;
+ template <class T, class D>
+ bool operator!=(nullptr_t, const unique_ptr<T, D>& y) noexcept;
+ template <class T, class D>
+ bool operator<(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator<(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator<=(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator<=(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator>(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator>(nullptr_t, const unique_ptr<T, D>& y);
+ template <class T, class D>
+ bool operator>=(const unique_ptr<T, D>& x, nullptr_t);
+ template <class T, class D>
+ bool operator>=(nullptr_t, const unique_ptr<T, D>& y);
+}
+)cc";
+
std::vector<std::pair<std::string, std::string>> getMockHeaders() {
std::vector<std::pair<std::string, std::string>> Headers;
Headers.emplace_back("cstddef.h", CStdDefHeader);
@@ -2249,6 +2342,7 @@ std::vector<std::pair<std::string, std::string>> getMockHeaders() {
Headers.emplace_back("statusor_defs.h", StatusOrDefsHeader);
Headers.emplace_back("absl_log.h", AbslLogHeader);
Headers.emplace_back("testing_defs.h", TestingDefsHeader);
+ Headers.emplace_back("std_unique_ptr.h", StdUniquePtrHeader);
return Headers;
}
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 66b3bba..a6308d1 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -17,6 +17,7 @@
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
#include "clang/Analysis/FlowSensitive/RecordOps.h"
@@ -4382,6 +4383,40 @@ TEST(TransferTest, VarDeclInitAssignConditionalOperator) {
});
}
+TEST(TransferTest, VarDeclInitReferenceAssignConditionalOperator) {
+ std::string Code = R"(
+ struct A {
+ int i;
+ };
+
+ void target(A Foo, A Bar, bool Cond) {
+ A &Baz = Cond ? Foo : Bar;
+ // Make sure A::i is modeled.
+ Baz.i;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+ auto *FooIVal = cast<IntegerValue>(getFieldValue(
+ &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Foo"), "i",
+ ASTCtx, Env));
+ auto *BarIVal = cast<IntegerValue>(getFieldValue(
+ &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Bar"), "i",
+ ASTCtx, Env));
+ auto *BazIVal = cast<IntegerValue>(getFieldValue(
+ &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Baz"), "i",
+ ASTCtx, Env));
+
+ EXPECT_NE(BazIVal, FooIVal);
+ EXPECT_NE(BazIVal, BarIVal);
+ });
+}
+
TEST(TransferTest, VarDeclInDoWhile) {
std::string Code = R"(
void target(int *Foo) {
@@ -6150,6 +6185,45 @@ TEST(TransferTest, ConditionalOperatorValue) {
});
}
+TEST(TransferTest, ConditionalOperatorValuesTested) {
+ // We should be able to show that the result of the conditional operator,
+ // JoinResultMustBeB1, must be equal to B1, because the condition is checking
+ // `B1 == B2` and selecting B1 on the false branch, or B2 on the true branch.
+ // Similarly, for JoinResultMustBeB2 == B2.
+ // Note that the conditional operator involves a join of two *different*
+ // glvalues, before casting the lvalue to an rvalue, which may affect the
+ // implementation of the transfer function, and thus affect whether or not we
+ // can prove that IsB1 == B1.
+ std::string Code = R"(
+ void target(bool B1, bool B2) {
+ bool JoinResultMustBeB1 = (B1 == B2) ? B2 : B1;
+ bool JoinResultMustBeB2 = (B1 == B2) ? B1 : B2;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
+
+ auto &B1 = getValueForDecl<BoolValue>(ASTCtx, Env, "B1");
+ auto &B2 = getValueForDecl<BoolValue>(ASTCtx, Env, "B2");
+ auto &JoinResultMustBeB1 =
+ getValueForDecl<BoolValue>(ASTCtx, Env, "JoinResultMustBeB1");
+ auto &JoinResultMustBeB2 =
+ getValueForDecl<BoolValue>(ASTCtx, Env, "JoinResultMustBeB2");
+
+ const Formula &MustBeB1_Eq_B1 =
+ Env.arena().makeEquals(JoinResultMustBeB1.formula(), B1.formula());
+ EXPECT_TRUE(Env.proves(MustBeB1_Eq_B1));
+
+ const Formula &MustBeB2_Eq_B2 =
+ Env.arena().makeEquals(JoinResultMustBeB2.formula(), B2.formula());
+ EXPECT_TRUE(Env.proves(MustBeB2_Eq_B2));
+ });
+}
+
TEST(TransferTest, ConditionalOperatorLocation) {
std::string Code = R"(
void target(bool Cond, int I1, int I2) {
@@ -6177,6 +6251,66 @@ TEST(TransferTest, ConditionalOperatorLocation) {
});
}
+TEST(TransferTest, ConditionalOperatorLocationUpdatedAfter) {
+ // We don't currently model a Conditional Operator with an LValue result
+ // as having aliases to the LHS and RHS (if it isn't just the same LValue
+ // on both sides). We also don't model that the update "may" happen
+ // (a weak update). So, we don't consider the LHS and RHS as being weakly
+ // updated at [[after_diff]].
+ std::string Code = R"(
+ void target(bool Cond, bool B1, bool B2) {
+ (void)0;
+ // [[before_same]]
+ (Cond ? B1 : B1) = !B1;
+ // [[after_same]]
+ (Cond ? B1 : B2) = !B1;
+ // [[after_diff]]
+ }
+ )";
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ Environment BeforeSameEnv =
+ getEnvironmentAtAnnotation(Results, "before_same").fork();
+ Environment AfterSameEnv =
+ getEnvironmentAtAnnotation(Results, "after_same").fork();
+ Environment AfterDiffEnv =
+ getEnvironmentAtAnnotation(Results, "after_diff").fork();
+
+ auto &BeforeSameB1 =
+ getValueForDecl<BoolValue>(ASTCtx, BeforeSameEnv, "B1");
+ auto &AfterSameB1 =
+ getValueForDecl<BoolValue>(ASTCtx, AfterSameEnv, "B1");
+ auto &AfterDiffB1 =
+ getValueForDecl<BoolValue>(ASTCtx, AfterDiffEnv, "B1");
+
+ EXPECT_NE(&BeforeSameB1, &AfterSameB1);
+ EXPECT_NE(&BeforeSameB1, &AfterDiffB1);
+ // FIXME: The formula for AfterSameB1 should be different from
+ // AfterDiffB1 to reflect that B1 may be updated.
+ EXPECT_EQ(&AfterSameB1, &AfterDiffB1);
+
+ // The value of B1 is definitely different from before_same vs
+ // after_same.
+ const Formula &B1ChangedForSame =
+ AfterSameEnv.arena().makeNot(AfterSameEnv.arena().makeEquals(
+ AfterSameB1.formula(), BeforeSameB1.formula()));
+ EXPECT_TRUE(AfterSameEnv.allows(B1ChangedForSame));
+ EXPECT_TRUE(AfterSameEnv.proves(B1ChangedForSame));
+
+ const Formula &B1ChangedForDiff =
+ AfterDiffEnv.arena().makeNot(AfterDiffEnv.arena().makeEquals(
+ AfterDiffB1.formula(), AfterSameB1.formula()));
+ // FIXME: It should be possible that B1 *may* be updated, so it may be
+ // that AfterSameB1 != AfterDiffB1 or AfterSameB1 == AfterDiffB1.
+ EXPECT_FALSE(AfterSameEnv.allows(B1ChangedForDiff));
+ // proves() should be false, since B1 may or may not have changed
+ // depending on `Cond`.
+ EXPECT_FALSE(AfterSameEnv.proves(B1ChangedForDiff));
+ });
+}
+
TEST(TransferTest, ConditionalOperatorOnConstantExpr) {
// This is a regression test: We used to crash when a `ConstantExpr` was used
// in the branches of a conditional operator.
diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
index 5635ff4..deaeea3 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp
@@ -1725,6 +1725,90 @@ TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) {
)cc");
}
+TEST_P(UncheckedStatusOrAccessModelTest, CheckOkMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK_OK(sor.status());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK_OK(sor);
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUS s = Make<STATUS>();
+ CHECK_OK(s);
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckOkMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK_OK(sor.status());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK_OK(sor);
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ STATUS s = Make<STATUS>();
+ QCHECK_OK(s);
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, CheckEqMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ CHECK_EQ(sor.status(), absl::OkStatus());
+ sor.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ CHECK_EQ(Make<STATUS>(), absl::OkStatus());
+ CHECK_EQ(absl::OkStatus(), Make<STATUS>());
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, QcheckEqMacro) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(STATUSOR_INT sor) {
+ QCHECK_EQ(sor.status(), absl::OkStatus());
+ sor.value();
+ }
+ )cc");
+}
+
TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) {
ExpectDiagnosticsFor(R"cc(
#include "unchecked_statusor_access_test_defs.h"
@@ -3186,6 +3270,252 @@ TEST_P(UncheckedStatusOrAccessModelTest, ConstructStatusFromReference) {
)cc");
}
+TEST_P(UncheckedStatusOrAccessModelTest, AccessorCall) {
+ // Accessor returns reference.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT& sor() const { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) foo.sor().value();
+ }
+ )cc");
+
+ // Uses an operator
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT& operator()() const { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo().ok()) foo().value();
+ }
+ )cc");
+
+ // Calls nonconst method in between.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ void invalidate() {}
+
+ const STATUSOR_INT& sor() const { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) {
+ foo.invalidate();
+ foo.sor().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+
+ // Calls nonconst operator in between.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ void operator()() {}
+
+ const STATUSOR_INT& sor() const { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) {
+ foo();
+ foo.sor().value(); // [[unsafe]]
+ }
+ }
+ )cc");
+
+ // Accessor returns copy.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ STATUSOR_INT sor() const { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) foo.sor().value();
+ }
+ )cc");
+
+ // Non-const accessor.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT& sor() { return sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) foo.sor().value(); // [[unsafe]]
+ }
+ )cc");
+
+ // Non-const rvalue accessor.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ STATUSOR_INT&& sor() { return std::move(sor_); }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor().ok()) foo.sor().value(); // [[unsafe]]
+ }
+ )cc");
+
+ // const pointer accessor.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT* sor() const { return &sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo.sor()->ok()) foo.sor()->value();
+ }
+ )cc");
+
+ // const pointer operator.
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT* operator->() const { return &sor_; }
+ };
+
+ void target(Foo foo) {
+ if (foo->ok()) foo->value();
+ }
+ )cc");
+
+ // We copy the result of the accessor.
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ struct Foo {
+ STATUSOR_INT sor_;
+
+ const STATUSOR_INT& sor() const { return sor_; }
+ };
+ void target() {
+ Foo foo;
+ if (!foo.sor().ok()) return;
+ const auto sor = foo.sor();
+ sor.value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, PointerLike) {
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo {
+ public:
+ std::pair<int, STATUSOR_VOIDPTR>& operator*() const;
+ std::pair<int, STATUSOR_VOIDPTR>* operator->() const;
+ bool operator!=(const Foo& other) const;
+ };
+
+ void target() {
+ Foo foo;
+ if (foo->second.ok() && *foo->second != nullptr) {
+ *foo->second;
+ (*foo).second.value();
+ }
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ class Foo {
+ public:
+ std::pair<int, STATUSOR_INT>& operator*() const;
+ std::pair<int, STATUSOR_INT>* operator->() const;
+ };
+ void target() {
+ Foo foo;
+ if (!foo->second.ok()) return;
+ foo->second.value();
+ (*foo).second.value();
+ }
+ )cc");
+ ExpectDiagnosticsFor(R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target(std::pair<int, STATUSOR_VOIDPTR>* foo) {
+ if (foo->second.ok() && *foo->second != nullptr) {
+ *foo->second;
+ (*foo).second.value();
+ }
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UniquePtr) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto sor_up = Make<std::unique_ptr<STATUSOR_INT>>();
+ if (sor_up->ok()) sor_up->value();
+ }
+ )cc");
+}
+
+TEST_P(UncheckedStatusOrAccessModelTest, UniquePtrReset) {
+ ExpectDiagnosticsFor(
+ R"cc(
+#include "unchecked_statusor_access_test_defs.h"
+
+ void target() {
+ auto sor_up = Make<std::unique_ptr<STATUSOR_INT>>();
+ if (sor_up->ok()) {
+ sor_up.reset(Make<STATUSOR_INT*>());
+ sor_up->value(); // [[unsafe]]
+ }
+ }
+ )cc");
+}
+
} // namespace
std::string
@@ -3235,6 +3565,7 @@ GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind) {
#include "std_pair.h"
#include "absl_log.h"
#include "testing_defs.h"
+#include "std_unique_ptr.h"
template <typename T>
T Make();
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index 34af476..fee4e79 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -20,6 +20,7 @@ namespace clang::lifetimes::internal {
namespace {
using namespace ast_matchers;
+using ::testing::Not;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAreArray;
@@ -58,6 +59,7 @@ public:
BuildOptions.setAllAlwaysAdd();
BuildOptions.AddImplicitDtors = true;
BuildOptions.AddTemporaryDtors = true;
+ BuildOptions.AddLifetime = true;
// Run the main analysis.
Analysis = std::make_unique<LifetimeSafetyAnalysis>(*AnalysisCtx, nullptr);
@@ -112,9 +114,10 @@ public:
return {};
}
std::vector<LoanID> LID;
- for (const Loan &L : Analysis.getFactManager().getLoanMgr().getLoans())
- if (L.Path.D == VD)
- LID.push_back(L.ID);
+ for (const Loan *L : Analysis.getFactManager().getLoanMgr().getLoans())
+ if (const auto *BL = dyn_cast<PathLoan>(L))
+ if (BL->getAccessPath().D == VD)
+ LID.push_back(L->getID());
if (LID.empty()) {
ADD_FAILURE() << "Loan for '" << VarName << "' not found.";
return {};
@@ -122,6 +125,40 @@ public:
return LID;
}
+ // Gets the set of loans that are live at the given program point. A loan is
+ // considered live at point P if there is a live origin which contains this
+ // loan.
+ std::optional<LoanSet> getLiveLoansAtPoint(ProgramPoint P) const {
+ const auto &LiveOriginsAnalysis = Runner.getAnalysis().getLiveOrigins();
+ const auto &LoanPropagation = Runner.getAnalysis().getLoanPropagation();
+
+ LivenessMap LiveOriginsMap = LiveOriginsAnalysis.getLiveOriginsAt(P);
+
+ LoanSet::Factory F;
+ LoanSet Result = F.getEmptySet();
+
+ for (const auto &[OID, LI] : LiveOriginsMap) {
+ LoanSet Loans = LoanPropagation.getLoans(OID, P);
+ Result = clang::lifetimes::internal::utils::join(Result, Loans, F);
+ }
+
+ if (Result.isEmpty())
+ return std::nullopt;
+
+ return Result;
+ }
+
+ const ExpireFact *
+ getExpireFactFromAllFacts(const llvm::ArrayRef<const Fact *> &FactsInBlock,
+ const LoanID &loanID) {
+ for (const Fact *F : FactsInBlock) {
+ if (auto const *CurrentEF = F->getAs<ExpireFact>())
+ if (CurrentEF->getLoanID() == loanID)
+ return CurrentEF;
+ }
+ return nullptr;
+ }
+
std::optional<LoanSet> getLoansAtPoint(OriginID OID,
llvm::StringRef Annotation) {
ProgramPoint PP = Runner.getProgramPoint(Annotation);
@@ -141,6 +178,14 @@ public:
return Result;
}
+ ProgramPoint getProgramPoint(llvm::StringRef Annotation) {
+ return Runner.getProgramPoint(Annotation);
+ }
+
+ llvm::ArrayRef<const Fact *> getBlockContaining(ProgramPoint P) {
+ return Runner.getAnalysis().getFactManager().getBlockContaining(P);
+ }
+
private:
template <typename DeclT> DeclT *findDecl(llvm::StringRef Name) {
auto &Ctx = Runner.getASTContext();
@@ -304,6 +349,43 @@ MATCHER_P2(AreLiveAtImpl, Annotation, ConfFilter, "") {
return true;
}
+MATCHER_P2(HasLiveLoanAtExpiryImpl, HelperPtr, Annotation, "") {
+ llvm::StringRef VarName = arg;
+ LifetimeTestHelper &Helper = *HelperPtr;
+
+ std::vector<LoanID> Loans = Helper.getLoansForVar(VarName);
+ if (Loans.empty()) {
+ *result_listener << "No loans found for variable" << VarName.str();
+ return false;
+ }
+
+ ProgramPoint PP = Helper.getProgramPoint(Annotation);
+ llvm::ArrayRef<const Fact *> AllFactsInBlock = Helper.getBlockContaining(PP);
+
+ bool NoExpireFactLive = false;
+ for (const LoanID CurrentLoanID : Loans) {
+ const ExpireFact *EF =
+ Helper.getExpireFactFromAllFacts(AllFactsInBlock, CurrentLoanID);
+ if (!EF) {
+ NoExpireFactLive = true;
+ continue;
+ }
+ std::optional<LoanSet> LiveLoans = Helper.getLiveLoansAtPoint(EF);
+ if (!LiveLoans.has_value()) {
+ *result_listener << "No Live Loans At Expiry Location.";
+ continue;
+ }
+ if (LiveLoans->contains({CurrentLoanID}))
+ return true;
+ }
+ if (NoExpireFactLive) {
+ *result_listener << "No Expire Fact for loan of " << VarName.str();
+ return false;
+ }
+ *result_listener << "No loans of " << VarName.str() << " are live";
+ return false;
+}
+
MATCHER_P(MustBeLiveAt, Annotation, "") {
return ExplainMatchResult(AreLiveAtImpl(Annotation, LivenessKindFilter::Must),
arg, result_listener);
@@ -353,6 +435,10 @@ protected:
return HasLoansToImpl(std::vector<std::string>(LoanVars), Annotation);
}
+ auto HasLiveLoanAtExpiry(const char *Annotation) {
+ return HasLiveLoanAtExpiryImpl(Helper.get(), Annotation);
+ }
+
std::unique_ptr<LifetimeTestRunner> Runner;
std::unique_ptr<LifetimeTestHelper> Helper;
};
@@ -689,7 +775,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerConstructFromView) {
EXPECT_THAT(Origin("q"), HasLoansTo({"a"}, "p1"));
}
-// FIXME: Handle loans in ternary operator!
TEST_F(LifetimeAnalysisTest, GslPointerInConditionalOperator) {
SetupTest(R"(
void target(bool cond) {
@@ -698,7 +783,24 @@ TEST_F(LifetimeAnalysisTest, GslPointerInConditionalOperator) {
POINT(p1);
}
)");
- EXPECT_THAT(Origin("v"), HasLoansTo({}, "p1"));
+ EXPECT_THAT(Origin("v"), HasLoansTo({"a", "b"}, "p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, ExtraParenthesis) {
+ SetupTest(R"(
+ void target() {
+ MyObj a;
+ View x = ((View((((a))))));
+ View y = ((View{(((x)))}));
+ View z = ((View(((y)))));
+ View p = ((View{((x))}));
+ POINT(p1);
+ }
+ )");
+ EXPECT_THAT(Origin("x"), HasLoansTo({"a"}, "p1"));
+ EXPECT_THAT(Origin("y"), HasLoansTo({"a"}, "p1"));
+ EXPECT_THAT(Origin("z"), HasLoansTo({"a"}, "p1"));
+ EXPECT_THAT(Origin("p"), HasLoansTo({"a"}, "p1"));
}
// FIXME: Handle temporaries.
@@ -1207,5 +1309,269 @@ TEST_F(LifetimeAnalysisTest, LivenessOutsideLoop) {
EXPECT_THAT(Origins({"p"}), MaybeLiveAt("p1"));
}
+TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAF) {
+ SetupTest(R"(
+ void target() {
+ int *ptr;
+ {
+ int s = 1;
+ ptr = &s;
+ }
+ POINT(p1);
+ (void)*ptr;
+ }
+ )");
+ EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));
+ EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAF) {
+ SetupTest(R"(
+ class S {
+ View a, b;
+ };
+
+ void target() {
+ S* ptr;
+ {
+ S s;
+ ptr = &s;
+ }
+ POINT(p1);
+ (void)ptr;
+ }
+ )");
+ EXPECT_THAT(Origin("ptr"), HasLoansTo({"s"}, "p1"));
+ EXPECT_THAT(Origins({"ptr"}), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, SimpleReturnStackAddress) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj s;
+ MyObj* p = &s;
+ POINT(p1);
+ return p;
+ }
+ )");
+ EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, DirectReturn) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj s;
+ POINT(P);
+ return &s;
+ }
+ )");
+ EXPECT_THAT("s", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, ConditionalAssignUnconditionalReturn) {
+ SetupTest(R"(
+ MyObj* target(bool c) {
+ MyObj s1;
+ MyObj* p = nullptr;
+ if (c) {
+ p = &s1;
+ }
+ POINT(P);
+ return p;
+ }
+ )");
+ EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignments) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj s;
+ MyObj* p1 = &s;
+ MyObj* p2 = &s;
+ POINT(P);
+ return p2;
+ }
+ )");
+ // Test if atleast one loan to "s" is live;
+ EXPECT_THAT("s", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, ConditionalAssignBothBranches) {
+ SetupTest(R"(
+ MyObj* target(bool c) {
+ MyObj s1;
+ static MyObj s2;
+ MyObj* p = nullptr;
+ if (c) {
+ p = &s1;
+ } else {
+ p = &s2;
+ }
+ POINT(P);
+ return p;
+ }
+ )");
+ EXPECT_THAT("s1", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, ReassignFromSafeToLocalThenReturn) {
+ SetupTest(R"(
+ MyObj* target() {
+ static MyObj safe_obj;
+ MyObj local_obj;
+ MyObj* p = &safe_obj;
+
+ p = &local_obj;
+ POINT(P);
+ return p;
+ }
+ )");
+ EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, PointerChainToLocal) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj local_obj;
+ MyObj* p1 = &local_obj;
+ MyObj* p2 = p1;
+ POINT(P);
+ return p2;
+ }
+ )");
+ EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignmentMultipleReturn) {
+ SetupTest(R"(
+ MyObj* target(bool c1, bool c2) {
+ static MyObj global_obj;
+ MyObj local_obj1;
+ MyObj local_obj2;
+ MyObj* p = nullptr;
+ if(c1){
+ p = &local_obj1;
+ POINT(C1);
+ return p;
+ }
+ else if(c2){
+ p = &local_obj2;
+ POINT(C2);
+ return p;
+ }
+ p = &global_obj;
+ POINT(C3);
+ return p;
+ }
+ )");
+
+ EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("C1"));
+ EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("C2"));
+
+ EXPECT_THAT("local_obj1", Not(HasLiveLoanAtExpiry("C3")));
+ EXPECT_THAT("local_obj2", Not(HasLiveLoanAtExpiry("C3")));
+}
+
+TEST_F(LifetimeAnalysisTest, MultipleAssignmentsSingleReturn) {
+ SetupTest(R"(
+ MyObj* target(bool c1, bool c2) {
+ static MyObj global_obj;
+ MyObj local_obj1;
+ MyObj local_obj2;
+ MyObj* p = nullptr;
+ if(c1){
+ p = &local_obj1;
+ }
+ else if(c2){
+ p = &local_obj2;
+ }
+ else{
+ p = &global_obj;
+ }
+ POINT(P);
+ return p;
+ }
+ )");
+ EXPECT_THAT("local_obj1", HasLiveLoanAtExpiry("P"));
+ EXPECT_THAT("local_obj2", HasLiveLoanAtExpiry("P"));
+}
+
+TEST_F(LifetimeAnalysisTest, UseAfterScopeThenReturn) {
+ SetupTest(R"(
+ MyObj* target() {
+ MyObj* p;
+ {
+ MyObj local_obj;
+ p = &local_obj;
+ POINT(p1);
+ }
+ POINT(p2);
+ return p;
+ }
+ )");
+ EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p2"));
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p2"));
+
+ EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p1"));
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));
+
+ EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p2"));
+}
+
+TEST_F(LifetimeAnalysisTest, ReturnBeforeUseAfterScope) {
+ SetupTest(R"(
+ MyObj* target(bool c) {
+ MyObj* p;
+ static MyObj global_obj;
+ {
+ MyObj local_obj;
+ p = &local_obj;
+ if(c){
+ POINT(p1);
+ return p;
+ }
+ }
+ POINT(p2);
+ return &global_obj;
+ }
+ )");
+ EXPECT_THAT("local_obj", HasLiveLoanAtExpiry("p1"));
+
+ EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
+
+ EXPECT_THAT(Origin("p"), HasLoansTo({"local_obj"}, "p1"));
+ EXPECT_THAT(Origins({"p"}), MustBeLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, TrivialDestructorsUAR) {
+ SetupTest(R"(
+ int* target() {
+ int s = 10;
+ int* p = &s;
+ POINT(p1);
+ return p;
+ }
+ )");
+ EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, TrivialClassDestructorsUAR) {
+ SetupTest(R"(
+ class S {
+ View a, b;
+ };
+
+ S* target() {
+ S *ptr;
+ S s;
+ ptr = &s;
+ POINT(p1);
+ return ptr;
+ }
+ )");
+ EXPECT_THAT("s", HasLiveLoanAtExpiry("p1"));
+}
+
} // anonymous namespace
} // namespace clang::lifetimes::internal
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index de09086..5492146 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -294,7 +294,7 @@ TEST_F(SuppressionMappingTest, EmitCategoryIsExcluded) {
locForFile("foo.cpp")));
}
-TEST_F(SuppressionMappingTest, LongestMatchWins) {
+TEST_F(SuppressionMappingTest, LastMatchWins) {
llvm::StringLiteral SuppressionMappingFile = R"(
[unused]
src:*clang/*
@@ -327,10 +327,8 @@ TEST_F(SuppressionMappingTest, LongShortMatch) {
EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
locForFile("test/t1.cpp")));
-
- // FIXME: This is confusing.
- EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
- locForFile("lld/test/t2.cpp")));
+ EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+ locForFile("lld/test/t2.cpp")));
}
TEST_F(SuppressionMappingTest, ShortLongMatch) {
diff --git a/clang/unittests/Basic/SarifTest.cpp b/clang/unittests/Basic/SarifTest.cpp
index 089b6cb..42e8508 100644
--- a/clang/unittests/Basic/SarifTest.cpp
+++ b/clang/unittests/Basic/SarifTest.cpp
@@ -290,7 +290,7 @@ TEST_F(SarifDocumentWriterTest, checkSerializingResultsWithCustomRuleConfig) {
TEST_F(SarifDocumentWriterTest, checkSerializingArtifacts) {
// GIVEN:
const std::string ExpectedOutput =
- R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":40,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:///main.cpp"},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"message":{"text":"expected ';' after top level declarator"},"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})";
+ R"({"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":40,"location":{"index":0,"uri":"file:///main.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:///main.cpp"},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"message":{"text":"expected ';' after top level declarator"},"relatedLocations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"file:///main.cpp"},"region":{"endColumn":14,"startColumn":14,"startLine":3}}}],"ruleId":"clang.unittest","ruleIndex":0}],"tool":{"driver":{"fullName":"sarif test runner","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"sarif test","rules":[{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":"Example rule created during unit tests"},"id":"clang.unittest","name":"clang unit test"}],"version":"1.0.0"}}}],"version":"2.1.0"})";
SarifDocumentWriter Writer{SourceMgr};
const SarifRule &Rule =
@@ -312,12 +312,12 @@ TEST_F(SarifDocumentWriterTest, checkSerializingArtifacts) {
registerSource("/main.cpp", SourceText, /* IsMainFile = */ true);
CharSourceRange SourceCSR =
getFakeCharSourceRange(MainFileID, {3, 14}, {3, 14});
-
DiagLocs.push_back(SourceCSR);
const SarifResult &Result =
SarifResult::create(RuleIdx)
- .setLocations(DiagLocs)
+ .addLocations(DiagLocs)
+ .addRelatedLocations(DiagLocs)
.setDiagnosticMessage("expected ';' after top level declarator")
.setDiagnosticLevel(SarifResultLevel::Error);
Writer.appendResult(Result);
@@ -377,7 +377,7 @@ TEST_F(SarifDocumentWriterTest, checkSerializingCodeflows) {
unsigned RuleIdx = Writer.createRule(Rule);
const SarifResult &Result =
SarifResult::create(RuleIdx)
- .setLocations({DiagLoc})
+ .addLocations({DiagLoc})
.setDiagnosticMessage("Redefinition of 'foo'")
.setThreadFlows(Threadflows)
.setDiagnosticLevel(SarifResultLevel::Warning);
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 54c781a3..438a5c4 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -79,6 +79,7 @@ add_subdirectory(Basic)
add_subdirectory(Lex)
add_subdirectory(Parse)
add_subdirectory(Driver)
+add_subdirectory(DependencyScanning)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(Analysis)
add_subdirectory(StaticAnalyzer)
diff --git a/clang/unittests/DependencyScanning/CMakeLists.txt b/clang/unittests/DependencyScanning/CMakeLists.txt
new file mode 100644
index 0000000..4042582
--- /dev/null
+++ b/clang/unittests/DependencyScanning/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_clang_unittest(ClangDependencyScanningTests
+ DependencyScanningFilesystemTest.cpp
+ DependencyScanningWorkerTest.cpp
+ CLANG_LIBS
+ clangDependencyScanning
+ clangFrontend # For TextDiagnosticPrinter.
+ LLVM_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ Option
+ Support
+ )
diff --git a/clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp b/clang/unittests/DependencyScanning/DependencyScanningFilesystemTest.cpp
index 023c02dd..0e19541 100644
--- a/clang/unittests/Tooling/DependencyScanning/DependencyScanningFilesystemTest.cpp
+++ b/clang/unittests/DependencyScanning/DependencyScanningFilesystemTest.cpp
@@ -6,12 +6,45 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
+#include "clang/DependencyScanning/DependencyScanningFilesystem.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"
-using namespace clang::tooling::dependencies;
+using namespace clang::dependencies;
+
+TEST(DependencyScanningFilesystem, OpenFileAndGetBufferRepeatedly) {
+ auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ InMemoryFS->setCurrentWorkingDirectory("/");
+ InMemoryFS->addFile("/foo", 0, llvm::MemoryBuffer::getMemBuffer("content"));
+
+ DependencyScanningFilesystemSharedCache SharedCache;
+ DependencyScanningWorkerFilesystem DepFS(SharedCache, InMemoryFS);
+
+ auto FileOrErr1 = DepFS.openFileForRead("foo");
+ auto FileOrErr2 = DepFS.openFileForRead("foo");
+ ASSERT_EQ(FileOrErr1.getError(), std::error_code{});
+ ASSERT_EQ(FileOrErr1.getError(), std::error_code{});
+ std::unique_ptr<llvm::vfs::File> File1 = std::move(*FileOrErr1);
+ std::unique_ptr<llvm::vfs::File> File2 = std::move(*FileOrErr2);
+ ASSERT_NE(File1, nullptr);
+ ASSERT_NE(File2, nullptr);
+ auto BufOrErr11 = File1->getBuffer("buf11");
+ auto BufOrErr12 = File1->getBuffer("buf12");
+ auto BufOrErr21 = File1->getBuffer("buf21");
+ ASSERT_EQ(BufOrErr11.getError(), std::error_code{});
+ ASSERT_EQ(BufOrErr12.getError(), std::error_code{});
+ ASSERT_EQ(BufOrErr21.getError(), std::error_code{});
+ std::unique_ptr<llvm::MemoryBuffer> Buf11 = std::move(*BufOrErr11);
+ std::unique_ptr<llvm::MemoryBuffer> Buf12 = std::move(*BufOrErr12);
+ std::unique_ptr<llvm::MemoryBuffer> Buf21 = std::move(*BufOrErr21);
+ ASSERT_NE(Buf11, nullptr);
+ ASSERT_NE(Buf12, nullptr);
+ ASSERT_NE(Buf21, nullptr);
+ ASSERT_EQ(Buf11->getBuffer().data(), Buf12->getBuffer().data());
+ ASSERT_EQ(Buf11->getBuffer().data(), Buf21->getBuffer().data());
+ EXPECT_EQ(Buf11->getBuffer(), "content");
+}
TEST(DependencyScanningWorkerFilesystem, CacheStatusFailures) {
auto InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
diff --git a/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp
new file mode 100644
index 0000000..e6a5684
--- /dev/null
+++ b/clang/unittests/DependencyScanning/DependencyScanningWorkerTest.cpp
@@ -0,0 +1,97 @@
+//===- DependencyScanningWorkerTest.cpp -----------------------------------===//
+//
+// 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 "clang/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/DependencyScanning/DependencyScanningUtils.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace clang;
+using namespace dependencies;
+
+TEST(DependencyScanner, ScanDepsWithDiagConsumer) {
+ StringRef CWD = "/root";
+
+ auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ VFS->setCurrentWorkingDirectory(CWD);
+ auto Sept = llvm::sys::path::get_separator();
+ std::string HeaderPath =
+ std::string(llvm::formatv("{0}root{0}header.h", Sept));
+ std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
+ std::string AsmPath = std::string(llvm::formatv("{0}root{0}test.s", Sept));
+
+ VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
+ VFS->addFile(TestPath, 0,
+ llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));
+ VFS->addFile(AsmPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
+
+ DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
+ ScanningOutputFormat::Make);
+ DependencyScanningWorker Worker(Service, VFS);
+
+ llvm::DenseSet<ModuleID> AlreadySeen;
+ FullDependencyConsumer DC(AlreadySeen);
+ CallbackActionController AC(nullptr);
+
+ struct EnsureFinishedConsumer : public DiagnosticConsumer {
+ bool Finished = false;
+ void finish() override { Finished = true; }
+ };
+
+ {
+ // Check that a successful scan calls DiagConsumer.finish().
+ std::vector<std::string> Args = {"clang",
+ "-target",
+ "x86_64-apple-macosx10.7",
+ "-c",
+ "test.cpp",
+ "-o"
+ "test.cpp.o"};
+
+ EnsureFinishedConsumer DiagConsumer;
+ bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
+
+ EXPECT_TRUE(Success);
+ EXPECT_EQ(DiagConsumer.getNumErrors(), 0u);
+ EXPECT_TRUE(DiagConsumer.Finished);
+ }
+
+ {
+ // Check that an invalid command-line, which never enters the scanning
+ // action calls DiagConsumer.finish().
+ std::vector<std::string> Args = {"clang", "-invalid-arg"};
+ EnsureFinishedConsumer DiagConsumer;
+ bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
+
+ EXPECT_FALSE(Success);
+ EXPECT_GE(DiagConsumer.getNumErrors(), 1u);
+ EXPECT_TRUE(DiagConsumer.Finished);
+ }
+
+ {
+ // Check that a valid command line that produces no scanning jobs calls
+ // DiagConsumer.finish().
+ std::vector<std::string> Args = {"clang",
+ "-target",
+ "x86_64-apple-macosx10.7",
+ "-c",
+ "-x",
+ "assembler",
+ "test.s",
+ "-o"
+ "test.cpp.o"};
+
+ EnsureFinishedConsumer DiagConsumer;
+ bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
+
+ EXPECT_FALSE(Success);
+ EXPECT_EQ(DiagConsumer.getNumErrors(), 1u);
+ EXPECT_TRUE(DiagConsumer.Finished);
+ }
+}
diff --git a/clang/unittests/Driver/DXCModeTest.cpp b/clang/unittests/Driver/DXCModeTest.cpp
index 6227423..130da62 100644
--- a/clang/unittests/Driver/DXCModeTest.cpp
+++ b/clang/unittests/Driver/DXCModeTest.cpp
@@ -15,6 +15,7 @@
#include "clang/Basic/LLVM.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Driver/Compilation.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/ToolChain.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -131,8 +132,8 @@ TEST(DxcModeTest, ValidatorVersionValidation) {
TC.TranslateArgs(*DAL, "0", Action::OffloadKind::OFK_None)};
EXPECT_NE(TranslatedArgs, nullptr);
if (TranslatedArgs) {
- auto *A = TranslatedArgs->getLastArg(
- clang::driver::options::OPT_dxil_validator_version);
+ auto *A =
+ TranslatedArgs->getLastArg(clang::options::OPT_dxil_validator_version);
EXPECT_NE(A, nullptr);
if (A) {
EXPECT_STREQ(A->getValue(), "1.1");
diff --git a/clang/unittests/Driver/ToolChainTest.cpp b/clang/unittests/Driver/ToolChainTest.cpp
index afa17ff..8f53379 100644
--- a/clang/unittests/Driver/ToolChainTest.cpp
+++ b/clang/unittests/Driver/ToolChainTest.cpp
@@ -17,6 +17,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Driver/Compilation.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Driver/Driver.h"
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/ArrayRef.h"
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index d578fa7..fec1c48 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -1164,6 +1164,36 @@ TEST(ConfigParseTest, ParsesConfiguration) {
FormatStyle::BLS_Block);
CHECK_PARSE("Cpp11BracedListStyle: true", Cpp11BracedListStyle,
FormatStyle::BLS_AlignFirstComment);
+
+ constexpr FormatStyle::IntegerLiteralSeparatorStyle
+ ExpectedIntegerLiteralSeparatorStyle{/*Binary=*/2,
+ /*BinaryMinDigitInsert=*/5,
+ /*BinaryMaxDigitRemove=*/2,
+ /*Decimal=*/6,
+ /*DecimalMinDigitInsert=*/6,
+ /*DecimalMaxDigitRemove=*/3,
+ /*Hex=*/4,
+ /*HexMinDigitInsert=*/2,
+ /*HexMaxDigitRemove=*/1};
+ CHECK_PARSE("IntegerLiteralSeparator:\n"
+ " Binary: 2\n"
+ " BinaryMinDigitsInsert: 5\n"
+ " BinaryMaxDigitsRemove: 2\n"
+ " Decimal: 6\n"
+ " DecimalMinDigitsInsert: 6\n"
+ " DecimalMaxDigitsRemove: 3\n"
+ " Hex: 4\n"
+ " HexMinDigitsInsert: 2\n"
+ " HexMaxDigitsRemove: 1",
+ IntegerLiteralSeparator, ExpectedIntegerLiteralSeparatorStyle);
+
+ // Backward compatibility:
+ CHECK_PARSE_NESTED_VALUE("BinaryMinDigits: 6", IntegerLiteralSeparator,
+ BinaryMinDigitsInsert, 6);
+ CHECK_PARSE_NESTED_VALUE("DecimalMinDigits: 5", IntegerLiteralSeparator,
+ DecimalMinDigitsInsert, 5);
+ CHECK_PARSE_NESTED_VALUE("HexMinDigits: 5", IntegerLiteralSeparator,
+ HexMinDigitsInsert, 5);
}
TEST(ConfigParseTest, ParsesConfigurationWithLanguages) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index c9446fa..3ff7840 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -19851,6 +19851,14 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) {
" Test &operator=(const Test &) = default;\n"
"};",
Alignment);
+
+ // The comment to the right should still align right.
+ verifyFormat("void foo(int name, // name\n"
+ " float name, // name\n"
+ " int name) // name\n"
+ "{}",
+ Alignment);
+
unsigned OldColumnLimit = Alignment.ColumnLimit;
// We need to set ColumnLimit to zero, in order to stress nested alignments,
// otherwise the function parameters will be re-flowed onto a single line.
@@ -20681,6 +20689,16 @@ TEST_F(FormatTest, AlignWithLineBreaks) {
"}",
Style);
+ verifyFormat("auto someLongName = 3;\n"
+ "auto x = someLongExpression //\n"
+ " | ranges::views::values;",
+ Style);
+ verifyFormat(
+ "veryverylongvariablename = somethingelse;\n"
+ "shortervariablename = anotherverylonglonglongvariablename + //\n"
+ " somevariablethatwastoolongtofitonthesamerow;",
+ Style);
+
// clang-format off
verifyFormat("void foo() {\n"
" const int capacityBefore = Entries.capacity();\n"
@@ -20754,6 +20772,42 @@ TEST_F(FormatTest, AlignWithLineBreaks) {
Style);
// clang-format on
+ // The start of the closure is indented from the start of the line. It should
+ // not move with the equal sign.
+ Style.ContinuationIndentWidth = 6;
+ Style.IndentWidth = 8;
+ Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+ Style.BraceWrapping.BeforeLambdaBody = true;
+ Style.BraceWrapping.IndentBraces = true;
+ Style.ColumnLimit = 32;
+ verifyFormat("auto aaaaaaaaaaa = {};\n"
+ "b = []() constexpr\n"
+ " -> aaaaaaaaaaaaaaaaaaaaaaa\n"
+ " {\n"
+ " return {}; //\n"
+ " };",
+ Style);
+ verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n"
+ "b = []()\n"
+ " {\n"
+ " return; //\n"
+ " };",
+ Style);
+ Style.ColumnLimit = 33;
+ verifyFormat("auto aaaaaaaaaaa = {};\n"
+ "b = []() constexpr\n"
+ " -> aaaaaaaaaaaaaaaaaaaaaaa\n"
+ " {\n"
+ " return {}; //\n"
+ " };",
+ Style);
+ verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n"
+ "b = []()\n"
+ " {\n"
+ " return; //\n"
+ " };",
+ Style);
+
Style = getLLVMStyleWithColumns(20);
Style.AlignConsecutiveAssignments.Enabled = true;
Style.IndentWidth = 4;
@@ -20818,6 +20872,13 @@ TEST_F(FormatTest, AlignWithLineBreaks) {
Style);
Style.AlignConsecutiveAssignments.Enabled = true;
+ verifyFormat("float i2 = 0;\n"
+ "auto v = false ? type{}\n"
+ " : type{\n"
+ " 1,\n"
+ " };",
+ Style);
+
Style.ColumnLimit = 15;
verifyFormat("int i1 = 1;\n"
"k = bar(\n"
@@ -20878,6 +20939,12 @@ TEST_F(FormatTest, AlignWithInitializerPeriods) {
"});",
Style);
+ verifyFormat("auto aaaaaaaaaaaaaaaaaaaaa = {};\n"
+ "auto b = {.a = {\n"
+ " .a = 0,\n"
+ " }};",
+ Style);
+
Style.AlignConsecutiveAssignments.Enabled = false;
Style.AlignConsecutiveDeclarations.Enabled = true;
verifyFormat("void foo3(void) {\n"
@@ -27320,6 +27387,7 @@ TEST_F(FormatTest, Cpp20ModulesSupport) {
verifyFormat("export", Style);
verifyFormat("import /* not keyword */ = val ? 2 : 1;");
+ verifyFormat("_world->import<engine_module>();");
}
TEST_F(FormatTest, CoroutineForCoawait) {
diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp
index cf8143a..c685c55 100644
--- a/clang/unittests/Format/FormatTestObjC.cpp
+++ b/clang/unittests/Format/FormatTestObjC.cpp
@@ -876,6 +876,32 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) {
verifyFormat("aaaaaa = [aa aa:aa\n"
" aa:aa];");
+ Style.AlignConsecutiveAssignments.Enabled = true;
+ // When the method name and parameters are on their own lines, their positions
+ // only depend on the continuation indentation configuration, not where the
+ // square bracket is. Thus they should not move with the square bracket in the
+ // alignment step.
+ verifyFormat("aaaaaa = [aa aa:aa\n"
+ " aa:aa];\n"
+ "a = [a //\n"
+ " aaaaaaa:aa];");
+ verifyFormat("aaaaaa = [aa aa:aa\n"
+ " aa:aa];\n"
+ "aaaaa = [a //\n"
+ " a:aa\n"
+ " aaaaaaa:aa];");
+ // When the method name is on the same line as the square bracket, the
+ // positions of the parameters depend on where the square bracket is. Thus
+ // they should move with the square bracket in the alignment step.
+ verifyFormat("aaaaa = [a aa:aa\n"
+ " aa:aa];\n"
+ "aaaaaa = [aa aa:aa\n"
+ " aa:aa];\n"
+ "aaaaa = [a aa:aa\n"
+ " aaaaaaaaaa:aa];");
+
+ Style.AlignConsecutiveAssignments.Enabled = false;
+
// Message receiver taking multiple lines.
// Non-corner case.
verifyFormat("[[object block:^{\n"
diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp
index 63e2cadf..f407fc3 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -423,6 +423,11 @@ TEST_F(FormatTestVerilog, Declaration) {
verifyFormat("wire (strong1, pull0) mynet, mynet1 = enable;");
verifyFormat("wire (strong1, pull0) mynet, //\n"
" mynet1 = enable;");
+
+ // The type or variable can be a C++ keyword.
+ verifyFormat("private mynet;");
+ verifyFormat("switch mynet;");
+ verifyFormat("wire try;");
}
TEST_F(FormatTestVerilog, Delay) {
@@ -676,6 +681,16 @@ TEST_F(FormatTestVerilog, Hierarchy) {
" endprogram\n"
"endmodule");
// Test that an extern declaration doesn't change the indentation.
+ verifyFormat("import \"DPI-C\" context MyCFunc = function integer MapID\n"
+ " (int portID);\n"
+ "x = x;");
+ verifyFormat("export \"DPI-C\" function exported_sv_func;\n"
+ "x = x;");
+ verifyFormat("import \"DPI-C\" function void f1\n"
+ " (input int i1,\n"
+ " pair i2,\n"
+ " output logic [63 : 0] o3);\n"
+ "x = x;");
verifyFormat("extern module x;\n"
"x = x;");
// Test complex headers
diff --git a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
index 53b6dd8..21cdab2 100644
--- a/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
+++ b/clang/unittests/Format/IntegerLiteralSeparatorTest.cpp
@@ -137,34 +137,34 @@ TEST_F(IntegerLiteralSeparatorTest, UnderscoreAsSeparator) {
verifyFormat("o = 0o400000000000000003n;", Style);
}
-TEST_F(IntegerLiteralSeparatorTest, MinDigits) {
+TEST_F(IntegerLiteralSeparatorTest, MinDigitsInsert) {
FormatStyle Style = getLLVMStyle();
Style.IntegerLiteralSeparator.Binary = 3;
Style.IntegerLiteralSeparator.Decimal = 3;
Style.IntegerLiteralSeparator.Hex = 2;
- Style.IntegerLiteralSeparator.BinaryMinDigits = 7;
+ Style.IntegerLiteralSeparator.BinaryMinDigitsInsert = 7;
verifyFormat("b1 = 0b101101;\n"
"b2 = 0b1'101'101;",
"b1 = 0b101'101;\n"
"b2 = 0b1101101;",
Style);
- Style.IntegerLiteralSeparator.DecimalMinDigits = 5;
+ Style.IntegerLiteralSeparator.DecimalMinDigitsInsert = 5;
verifyFormat("d1 = 2023;\n"
"d2 = 10'000;",
"d1 = 2'023;\n"
"d2 = 100'00;",
Style);
- Style.IntegerLiteralSeparator.DecimalMinDigits = 3;
+ Style.IntegerLiteralSeparator.DecimalMinDigitsInsert = 3;
verifyFormat("d1 = 123;\n"
"d2 = 1'234;",
"d1 = 12'3;\n"
"d2 = 12'34;",
Style);
- Style.IntegerLiteralSeparator.HexMinDigits = 6;
+ Style.IntegerLiteralSeparator.HexMinDigitsInsert = 6;
verifyFormat("h1 = 0xABCDE;\n"
"h2 = 0xAB'CD'EF;",
"h1 = 0xA'BC'DE;\n"
@@ -243,6 +243,23 @@ TEST_F(IntegerLiteralSeparatorTest, FloatingPoint) {
Style);
}
+TEST_F(IntegerLiteralSeparatorTest, MaxDigitsRemove) {
+ auto Style = getLLVMStyle();
+ Style.IntegerLiteralSeparator.Decimal = 3;
+ Style.IntegerLiteralSeparator.DecimalMaxDigitsRemove = 4;
+ Style.IntegerLiteralSeparator.DecimalMinDigitsInsert = 7;
+
+ verifyFormat("d1 = 123456;\n"
+ "d2 = 1234'56;",
+ Style);
+
+ verifyFormat("d0 = 2023;\n"
+ "d3 = 5'000'000;",
+ "d0 = 20'2'3;\n"
+ "d3 = 5000000;",
+ Style);
+}
+
} // namespace
} // namespace test
} // namespace format
diff --git a/clang/unittests/Format/QualifierFixerTest.cpp b/clang/unittests/Format/QualifierFixerTest.cpp
index 58e64ff..86c69a5 100644
--- a/clang/unittests/Format/QualifierFixerTest.cpp
+++ b/clang/unittests/Format/QualifierFixerTest.cpp
@@ -215,6 +215,8 @@ TEST_F(QualifierFixerTest, RightQualifier) {
Style);
verifyFormat("void foo() const override;", Style);
verifyFormat("void foo() const override LLVM_READONLY;", Style);
+ verifyFormat("MOCK_METHOD(ReturnType, myMethod, (int), (const override));",
+ Style);
verifyFormat("void foo() const final;", Style);
verifyFormat("void foo() const final LLVM_READONLY;", Style);
verifyFormat("void foo() const LLVM_READONLY;", Style);
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 815c79e..a3c0a32 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -1392,6 +1392,15 @@ TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
EXPECT_TOKEN(Tokens[19], tok::l_brace, TT_RequiresExpressionLBrace);
Tokens =
+ annotate("template <typename... Ts>\n"
+ " requires requires {\n"
+ " requires std::same_as<int, SomeTemplate<void(Ts &&...)>>;\n"
+ " }\n"
+ "void Foo();");
+ ASSERT_EQ(Tokens.size(), 34u) << Tokens;
+ EXPECT_TOKEN(Tokens[21], tok::ampamp, TT_PointerOrReference);
+
+ Tokens =
annotate("template <class A, class B> concept C ="
"std::same_as<std::iter_value_t<A>, std::iter_value_t<B>>;");
ASSERT_EQ(Tokens.size(), 31u) << Tokens;
@@ -2313,6 +2322,12 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) {
EXPECT_TOKEN(Tokens[3], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_LambdaDefinitionLParen);
EXPECT_TOKEN(Tokens[10], tok::l_square, TT_ArraySubscriptLSquare);
+
+ Tokens = annotate("foo = bar * [] { return 2; }();");
+ ASSERT_EQ(Tokens.size(), 15u) << Tokens;
+ EXPECT_TOKEN(Tokens[3], tok::star, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[4], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace);
}
TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {
@@ -2909,6 +2924,13 @@ TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) {
EXPECT_EQ(Tokens[0]->TokenText, R"(\busa+index\
+)");
EXPECT_TOKEN(Tokens[1], tok::semi, TT_Unknown);
+
+ // A C++ keyword should be treated as an identifier.
+ Tokens = Annotate("volatile delete;");
+ ASSERT_EQ(Tokens.size(), 4u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::identifier, TT_Unknown);
+ EXPECT_TOKEN(Tokens[1], tok::identifier, TT_StartOfName);
+
// An escaped newline should not be treated as an escaped identifier.
Tokens = Annotate("\\\n");
ASSERT_EQ(Tokens.size(), 1u) << Tokens;
@@ -3361,6 +3383,11 @@ TEST_F(TokenAnnotatorTest, UnderstandDesignatedInitializers) {
ASSERT_EQ(Tokens.size(), 14u) << Tokens;
EXPECT_TOKEN(Tokens[6], tok::l_square, TT_DesignatedInitializerLSquare);
EXPECT_BRACE_KIND(Tokens[9], BK_BracedInit);
+
+ Tokens = annotate("Foo foo[] = {[0] = 1, [1] = 2};");
+ ASSERT_EQ(Tokens.size(), 20u) << Tokens;
+ EXPECT_TOKEN(Tokens[6], tok::l_square, TT_DesignatedInitializerLSquare);
+ EXPECT_TOKEN(Tokens[12], tok::l_square, TT_DesignatedInitializerLSquare);
}
TEST_F(TokenAnnotatorTest, UnderstandsJavaScript) {
diff --git a/clang/unittests/Frontend/ASTUnitTest.cpp b/clang/unittests/Frontend/ASTUnitTest.cpp
index dfdbe90..bf9e4e1 100644
--- a/clang/unittests/Frontend/ASTUnitTest.cpp
+++ b/clang/unittests/Frontend/ASTUnitTest.cpp
@@ -9,6 +9,8 @@
#include <fstream>
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateASTUnitFromArgs.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
@@ -173,7 +175,7 @@ TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) {
auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
std::unique_ptr<clang::ASTUnit> ErrUnit;
- std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine(
+ std::unique_ptr<ASTUnit> AST = CreateASTUnitFromCommandLine(
&Args[0], &Args[4], PCHContainerOps, DiagOpts, Diags, "", false, "",
false, CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false,
false, SkipFunctionBodiesScope::None, false, true, false, false,
@@ -201,7 +203,7 @@ TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) {
auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
std::unique_ptr<clang::ASTUnit> ErrUnit;
- std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine(
+ std::unique_ptr<ASTUnit> AST = CreateASTUnitFromCommandLine(
&Args[0], &Args[4], PCHContainerOps, DiagOpts, Diags, "", false, "",
false, CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false,
false, SkipFunctionBodiesScope::None, false, true, false, false,
diff --git a/clang/unittests/Frontend/CompilerInstanceTest.cpp b/clang/unittests/Frontend/CompilerInstanceTest.cpp
index cd3fefa..39d35b4 100644
--- a/clang/unittests/Frontend/CompilerInstanceTest.cpp
+++ b/clang/unittests/Frontend/CompilerInstanceTest.cpp
@@ -8,6 +8,7 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
diff --git a/clang/unittests/Frontend/UtilsTest.cpp b/clang/unittests/Frontend/UtilsTest.cpp
index fc411e4..a82733d 100644
--- a/clang/unittests/Frontend/UtilsTest.cpp
+++ b/clang/unittests/Frontend/UtilsTest.cpp
@@ -9,6 +9,7 @@
#include "clang/Frontend/Utils.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/TargetOptions.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Lex/PreprocessorOptions.h"
diff --git a/clang/unittests/Lex/PPCallbacksTest.cpp b/clang/unittests/Lex/PPCallbacksTest.cpp
index 990689c..9533fbc 100644
--- a/clang/unittests/Lex/PPCallbacksTest.cpp
+++ b/clang/unittests/Lex/PPCallbacksTest.cpp
@@ -437,6 +437,7 @@ TEST_F(PPCallbacksTest, FileNotFoundSkipped) {
PreprocessorOptions PPOpts;
HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
+ unsigned int NumCalls = 0;
DiagnosticConsumer *DiagConsumer = new DiagnosticConsumer;
DiagnosticsEngine FileNotFoundDiags(DiagID, DiagOpts, DiagConsumer);
Preprocessor PP(PPOpts, FileNotFoundDiags, LangOpts, SourceMgr, HeaderInfo,
@@ -445,21 +446,68 @@ TEST_F(PPCallbacksTest, FileNotFoundSkipped) {
class FileNotFoundCallbacks : public PPCallbacks {
public:
- unsigned int NumCalls = 0;
+ unsigned int &NumCalls;
+
+ FileNotFoundCallbacks(unsigned int &NumCalls) : NumCalls(NumCalls) {}
+
bool FileNotFound(StringRef FileName) override {
NumCalls++;
return FileName == "skipped.h";
}
};
- auto *Callbacks = new FileNotFoundCallbacks;
- PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
+ PP.addPPCallbacks(std::make_unique<FileNotFoundCallbacks>(NumCalls));
+
+ // Lex source text.
+ PP.EnterMainSourceFile();
+ PP.LexTokensUntilEOF();
+
+ ASSERT_EQ(1u, NumCalls);
+ ASSERT_EQ(0u, DiagConsumer->getNumErrors());
+}
+
+TEST_F(PPCallbacksTest, EmbedFileNotFoundChained) {
+ const char *SourceText = "#embed \"notfound.h\"\n";
+
+ std::unique_ptr<llvm::MemoryBuffer> SourceBuf =
+ llvm::MemoryBuffer::getMemBuffer(SourceText);
+ SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
+
+ unsigned int NumCalls = 0;
+ HeaderSearchOptions HSOpts;
+ TrivialModuleLoader ModLoader;
+ PreprocessorOptions PPOpts;
+ HeaderSearch HeaderInfo(HSOpts, SourceMgr, Diags, LangOpts, Target.get());
+
+ DiagnosticConsumer *DiagConsumer = new DiagnosticConsumer;
+ DiagnosticsEngine EmbedFileNotFoundDiags(DiagID, DiagOpts, DiagConsumer);
+ Preprocessor PP(PPOpts, EmbedFileNotFoundDiags, LangOpts, SourceMgr,
+ HeaderInfo, ModLoader, /*IILookup=*/nullptr,
+ /*OwnsHeaderSearch=*/false);
+ PP.Initialize(*Target);
+
+ class EmbedFileNotFoundCallbacks : public PPCallbacks {
+ public:
+ unsigned int &NumCalls;
+
+ EmbedFileNotFoundCallbacks(unsigned int &NumCalls) : NumCalls(NumCalls) {}
+
+ bool EmbedFileNotFound(StringRef FileName) override {
+ NumCalls++;
+ return true;
+ }
+ };
+
+ // Add two instances of `EmbedFileNotFoundCallbacks` to ensure the
+ // preprocessor is using an instance of `PPChainedCallbaks`.
+ PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(NumCalls));
+ PP.addPPCallbacks(std::make_unique<EmbedFileNotFoundCallbacks>(NumCalls));
// Lex source text.
PP.EnterMainSourceFile();
PP.LexTokensUntilEOF();
- ASSERT_EQ(1u, Callbacks->NumCalls);
+ ASSERT_EQ(2u, NumCalls);
ASSERT_EQ(0u, DiagConsumer->getNumErrors());
}
diff --git a/clang/unittests/Sema/CMakeLists.txt b/clang/unittests/Sema/CMakeLists.txt
index b61ed8c..188f613 100644
--- a/clang/unittests/Sema/CMakeLists.txt
+++ b/clang/unittests/Sema/CMakeLists.txt
@@ -13,6 +13,7 @@ add_distinct_clang_unittest(SemaTests
clangAST
clangASTMatchers
clangBasic
+ clangDriver
clangFrontend
clangParse
clangSema
diff --git a/clang/unittests/Sema/SemaNoloadLookupTest.cpp b/clang/unittests/Sema/SemaNoloadLookupTest.cpp
index e565372..3944269 100644
--- a/clang/unittests/Sema/SemaNoloadLookupTest.cpp
+++ b/clang/unittests/Sema/SemaNoloadLookupTest.cpp
@@ -10,6 +10,7 @@
#include "clang/AST/DeclarationName.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/ForceCheckFileInputTest.cpp b/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
index edf33ae..b76dcfe 100644
--- a/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
+++ b/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
@@ -9,6 +9,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
index d7b5549..f55925a 100644
--- a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
+++ b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/ModuleCacheTest.cpp b/clang/unittests/Serialization/ModuleCacheTest.cpp
index e9b8da3..df26e54 100644
--- a/clang/unittests/Serialization/ModuleCacheTest.cpp
+++ b/clang/unittests/Serialization/ModuleCacheTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/NoCommentsTest.cpp b/clang/unittests/Serialization/NoCommentsTest.cpp
index 01bb699..444a082 100644
--- a/clang/unittests/Serialization/NoCommentsTest.cpp
+++ b/clang/unittests/Serialization/NoCommentsTest.cpp
@@ -9,6 +9,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/PreambleInNamedModulesTest.cpp b/clang/unittests/Serialization/PreambleInNamedModulesTest.cpp
index 55ee728..b826f20 100644
--- a/clang/unittests/Serialization/PreambleInNamedModulesTest.cpp
+++ b/clang/unittests/Serialization/PreambleInNamedModulesTest.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
index 743f851..2be01de 100644
--- a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
+++ b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
@@ -9,6 +9,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt
index 106c6b9..8c8b222 100644
--- a/clang/unittests/Tooling/CMakeLists.txt
+++ b/clang/unittests/Tooling/CMakeLists.txt
@@ -13,8 +13,7 @@ add_clang_unittest(ToolingTests
LookupTest.cpp
QualTypeNamesTest.cpp
RangeSelectorTest.cpp
- DependencyScanning/DependencyScannerTest.cpp
- DependencyScanning/DependencyScanningFilesystemTest.cpp
+ DependencyScannerTest.cpp
RecursiveASTVisitorTests/Attr.cpp
RecursiveASTVisitorTests/BitfieldInitializer.cpp
RecursiveASTVisitorTests/CallbacksLeaf.cpp
diff --git a/clang/unittests/Tooling/DependencyScanning/DependencyScannerTest.cpp b/clang/unittests/Tooling/DependencyScannerTest.cpp
index 4523af3..da47252 100644
--- a/clang/unittests/Tooling/DependencyScanning/DependencyScannerTest.cpp
+++ b/clang/unittests/Tooling/DependencyScannerTest.cpp
@@ -9,13 +9,13 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
+#include "clang/DependencyScanning/DependencyScanningWorker.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CompilationDatabase.h"
-#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
-#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/Tooling/DependencyScanningTool.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/MC/TargetRegistry.h"
@@ -304,84 +304,3 @@ TEST(DependencyScanner, ScanDepsWithModuleLookup) {
EXPECT_TRUE(!llvm::is_contained(InterceptFS->StatPaths, OtherPath));
EXPECT_EQ(InterceptFS->ReadFiles, std::vector<std::string>{"test.m"});
}
-
-TEST(DependencyScanner, ScanDepsWithDiagConsumer) {
- StringRef CWD = "/root";
-
- auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
- VFS->setCurrentWorkingDirectory(CWD);
- auto Sept = llvm::sys::path::get_separator();
- std::string HeaderPath =
- std::string(llvm::formatv("{0}root{0}header.h", Sept));
- std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));
- std::string AsmPath = std::string(llvm::formatv("{0}root{0}test.s", Sept));
-
- VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));
- VFS->addFile(TestPath, 0,
- llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));
- VFS->addFile(AsmPath, 0, llvm::MemoryBuffer::getMemBuffer(""));
-
- DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,
- ScanningOutputFormat::Make);
- DependencyScanningWorker Worker(Service, VFS);
-
- llvm::DenseSet<ModuleID> AlreadySeen;
- FullDependencyConsumer DC(AlreadySeen);
- CallbackActionController AC(nullptr);
-
- struct EnsureFinishedConsumer : public DiagnosticConsumer {
- bool Finished = false;
- void finish() override { Finished = true; }
- };
-
- {
- // Check that a successful scan calls DiagConsumer.finish().
- std::vector<std::string> Args = {"clang",
- "-target",
- "x86_64-apple-macosx10.7",
- "-c",
- "test.cpp",
- "-o"
- "test.cpp.o"};
-
- EnsureFinishedConsumer DiagConsumer;
- bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
-
- EXPECT_TRUE(Success);
- EXPECT_EQ(DiagConsumer.getNumErrors(), 0u);
- EXPECT_TRUE(DiagConsumer.Finished);
- }
-
- {
- // Check that an invalid command-line, which never enters the scanning
- // action calls DiagConsumer.finish().
- std::vector<std::string> Args = {"clang", "-invalid-arg"};
- EnsureFinishedConsumer DiagConsumer;
- bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
-
- EXPECT_FALSE(Success);
- EXPECT_GE(DiagConsumer.getNumErrors(), 1u);
- EXPECT_TRUE(DiagConsumer.Finished);
- }
-
- {
- // Check that a valid command line that produces no scanning jobs calls
- // DiagConsumer.finish().
- std::vector<std::string> Args = {"clang",
- "-target",
- "x86_64-apple-macosx10.7",
- "-c",
- "-x",
- "assembler",
- "test.s",
- "-o"
- "test.cpp.o"};
-
- EnsureFinishedConsumer DiagConsumer;
- bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);
-
- EXPECT_FALSE(Success);
- EXPECT_EQ(DiagConsumer.getNumErrors(), 1u);
- EXPECT_TRUE(DiagConsumer.Finished);
- }
-}
diff --git a/clang/unittests/Tooling/RangeSelectorTest.cpp b/clang/unittests/Tooling/RangeSelectorTest.cpp
index a1fcbb0..9e83fa1 100644
--- a/clang/unittests/Tooling/RangeSelectorTest.cpp
+++ b/clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -327,6 +327,45 @@ TEST(RangeSelectorTest, EncloseOpGeneralParsed) {
EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7"));
}
+TEST(RangeSelectorTest, MergeOp) {
+ StringRef Code = R"cc(
+ int f(int x, int y, int z) { return 3; }
+ int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
+ )cc";
+ auto Matcher = callExpr(hasArgument(0, expr().bind("a0")),
+ hasArgument(1, expr().bind("a1")),
+ hasArgument(2, expr().bind("a2")));
+ RangeSelector R = merge(node("a0"), node("a1"));
+ TestMatch Match = matchCode(Code, Matcher);
+ EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7"));
+ // Test the merge of two non-contiguous and out-of-order token-ranges.
+ R = merge(node("a2"), node("a0"));
+ EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7 /* comment */, 9"));
+ // Test the merge of a token-range (expr node) with a char-range (before).
+ R = merge(node("a1"), before(node("a0")));
+ EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7"));
+ // Test the merge of two char-ranges.
+ R = merge(before(node("a0")), before(node("a1")));
+ EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, "));
+}
+
+TEST(RangeSelectorTest, MergeOpParsed) {
+ StringRef Code = R"cc(
+ int f(int x, int y, int z) { return 3; }
+ int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
+ )cc";
+ auto Matcher = callExpr(hasArgument(0, expr().bind("a0")),
+ hasArgument(1, expr().bind("a1")),
+ hasArgument(2, expr().bind("a2")));
+ auto R = parseRangeSelector(R"rs(merge(node("a0"), node("a1")))rs");
+ ASSERT_THAT_EXPECTED(R, llvm::Succeeded());
+ TestMatch Match = matchCode(Code, Matcher);
+ EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7"));
+ R = parseRangeSelector(R"rs(merge(node("a2"), node("a1")))rs");
+ ASSERT_THAT_EXPECTED(R, llvm::Succeeded());
+ EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("7 /* comment */, 9"));
+}
+
TEST(RangeSelectorTest, NodeOpStatement) {
StringRef Code = "int f() { return 3; }";
TestMatch Match = matchCode(Code, returnStmt().bind("id"));
@@ -339,6 +378,13 @@ TEST(RangeSelectorTest, NodeOpExpression) {
EXPECT_THAT_EXPECTED(select(node("id"), Match), HasValue("3"));
}
+TEST(RangeSelectorTest, NodeOpTypeLoc) {
+ StringRef Code = "namespace ns {struct Foo{};} ns::Foo a;";
+ TestMatch Match =
+ matchCode(Code, varDecl(hasTypeLoc(typeLoc().bind("typeloc"))));
+ EXPECT_THAT_EXPECTED(select(node("typeloc"), Match), HasValue("ns::Foo"));
+}
+
TEST(RangeSelectorTest, StatementOp) {
StringRef Code = "int f() { return 3; }";
TestMatch Match = matchCode(Code, expr().bind("id"));
diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp b/clang/unittests/Tooling/SourceCodeTest.cpp
index 549b777..a998954 100644
--- a/clang/unittests/Tooling/SourceCodeTest.cpp
+++ b/clang/unittests/Tooling/SourceCodeTest.cpp
@@ -510,10 +510,14 @@ TEST(SourceCodeTest, EditInvolvingExpansionIgnoringExpansionShouldFail) {
#define M1(x) x(1)
#define M2(x, y) x ## y
#define M3(x) foobar(x)
+#define M4(x, y) x y
+#define M5(x) x
int foobar(int);
int a = M1(foobar);
int b = M2(foo, bar(2));
int c = M3(3);
+int d = M4(foobar, (4));
+int e = M5(foobar) (5);
)cpp");
CallsVisitor Visitor;
diff --git a/clang/unittests/Tooling/Syntax/TokensTest.cpp b/clang/unittests/Tooling/Syntax/TokensTest.cpp
index 47184cb..468ca5d 100644
--- a/clang/unittests/Tooling/Syntax/TokensTest.cpp
+++ b/clang/unittests/Tooling/Syntax/TokensTest.cpp
@@ -20,6 +20,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.def"
#include "clang/Basic/TokenKinds.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/Utils.h"
diff --git a/clang/unittests/Tooling/Syntax/TreeTestBase.cpp b/clang/unittests/Tooling/Syntax/TreeTestBase.cpp
index b2be64f..dad7585 100644
--- a/clang/unittests/Tooling/Syntax/TreeTestBase.cpp
+++ b/clang/unittests/Tooling/Syntax/TreeTestBase.cpp
@@ -13,6 +13,7 @@
#include "TreeTestBase.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Driver/CreateInvocationFromArgs.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendAction.h"