
Adds a def file to have a single location where tested language versions are specified. Removes the need to update multiple locations in the testing infrastructure to add a new language version to be tested. Test instatiation can now include all languages without needing to specify them. This patch also adds pretty printing for instantiated test names. That means, that a test instantiated with C++23 will have the name `...TestSuite/TestName/CXX23` instead ending with some number (index of the argument for instantiation of the test), which provides a better experience when encountering a test failure with a specific language version. The suffix will also contain an `_win` if the target contains `win`. --------- Co-authored-by: Sirraide <aeternalmail@gmail.com>
304 lines
9.6 KiB
C++
304 lines
9.6 KiB
C++
//===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Provides MatchVerifier, a base class to implement gtest matchers that
|
|
// verify things that can be matched on the AST.
|
|
//
|
|
// Also implements matchers based on MatchVerifier:
|
|
// LocationVerifier and RangeVerifier to verify whether a matched node has
|
|
// the expected source location or source range.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
|
|
#define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Testing/CommandLineArgs.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace clang {
|
|
namespace ast_matchers {
|
|
|
|
/// \brief Base class for verifying some property of nodes found by a matcher.
|
|
template <typename NodeType>
|
|
class MatchVerifier : public MatchFinder::MatchCallback {
|
|
public:
|
|
template <typename MatcherType>
|
|
testing::AssertionResult match(const std::string &Code,
|
|
const MatcherType &AMatcher) {
|
|
std::vector<std::string> Args;
|
|
return match(Code, AMatcher, Args, Lang_CXX03);
|
|
}
|
|
|
|
template <typename MatcherType>
|
|
testing::AssertionResult match(const std::string &Code,
|
|
const MatcherType &AMatcher, TestLanguage L) {
|
|
std::vector<std::string> Args;
|
|
return match(Code, AMatcher, Args, L);
|
|
}
|
|
|
|
template <typename MatcherType>
|
|
testing::AssertionResult
|
|
match(const std::string &Code, const MatcherType &AMatcher,
|
|
std::vector<std::string> &Args, TestLanguage L);
|
|
|
|
template <typename MatcherType>
|
|
testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
|
|
|
|
protected:
|
|
void run(const MatchFinder::MatchResult &Result) override;
|
|
virtual void verify(const MatchFinder::MatchResult &Result,
|
|
const NodeType &Node) {}
|
|
|
|
void setFailure(const Twine &Result) {
|
|
Verified = false;
|
|
VerifyResult = Result.str();
|
|
}
|
|
|
|
void setSuccess() {
|
|
Verified = true;
|
|
}
|
|
|
|
private:
|
|
bool Verified;
|
|
std::string VerifyResult;
|
|
};
|
|
|
|
/// \brief Runs a matcher over some code, and returns the result of the
|
|
/// verifier for the matched node.
|
|
template <typename NodeType>
|
|
template <typename MatcherType>
|
|
testing::AssertionResult
|
|
MatchVerifier<NodeType>::match(const std::string &Code,
|
|
const MatcherType &AMatcher,
|
|
std::vector<std::string> &Args, TestLanguage L) {
|
|
MatchFinder Finder;
|
|
Finder.addMatcher(AMatcher.bind(""), this);
|
|
std::unique_ptr<tooling::FrontendActionFactory> Factory(
|
|
tooling::newFrontendActionFactory(&Finder));
|
|
|
|
StringRef FileName;
|
|
switch (L) {
|
|
#define TESTLANGUAGE(lang, version, std_flag, version_index) \
|
|
case Lang_##lang##version: \
|
|
Args.push_back("-std=" #std_flag); \
|
|
FileName = getFilenameForTesting(Lang_##lang##version); \
|
|
break;
|
|
#include "clang/Testing/TestLanguage.def"
|
|
|
|
case Lang_OpenCL:
|
|
Args.push_back("-cl-no-stdinc");
|
|
FileName = "input.cl";
|
|
break;
|
|
case Lang_OBJC:
|
|
Args.push_back("-fobjc-nonfragile-abi");
|
|
FileName = "input.m";
|
|
break;
|
|
case Lang_OBJCXX:
|
|
FileName = "input.mm";
|
|
break;
|
|
}
|
|
|
|
// Default to failure in case callback is never called
|
|
setFailure("Could not find match");
|
|
if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
|
|
return testing::AssertionFailure() << "Parsing error";
|
|
if (!Verified)
|
|
return testing::AssertionFailure() << VerifyResult;
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
/// \brief Runs a matcher over some AST, and returns the result of the
|
|
/// verifier for the matched node.
|
|
template <typename NodeType> template <typename MatcherType>
|
|
testing::AssertionResult MatchVerifier<NodeType>::match(
|
|
const Decl *D, const MatcherType &AMatcher) {
|
|
MatchFinder Finder;
|
|
Finder.addMatcher(AMatcher.bind(""), this);
|
|
|
|
setFailure("Could not find match");
|
|
Finder.match(*D, D->getASTContext());
|
|
|
|
if (!Verified)
|
|
return testing::AssertionFailure() << VerifyResult;
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
template <typename NodeType>
|
|
void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
|
|
const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
|
|
if (!Node) {
|
|
setFailure("Matched node has wrong type");
|
|
} else {
|
|
// Callback has been called, default to success.
|
|
setSuccess();
|
|
verify(Result, *Node);
|
|
}
|
|
}
|
|
|
|
template <>
|
|
inline void
|
|
MatchVerifier<DynTypedNode>::run(const MatchFinder::MatchResult &Result) {
|
|
BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
|
|
BoundNodes::IDToNodeMap::const_iterator I = M.find("");
|
|
if (I == M.end()) {
|
|
setFailure("Node was not bound");
|
|
} else {
|
|
// Callback has been called, default to success.
|
|
setSuccess();
|
|
verify(Result, I->second);
|
|
}
|
|
}
|
|
|
|
/// \brief Verify whether a node has the correct source location.
|
|
///
|
|
/// By default, Node.getSourceLocation() is checked. This can be changed
|
|
/// by overriding getLocation().
|
|
template <typename NodeType>
|
|
class LocationVerifier : public MatchVerifier<NodeType> {
|
|
public:
|
|
void expectLocation(unsigned Line, unsigned Column) {
|
|
ExpectLine = Line;
|
|
ExpectColumn = Column;
|
|
}
|
|
|
|
protected:
|
|
void verify(const MatchFinder::MatchResult &Result,
|
|
const NodeType &Node) override {
|
|
SourceLocation Loc = getLocation(Node);
|
|
unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
|
|
unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
|
|
if (Line != ExpectLine || Column != ExpectColumn) {
|
|
std::string MsgStr;
|
|
llvm::raw_string_ostream Msg(MsgStr);
|
|
Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
|
|
<< ">, found <";
|
|
Loc.print(Msg, *Result.SourceManager);
|
|
Msg << '>';
|
|
this->setFailure(MsgStr);
|
|
}
|
|
}
|
|
|
|
virtual SourceLocation getLocation(const NodeType &Node) {
|
|
return Node.getLocation();
|
|
}
|
|
|
|
private:
|
|
unsigned ExpectLine, ExpectColumn;
|
|
};
|
|
|
|
/// \brief Verify whether a node has the correct source range.
|
|
///
|
|
/// By default, Node.getSourceRange() is checked. This can be changed
|
|
/// by overriding getRange().
|
|
template <typename NodeType>
|
|
class RangeVerifier : public MatchVerifier<NodeType> {
|
|
public:
|
|
void expectRange(unsigned BeginLine, unsigned BeginColumn,
|
|
unsigned EndLine, unsigned EndColumn) {
|
|
ExpectBeginLine = BeginLine;
|
|
ExpectBeginColumn = BeginColumn;
|
|
ExpectEndLine = EndLine;
|
|
ExpectEndColumn = EndColumn;
|
|
}
|
|
|
|
protected:
|
|
void verify(const MatchFinder::MatchResult &Result,
|
|
const NodeType &Node) override {
|
|
SourceRange R = getRange(Node);
|
|
SourceLocation Begin = R.getBegin();
|
|
SourceLocation End = R.getEnd();
|
|
unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
|
|
unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
|
|
unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
|
|
unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
|
|
if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
|
|
EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
|
|
std::string MsgStr;
|
|
llvm::raw_string_ostream Msg(MsgStr);
|
|
Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
|
|
<< '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
|
|
Begin.print(Msg, *Result.SourceManager);
|
|
Msg << '-';
|
|
End.print(Msg, *Result.SourceManager);
|
|
Msg << '>';
|
|
this->setFailure(MsgStr);
|
|
}
|
|
}
|
|
|
|
virtual SourceRange getRange(const NodeType &Node) {
|
|
return Node.getSourceRange();
|
|
}
|
|
|
|
private:
|
|
unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
|
|
};
|
|
|
|
/// \brief Verify whether a node's dump contains a given substring.
|
|
class DumpVerifier : public MatchVerifier<DynTypedNode> {
|
|
public:
|
|
void expectSubstring(const std::string &Str) {
|
|
ExpectSubstring = Str;
|
|
}
|
|
|
|
protected:
|
|
void verify(const MatchFinder::MatchResult &Result,
|
|
const DynTypedNode &Node) override {
|
|
std::string DumpStr;
|
|
llvm::raw_string_ostream Dump(DumpStr);
|
|
Node.dump(Dump, *Result.Context);
|
|
|
|
if (DumpStr.find(ExpectSubstring) == std::string::npos) {
|
|
std::string MsgStr;
|
|
llvm::raw_string_ostream Msg(MsgStr);
|
|
Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
|
|
<< DumpStr << '>';
|
|
this->setFailure(MsgStr);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::string ExpectSubstring;
|
|
};
|
|
|
|
/// \brief Verify whether a node's pretty print matches a given string.
|
|
class PrintVerifier : public MatchVerifier<DynTypedNode> {
|
|
public:
|
|
void expectString(const std::string &Str) {
|
|
ExpectString = Str;
|
|
}
|
|
|
|
protected:
|
|
void verify(const MatchFinder::MatchResult &Result,
|
|
const DynTypedNode &Node) override {
|
|
std::string PrintStr;
|
|
llvm::raw_string_ostream Print(PrintStr);
|
|
Node.print(Print, Result.Context->getPrintingPolicy());
|
|
|
|
if (PrintStr != ExpectString) {
|
|
std::string MsgStr;
|
|
llvm::raw_string_ostream Msg(MsgStr);
|
|
Msg << "Expected pretty print <" << ExpectString << ">, found <"
|
|
<< PrintStr << '>';
|
|
this->setFailure(MsgStr);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::string ExpectString;
|
|
};
|
|
|
|
} // end namespace ast_matchers
|
|
} // end namespace clang
|
|
|
|
#endif
|