aboutsummaryrefslogtreecommitdiff
path: root/clang/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'clang/unittests')
-rw-r--r--clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp143
-rw-r--r--clang/unittests/Format/FormatTest.cpp6
-rw-r--r--clang/unittests/Format/FormatTestMacroExpansion.cpp21
-rw-r--r--clang/unittests/Format/MacroCallReconstructorTest.cpp129
4 files changed, 222 insertions, 77 deletions
diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
index 3bca9cc..34f9b0b 100644
--- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -77,17 +77,33 @@ protected:
return runDataflowAnalysis(*CFCtx, Analysis, Env);
}
+ /// Returns the `CFGBlock` containing `S` (and asserts that it exists).
+ const CFGBlock *blockForStmt(const Stmt &S) {
+ const CFGBlock *Block = CFCtx->getStmtToBlock().lookup(&S);
+ assert(Block != nullptr);
+ return Block;
+ }
+
template <typename StateT>
const StateT &
blockStateForStmt(const std::vector<std::optional<StateT>> &BlockStates,
- const Stmt *S) {
- const CFGBlock *Block = CFCtx->getStmtToBlock().lookup(S);
- assert(Block != nullptr);
- const std::optional<StateT> &MaybeState = BlockStates[Block->getBlockID()];
+ const Stmt &S) {
+ const std::optional<StateT> &MaybeState =
+ BlockStates[blockForStmt(S)->getBlockID()];
assert(MaybeState.has_value());
return *MaybeState;
}
+ /// Returns the first node that matches `Matcher` (and asserts that the match
+ /// was successful, i.e. the returned node is not null).
+ template <typename NodeT, typename MatcherT>
+ const NodeT &matchNode(MatcherT Matcher) {
+ const auto *Node = selectFirst<NodeT>(
+ "node", match(Matcher.bind("node"), AST->getASTContext()));
+ assert(Node != nullptr);
+ return *Node;
+ }
+
std::unique_ptr<ASTUnit> AST;
std::unique_ptr<ControlFlowContext> CFCtx;
std::unique_ptr<DataflowAnalysisContext> DACtx;
@@ -130,6 +146,79 @@ TEST_F(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) {
" (Lifetime ends)\n")));
}
+// Tests for the statement-to-block map.
+using StmtToBlockTest = DataflowAnalysisTest;
+
+TEST_F(StmtToBlockTest, ConditionalOperator) {
+ std::string Code = R"(
+ void target(bool b) {
+ int i = b ? 1 : 0;
+ }
+ )";
+ ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
+ Code, [](ASTContext &C) { return NoopAnalysis(C); })
+ .takeError(),
+ llvm::Succeeded());
+
+ const auto &IDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("i")))));
+ const auto &ConditionalOp =
+ matchNode<ConditionalOperator>(conditionalOperator());
+
+ // The conditional operator should be associated with the same block as the
+ // `DeclStmt` for `i`. (Specifically, the conditional operator should not be
+ // associated with the block for which it is the terminator.)
+ EXPECT_EQ(blockForStmt(IDecl), blockForStmt(ConditionalOp));
+}
+
+TEST_F(StmtToBlockTest, LogicalAnd) {
+ std::string Code = R"(
+ void target(bool b1, bool b2) {
+ bool b = b1 && b2;
+ }
+ )";
+ ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
+ Code, [](ASTContext &C) { return NoopAnalysis(C); })
+ .takeError(),
+ llvm::Succeeded());
+
+ const auto &BDecl = matchNode<DeclStmt>(declStmt(has(varDecl(hasName("b")))));
+ const auto &AndOp =
+ matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&")));
+
+ // The `&&` operator should be associated with the same block as the
+ // `DeclStmt` for `b`. (Specifically, the `&&` operator should not be
+ // associated with the block for which it is the terminator.)
+ EXPECT_EQ(blockForStmt(BDecl), blockForStmt(AndOp));
+}
+
+TEST_F(StmtToBlockTest, IfStatementWithLogicalAnd) {
+ std::string Code = R"(
+ void target(bool b1, bool b2) {
+ if (b1 && b2)
+ ;
+ }
+ )";
+ ASSERT_THAT_ERROR(runAnalysis<NoopAnalysis>(
+ Code, [](ASTContext &C) { return NoopAnalysis(C); })
+ .takeError(),
+ llvm::Succeeded());
+
+ const auto &If = matchNode<IfStmt>(ifStmt());
+ const auto &B2 =
+ matchNode<DeclRefExpr>(declRefExpr(to(varDecl(hasName("b2")))));
+ const auto &AndOp =
+ matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&")));
+
+ // The if statement is the terminator for the block that contains both `b2`
+ // and the `&&` operator (which appears only as a terminator condition, not
+ // as a regular `CFGElement`).
+ const CFGBlock *IfBlock = blockForStmt(If);
+ const CFGBlock *B2Block = blockForStmt(B2);
+ const CFGBlock *AndOpBlock = blockForStmt(AndOp);
+ EXPECT_EQ(IfBlock, B2Block);
+ EXPECT_EQ(IfBlock, AndOpBlock);
+}
+
// Tests that check we discard state for expressions correctly.
using DiscardExprStateTest = DataflowAnalysisTest;
@@ -144,25 +233,20 @@ TEST_F(DiscardExprStateTest, WhileStatement) {
auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
Code, [](ASTContext &C) { return NoopAnalysis(C); }));
- auto *NotEqOp = selectFirst<BinaryOperator>(
- "op", match(binaryOperator(hasOperatorName("!=")).bind("op"),
- AST->getASTContext()));
- ASSERT_NE(NotEqOp, nullptr);
-
- auto *CallFoo = selectFirst<CallExpr>(
- "call", match(callExpr(callee(functionDecl(hasName("foo")))).bind("call"),
- AST->getASTContext()));
- ASSERT_NE(CallFoo, nullptr);
+ const auto &NotEqOp =
+ matchNode<BinaryOperator>(binaryOperator(hasOperatorName("!=")));
+ const auto &CallFoo =
+ matchNode<CallExpr>(callExpr(callee(functionDecl(hasName("foo")))));
// In the block that evaluates the expression `p != nullptr`, this expression
// is associated with a value.
const auto &NotEqOpState = blockStateForStmt(BlockStates, NotEqOp);
- EXPECT_NE(NotEqOpState.Env.getValue(*NotEqOp), nullptr);
+ EXPECT_NE(NotEqOpState.Env.getValue(NotEqOp), nullptr);
// In the block that calls `foo(p)`, the value for `p != nullptr` is discarded
// because it is not consumed by this block.
const auto &CallFooState = blockStateForStmt(BlockStates, CallFoo);
- EXPECT_EQ(CallFooState.Env.getValue(*NotEqOp), nullptr);
+ EXPECT_EQ(CallFooState.Env.getValue(NotEqOp), nullptr);
}
TEST_F(DiscardExprStateTest, BooleanOperator) {
@@ -174,29 +258,24 @@ TEST_F(DiscardExprStateTest, BooleanOperator) {
auto BlockStates = llvm::cantFail(runAnalysis<NoopAnalysis>(
Code, [](ASTContext &C) { return NoopAnalysis(C); }));
- auto *AndOp = selectFirst<BinaryOperator>(
- "op", match(binaryOperator(hasOperatorName("&&")).bind("op"),
- AST->getASTContext()));
- ASSERT_NE(AndOp, nullptr);
-
- auto *Return = selectFirst<ReturnStmt>(
- "return", match(returnStmt().bind("return"), AST->getASTContext()));
- ASSERT_NE(Return, nullptr);
+ const auto &AndOp =
+ matchNode<BinaryOperator>(binaryOperator(hasOperatorName("&&")));
+ const auto &Return = matchNode<ReturnStmt>(returnStmt());
// In the block that evaluates the LHS of the `&&` operator, the LHS is
// associated with a value, while the right-hand side is not (unsurprisingly,
// as it hasn't been evaluated yet).
- const auto &LHSState = blockStateForStmt(BlockStates, AndOp->getLHS());
- auto *LHSValue = cast<BoolValue>(LHSState.Env.getValue(*AndOp->getLHS()));
+ const auto &LHSState = blockStateForStmt(BlockStates, *AndOp.getLHS());
+ auto *LHSValue = cast<BoolValue>(LHSState.Env.getValue(*AndOp.getLHS()));
ASSERT_NE(LHSValue, nullptr);
- EXPECT_EQ(LHSState.Env.getValue(*AndOp->getRHS()), nullptr);
+ EXPECT_EQ(LHSState.Env.getValue(*AndOp.getRHS()), nullptr);
// In the block that evaluates the RHS, the RHS is associated with a
// value. The value for the LHS has been discarded as it is not consumed by
// this block.
- const auto &RHSState = blockStateForStmt(BlockStates, AndOp->getRHS());
- EXPECT_EQ(RHSState.Env.getValue(*AndOp->getLHS()), nullptr);
- auto *RHSValue = cast<BoolValue>(RHSState.Env.getValue(*AndOp->getRHS()));
+ const auto &RHSState = blockStateForStmt(BlockStates, *AndOp.getRHS());
+ EXPECT_EQ(RHSState.Env.getValue(*AndOp.getLHS()), nullptr);
+ auto *RHSValue = cast<BoolValue>(RHSState.Env.getValue(*AndOp.getRHS()));
ASSERT_NE(RHSValue, nullptr);
// In the block that evaluates the return statement, the expression `b1 && b2`
@@ -217,9 +296,9 @@ TEST_F(DiscardExprStateTest, BooleanOperator) {
// operands, rather than from the environment for the block that contains the
// `&&`.
const auto &ReturnState = blockStateForStmt(BlockStates, Return);
- EXPECT_EQ(ReturnState.Env.getValue(*AndOp->getLHS()), nullptr);
- EXPECT_EQ(ReturnState.Env.getValue(*AndOp->getRHS()), nullptr);
- EXPECT_EQ(ReturnState.Env.getValue(*AndOp),
+ EXPECT_EQ(ReturnState.Env.getValue(*AndOp.getLHS()), nullptr);
+ EXPECT_EQ(ReturnState.Env.getValue(*AndOp.getRHS()), nullptr);
+ EXPECT_EQ(ReturnState.Env.getValue(AndOp),
&ReturnState.Env.makeAnd(*LHSValue, *RHSValue));
}
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 8282e75..b8dc01f 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -13503,6 +13503,12 @@ TEST_F(FormatTest, IncorrectCodeUnbalancedBraces) {
verifyFormat("{");
verifyFormat("#})");
verifyNoCrash("(/**/[:!] ?[).");
+ verifyNoCrash("struct X {\n"
+ " operator iunt(\n"
+ "};");
+ verifyNoCrash("struct Foo {\n"
+ " operator foo(bar\n"
+ "};");
}
TEST_F(FormatTest, IncorrectUnbalancedBracesInMacrosWithUnicode) {
diff --git a/clang/unittests/Format/FormatTestMacroExpansion.cpp b/clang/unittests/Format/FormatTestMacroExpansion.cpp
index 653ec2a..85ab6ea 100644
--- a/clang/unittests/Format/FormatTestMacroExpansion.cpp
+++ b/clang/unittests/Format/FormatTestMacroExpansion.cpp
@@ -48,7 +48,7 @@ TEST_F(FormatTestMacroExpansion, UnexpandConfiguredMacros) {
)",
Style);
verifyIncompleteFormat("ID3({, ID(a *b),\n"
- " ;\n"
+ " ;\n"
" });",
Style);
@@ -131,9 +131,9 @@ ID(CALL(CALL(a * b)));
EXPECT_EQ(R"(
ID3(
{
- CLASS
- a *b;
- };
+ CLASS
+ a *b;
+ };
},
ID(x *y);
,
@@ -287,6 +287,19 @@ TEST_F(FormatTestMacroExpansion,
Style);
}
+TEST_F(FormatTestMacroExpansion, IndentChildrenWithinMacroCall) {
+ FormatStyle Style = getGoogleStyleWithColumns(22);
+ Style.Macros.push_back("MACRO(a, b)=a=(b)");
+ verifyFormat("void f() {\n"
+ " MACRO(a b, call([] {\n"
+ " if (expr) {\n"
+ " indent();\n"
+ " }\n"
+ " }));\n"
+ "}",
+ Style);
+}
+
} // namespace
} // namespace test
} // namespace format
diff --git a/clang/unittests/Format/MacroCallReconstructorTest.cpp b/clang/unittests/Format/MacroCallReconstructorTest.cpp
index 6e69005..9df21ea 100644
--- a/clang/unittests/Format/MacroCallReconstructorTest.cpp
+++ b/clang/unittests/Format/MacroCallReconstructorTest.cpp
@@ -151,17 +151,21 @@ public:
Lex.Allocator, Lex.IdentTable);
}
- UnwrappedLine line(llvm::ArrayRef<FormatToken *> Tokens) {
+ UnwrappedLine line(llvm::ArrayRef<FormatToken *> Tokens, unsigned Level = 0) {
UnwrappedLine Result;
+ Result.Level = Level;
for (FormatToken *Tok : Tokens)
Result.Tokens.push_back(UnwrappedLineNode(Tok));
return Result;
}
- UnwrappedLine line(llvm::StringRef Text) { return line({lex(Text)}); }
+ UnwrappedLine line(llvm::StringRef Text, unsigned Level = 0) {
+ return line({lex(Text)}, Level);
+ }
- UnwrappedLine line(llvm::ArrayRef<Chunk> Chunks) {
+ UnwrappedLine line(llvm::ArrayRef<Chunk> Chunks, unsigned Level = 0) {
UnwrappedLine Result;
+ Result.Level = Level;
for (const Chunk &Chunk : Chunks) {
Result.Tokens.insert(Result.Tokens.end(), Chunk.Tokens.begin(),
Chunk.Tokens.end());
@@ -186,6 +190,8 @@ public:
};
bool matchesTokens(const UnwrappedLine &L1, const UnwrappedLine &L2) {
+ if (L1.Level != L2.Level)
+ return false;
if (L1.Tokens.size() != L2.Tokens.size())
return false;
for (auto L1It = L1.Tokens.begin(), L2It = L2.Tokens.begin();
@@ -288,7 +294,8 @@ TEST_F(MacroCallReconstructorTest, StatementSequence) {
matchesLine(line(
{U1.consume("SEMI"),
children({line({U2.consume("SEMI"),
- children({line(U3.consume("SEMI"))})})})})));
+ children({line(U3.consume("SEMI"), 2)})},
+ 1)})})));
}
TEST_F(MacroCallReconstructorTest, NestedBlock) {
@@ -337,9 +344,9 @@ TEST_F(MacroCallReconstructorTest, NestedBlock) {
auto Expected = line({Chunk2Start,
children({
- line(Chunk2LBrace),
- line({Chunk1, Chunk2Mid}),
- line(Chunk2RBrace),
+ line(Chunk2LBrace, 1),
+ line({Chunk1, Chunk2Mid}, 1),
+ line(Chunk2RBrace, 1),
}),
Chunk2End});
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
@@ -379,9 +386,11 @@ TEST_F(MacroCallReconstructorTest, NestedChildBlocks) {
Unexp.addLine(
line({E.consume("f([] {"),
children({line({E.consume("f([] {"),
- children({line(E.consume("return a * b;"))}),
- E.consume("})")})}),
- E.consume("})")}));
+ children({line(E.consume("return a * b;"), 3)}),
+ E.consume("})")},
+ 2)}),
+ E.consume("})")},
+ 1));
Unexp.addLine(line(E.consume("}")));
EXPECT_TRUE(Unexp.finished());
@@ -407,13 +416,15 @@ TEST_F(MacroCallReconstructorTest, NestedChildBlocks) {
auto Expected = line({
Chunk3Start,
children({
- line(Chunk3LBrace),
- line({
- Chunk2Start,
- Chunk1,
- Chunk2End,
- }),
- line(Chunk3RBrace),
+ line(Chunk3LBrace, 1),
+ line(
+ {
+ Chunk2Start,
+ Chunk1,
+ Chunk2End,
+ },
+ 2),
+ line(Chunk3RBrace, 1),
}),
Chunk3End,
});
@@ -469,8 +480,8 @@ TEST_F(MacroCallReconstructorTest, MultipleToplevelUnwrappedLines) {
auto Expected = line({
U.consume("ID("),
children({
- line(U.consume("x;")),
- line(U.consume("x")),
+ line(U.consume("x;"), 1),
+ line(U.consume("x"), 1),
}),
U.consume(", y)"),
});
@@ -524,9 +535,9 @@ TEST_F(MacroCallReconstructorTest, NestedCallsMultipleLines) {
auto Expected = line({
Chunk2Start,
children({
- line({Chunk2LBrace}),
- line({Chunk1, Chunk2Semi}),
- line({Chunk2RBrace}),
+ line({Chunk2LBrace}, 1),
+ line({Chunk1, Chunk2Semi}, 1),
+ line({Chunk2RBrace}, 1),
}),
Chunk2End,
});
@@ -556,15 +567,17 @@ TEST_F(MacroCallReconstructorTest, ParentOutsideMacroCall) {
auto Expected = line({
Prefix,
children({
- line({
- U.consume("ID("),
- children({
- line(U.consume("x;")),
- line(U.consume("y;")),
- line(U.consume("z;")),
- }),
- U.consume(")"),
- }),
+ line(
+ {
+ U.consume("ID("),
+ children({
+ line(U.consume("x;"), 2),
+ line(U.consume("y;"), 2),
+ line(U.consume("z;"), 2),
+ }),
+ U.consume(")"),
+ },
+ 1),
}),
Postfix,
});
@@ -590,7 +603,7 @@ TEST_F(MacroCallReconstructorTest, ChildrenSplitAcrossArguments) {
Matcher U(Call, Lex);
auto Expected = line({
U.consume("CALL({"),
- children(line(U.consume("a;"))),
+ children(line(U.consume("a;"), 1)),
U.consume(", b; })"),
});
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
@@ -620,16 +633,20 @@ TEST_F(MacroCallReconstructorTest, ChildrenAfterMacroCall) {
Matcher U(Call, Lex);
auto Expected = line({
U.consume("CALL({"),
- children(line(U.consume("a"))),
+ children(line(U.consume("a"), 1)),
U.consume(", b)"),
Semi,
- children(line({
- SecondLine,
- children(line({
- ThirdLine,
- Postfix,
- })),
- })),
+ children(line(
+ {
+ SecondLine,
+ children(line(
+ {
+ ThirdLine,
+ Postfix,
+ },
+ 2)),
+ },
+ 1)),
});
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
}
@@ -655,7 +672,37 @@ TEST_F(MacroCallReconstructorTest, InvalidCodeSplittingBracesAcrossArgs) {
Matcher U(Call, Lex);
auto Expected = line({
Prefix,
- children({line(U.consume("M({,x,)"))}),
+ children({line(U.consume("M({,x,)"), 1)}),
+ });
+ EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
+}
+
+TEST_F(MacroCallReconstructorTest, IndentLevelInExpandedCode) {
+ auto Macros = createExpander({"ID(a)=a"});
+ Expansion Exp(Lex, *Macros);
+ TokenList Call = Exp.expand("ID", {std::string("[] { { x; } }")});
+
+ MacroCallReconstructor Unexp(0, Exp.getUnexpanded());
+ Matcher E(Exp.getTokens(), Lex);
+ Unexp.addLine(line({
+ E.consume("[] {"),
+ children({
+ line(E.consume("{"), 1),
+ line(E.consume("x;"), 2),
+ line(E.consume("}"), 1),
+ }),
+ E.consume("}"),
+ }));
+ EXPECT_TRUE(Unexp.finished());
+ Matcher U(Call, Lex);
+ auto Expected = line({
+ U.consume("ID([] {"),
+ children({
+ line(U.consume("{"), 1),
+ line(U.consume("x;"), 2),
+ line(U.consume("}"), 1),
+ }),
+ U.consume("})"),
});
EXPECT_THAT(std::move(Unexp).takeResult(), matchesLine(Expected));
}