diff options
author | 2020-08-03 14:31:31 +0000 | |
---|---|---|
committer | 2020-08-03 14:31:31 +0000 | |
commit | e5dd70708596ae51455a0ffa086a00c5b29f8583 (patch) | |
tree | 5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp')
-rw-r--r-- | gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp b/gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp new file mode 100644 index 00000000000..9b5e7bf3ba8 --- /dev/null +++ b/gnu/llvm/clang/unittests/Tooling/SourceCodeBuildersTest.cpp @@ -0,0 +1,230 @@ +//===- unittest/Tooling/SourceCodeBuildersTest.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/Tooling/Transformer/SourceCodeBuilders.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Testing/Support/SupportHelpers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using MatchResult = MatchFinder::MatchResult; +using llvm::ValueIs; + +// Create a valid translation unit from a statement. +static std::string wrapSnippet(StringRef StatementCode) { + return ("struct S { S(); S(int); int field; };\n" + "S operator+(const S &a, const S &b);\n" + "auto test_snippet = []{" + + StatementCode + "};") + .str(); +} + +static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { + return varDecl(hasName("test_snippet"), + hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); +} + +struct TestMatch { + // The AST unit from which `result` is built. We bundle it because it backs + // the result. Users are not expected to access it. + std::unique_ptr<ASTUnit> AstUnit; + // The result to use in the test. References `ast_unit`. + MatchResult Result; +}; + +// Matches `Matcher` against the statement `StatementCode` and returns the +// result. Handles putting the statement inside a function and modifying the +// matcher correspondingly. `Matcher` should match one of the statements in +// `StatementCode` exactly -- that is, produce exactly one match. However, +// `StatementCode` may contain other statements not described by `Matcher`. +static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode, + StatementMatcher Matcher) { + auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); + if (AstUnit == nullptr) { + ADD_FAILURE() << "AST construction failed"; + return llvm::None; + } + ASTContext &Context = AstUnit->getASTContext(); + auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); + // We expect a single, exact match for the statement. + if (Matches.size() != 1) { + ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); + return llvm::None; + } + return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; +} + +static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet, + bool Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); + ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; + EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"))) + << "Snippet: " << Snippet; +} + +// Tests the predicate on the call argument, assuming `Snippet` is a function +// call. +static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet, + bool Expected) { + auto StmtMatch = matchStmt( + Snippet, expr(ignoringImplicit(callExpr(hasArgument( + 0, ignoringElidableConstructorCall(expr().bind("arg"))))))); + ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet; + EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg"))) + << "Snippet: " << Snippet; +} + +TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) { + testPredicate(needParensAfterUnaryOperator, "3 + 5;", true); + testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true); + testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true); + + testPredicate(needParensAfterUnaryOperator, "int x; x;", false); + testPredicate(needParensAfterUnaryOperator, "int(3.0);", false); + testPredicate(needParensAfterUnaryOperator, "void f(); f();", false); + testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false); + testPredicate(needParensAfterUnaryOperator, "S x; x.field;", false); + testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false); + testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false); +} + +TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) { + // The binary operation will be embedded in various implicit + // expressions. Verify they are ignored. + testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);", + true); +} + +TEST(SourceCodeBuildersTest, mayEverNeedParens) { + testPredicate(mayEverNeedParens, "3 + 5;", true); + testPredicate(mayEverNeedParens, "true ? 3 : 5;", true); + testPredicate(mayEverNeedParens, "int x = 1; --x;", true); + testPredicate(mayEverNeedParens, "int x = 1; -x;", true); + + testPredicate(mayEverNeedParens, "int x; x;", false); + testPredicate(mayEverNeedParens, "int(3.0);", false); + testPredicate(mayEverNeedParens, "void f(); f();", false); + testPredicate(mayEverNeedParens, "int a[3]; a[0];", false); + testPredicate(mayEverNeedParens, "S x; x.field;", false); +} + +TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) { + // The binary operation will be embedded in various implicit + // expressions. Verify they are ignored. + testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true); +} + +static void testBuilder( + llvm::Optional<std::string> (*Builder)(const Expr &, const ASTContext &), + StringRef Snippet, StringRef Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind("expr")); + ASSERT_TRUE(StmtMatch); + EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"), + *StmtMatch->Result.Context), + ValueIs(Expected)); +} + +TEST(SourceCodeBuildersTest, BuildParensUnaryOp) { + testBuilder(buildParens, "-4;", "(-4)"); +} + +TEST(SourceCodeBuildersTest, BuildParensBinOp) { + testBuilder(buildParens, "4 + 4;", "(4 + 4)"); +} + +TEST(SourceCodeBuildersTest, BuildParensValue) { + testBuilder(buildParens, "4;", "4"); +} + +TEST(SourceCodeBuildersTest, BuildParensSubscript) { + testBuilder(buildParens, "int a[3]; a[0];", "a[0]"); +} + +TEST(SourceCodeBuildersTest, BuildParensCall) { + testBuilder(buildParens, "int f(int); f(4);", "f(4)"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfValue) { + testBuilder(buildAddressOf, "S x; x;", "&x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) { + testBuilder(buildAddressOf, "S *x; *x;", "x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) { + testBuilder(buildAddressOf, "S *x; *(x);", "x"); +} + +TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) { + testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)"); +} + +TEST(SourceCodeBuildersTest, BuildDereferencePointer) { + testBuilder(buildDereference, "S *x; x;", "*x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) { + testBuilder(buildDereference, "S x; &x;", "x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) { + testBuilder(buildDereference, "S x; &(x);", "x"); +} + +TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) { + testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)"); +} + +TEST(SourceCodeBuildersTest, BuildDotValue) { + testBuilder(buildDot, "S x; x;", "x."); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereference) { + testBuilder(buildDot, "S *x; *x;", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) { + testBuilder(buildDot, "S *x; *(x);", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) { + testBuilder(buildDot, "S x; x + x;", "(x + x)."); +} + +TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) { + testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowPointer) { + testBuilder(buildArrow, "S *x; x;", "x->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddress) { + testBuilder(buildArrow, "S x; &x;", "x."); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) { + testBuilder(buildArrow, "S x; &(x);", "x."); +} + +TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) { + testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->"); +} + +TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) { + testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x)."); +} +} // namespace |