[clang-diff] Improve and test getNodeValue
Summary: Use qualified names if available. Reviewers: arphaman Subscribers: klimek Differential Revision: https://reviews.llvm.org/D36186 llvm-svn: 311292
This commit is contained in:
parent
760e00b0bc
commit
0dd86dc5f1
|
@ -45,6 +45,8 @@ struct Node {
|
|||
ast_type_traits::ASTNodeKind getType() const;
|
||||
StringRef getTypeLabel() const;
|
||||
bool isLeaf() const { return Children.empty(); }
|
||||
llvm::Optional<StringRef> getIdentifier() const;
|
||||
llvm::Optional<std::string> getQualifiedIdentifier() const;
|
||||
};
|
||||
|
||||
class ASTDiff {
|
||||
|
|
|
@ -149,6 +149,8 @@ public:
|
|||
|
||||
std::string getNodeValue(NodeId Id) const;
|
||||
std::string getNodeValue(const Node &Node) const;
|
||||
std::string getDeclValue(const Decl *D) const;
|
||||
std::string getStmtValue(const Stmt *S) const;
|
||||
|
||||
private:
|
||||
/// Nodes in preorder.
|
||||
|
@ -268,9 +270,6 @@ struct PreorderVisitor : public RecursiveASTVisitor<PreorderVisitor> {
|
|||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
SyntaxTree::Impl::Impl(SyntaxTree *Parent, const ASTContext &AST)
|
||||
: Impl(Parent, AST.getTranslationUnitDecl(), AST) {}
|
||||
|
||||
SyntaxTree::Impl::Impl(SyntaxTree *Parent, Decl *N, const ASTContext &AST)
|
||||
: Parent(Parent), AST(AST) {
|
||||
NodeCountVisitor NodeCounter(*this);
|
||||
|
@ -372,49 +371,82 @@ std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const {
|
|||
|
||||
std::string SyntaxTree::Impl::getNodeValue(const Node &N) const {
|
||||
const DynTypedNode &DTN = N.ASTNode;
|
||||
if (auto *X = DTN.get<BinaryOperator>())
|
||||
return X->getOpcodeStr();
|
||||
if (auto *X = DTN.get<AccessSpecDecl>()) {
|
||||
CharSourceRange Range(X->getSourceRange(), false);
|
||||
if (auto *S = DTN.get<Stmt>())
|
||||
return getStmtValue(S);
|
||||
if (auto *D = DTN.get<Decl>())
|
||||
return getDeclValue(D);
|
||||
llvm_unreachable("Fatal: unhandled AST node.\n");
|
||||
}
|
||||
|
||||
std::string SyntaxTree::Impl::getDeclValue(const Decl *D) const {
|
||||
std::string Value;
|
||||
PrintingPolicy TypePP(AST.getLangOpts());
|
||||
TypePP.AnonymousTagLocations = false;
|
||||
|
||||
if (auto *V = dyn_cast<ValueDecl>(D)) {
|
||||
Value += V->getQualifiedNameAsString() + "(" +
|
||||
V->getType().getAsString(TypePP) + ")";
|
||||
if (auto *C = dyn_cast<CXXConstructorDecl>(D)) {
|
||||
for (auto *Init : C->inits()) {
|
||||
if (!Init->isWritten())
|
||||
continue;
|
||||
if (Init->isBaseInitializer()) {
|
||||
Value += Init->getBaseClass()->getCanonicalTypeInternal().getAsString(
|
||||
TypePP);
|
||||
} else if (Init->isDelegatingInitializer()) {
|
||||
Value += C->getNameAsString();
|
||||
} else {
|
||||
assert(Init->isAnyMemberInitializer());
|
||||
Value += Init->getMember()->getQualifiedNameAsString();
|
||||
}
|
||||
Value += ",";
|
||||
}
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
if (auto *N = dyn_cast<NamedDecl>(D))
|
||||
Value += N->getQualifiedNameAsString() + ";";
|
||||
if (auto *T = dyn_cast<TypedefNameDecl>(D))
|
||||
return Value + T->getUnderlyingType().getAsString(TypePP) + ";";
|
||||
if (auto *T = dyn_cast<TypeDecl>(D))
|
||||
if (T->getTypeForDecl())
|
||||
Value +=
|
||||
T->getTypeForDecl()->getCanonicalTypeInternal().getAsString(TypePP) +
|
||||
";";
|
||||
if (auto *U = dyn_cast<UsingDirectiveDecl>(D))
|
||||
return U->getNominatedNamespace()->getName();
|
||||
if (auto *A = dyn_cast<AccessSpecDecl>(D)) {
|
||||
CharSourceRange Range(A->getSourceRange(), false);
|
||||
return Lexer::getSourceText(Range, AST.getSourceManager(),
|
||||
AST.getLangOpts());
|
||||
}
|
||||
if (auto *X = DTN.get<IntegerLiteral>()) {
|
||||
return Value;
|
||||
}
|
||||
|
||||
std::string SyntaxTree::Impl::getStmtValue(const Stmt *S) const {
|
||||
if (auto *U = dyn_cast<UnaryOperator>(S))
|
||||
return UnaryOperator::getOpcodeStr(U->getOpcode());
|
||||
if (auto *B = dyn_cast<BinaryOperator>(S))
|
||||
return B->getOpcodeStr();
|
||||
if (auto *M = dyn_cast<MemberExpr>(S))
|
||||
return M->getMemberDecl()->getQualifiedNameAsString();
|
||||
if (auto *I = dyn_cast<IntegerLiteral>(S)) {
|
||||
SmallString<256> Str;
|
||||
X->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false);
|
||||
I->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false);
|
||||
return Str.str();
|
||||
}
|
||||
if (auto *X = DTN.get<StringLiteral>())
|
||||
return X->getString();
|
||||
if (auto *X = DTN.get<ValueDecl>())
|
||||
return X->getNameAsString() + "(" + X->getType().getAsString() + ")";
|
||||
if (DTN.get<DeclStmt>() || DTN.get<TranslationUnitDecl>())
|
||||
return "";
|
||||
std::string Value;
|
||||
if (auto *X = DTN.get<DeclRefExpr>()) {
|
||||
if (X->hasQualifier()) {
|
||||
llvm::raw_string_ostream OS(Value);
|
||||
PrintingPolicy PP(AST.getLangOpts());
|
||||
X->getQualifier()->print(OS, PP);
|
||||
}
|
||||
Value += X->getDecl()->getNameAsString();
|
||||
return Value;
|
||||
if (auto *F = dyn_cast<FloatingLiteral>(S)) {
|
||||
SmallString<256> Str;
|
||||
F->getValue().toString(Str);
|
||||
return Str.str();
|
||||
}
|
||||
if (auto *X = DTN.get<NamedDecl>())
|
||||
Value += X->getNameAsString() + ";";
|
||||
if (auto *X = DTN.get<TypedefNameDecl>())
|
||||
return Value + X->getUnderlyingType().getAsString() + ";";
|
||||
if (DTN.get<NamespaceDecl>())
|
||||
return Value;
|
||||
if (auto *X = DTN.get<TypeDecl>())
|
||||
if (X->getTypeForDecl())
|
||||
Value +=
|
||||
X->getTypeForDecl()->getCanonicalTypeInternal().getAsString() + ";";
|
||||
if (DTN.get<Decl>())
|
||||
return Value;
|
||||
if (DTN.get<Stmt>())
|
||||
return "";
|
||||
llvm_unreachable("Fatal: unhandled AST node.\n");
|
||||
if (auto *D = dyn_cast<DeclRefExpr>(S))
|
||||
return D->getDecl()->getQualifiedNameAsString();
|
||||
if (auto *String = dyn_cast<StringLiteral>(S))
|
||||
return String->getString();
|
||||
if (auto *B = dyn_cast<CXXBoolLiteralExpr>(S))
|
||||
return B->getValue() ? "true" : "false";
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Identifies a node in a subtree by its postorder offset, starting at 1.
|
||||
|
@ -637,6 +669,22 @@ ast_type_traits::ASTNodeKind Node::getType() const {
|
|||
|
||||
StringRef Node::getTypeLabel() const { return getType().asStringRef(); }
|
||||
|
||||
llvm::Optional<std::string> Node::getQualifiedIdentifier() const {
|
||||
if (auto *ND = ASTNode.get<NamedDecl>()) {
|
||||
if (ND->getDeclName().isIdentifier())
|
||||
return ND->getQualifiedNameAsString();
|
||||
}
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
llvm::Optional<StringRef> Node::getIdentifier() const {
|
||||
if (auto *ND = ASTNode.get<NamedDecl>()) {
|
||||
if (ND->getDeclName().isIdentifier())
|
||||
return ND->getName();
|
||||
}
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Compares nodes by their depth.
|
||||
struct HeightLess {
|
||||
|
|
|
@ -5,22 +5,31 @@
|
|||
// CHECK: {{^}} NamespaceDecl: test;(
|
||||
namespace test {
|
||||
|
||||
// CHECK: {{^}} FunctionDecl: f(
|
||||
// CHECK: {{^}} FunctionDecl: test::f(
|
||||
// CHECK: CompoundStmt(
|
||||
void f() {
|
||||
// CHECK: VarDecl: i(int)(
|
||||
// CHECK: IntegerLiteral: 1
|
||||
auto i = 1;
|
||||
// CHECK: FloatingLiteral: 1.5(
|
||||
auto r = 1.5;
|
||||
// CHECK: CXXBoolLiteralExpr: true(
|
||||
auto b = true;
|
||||
// CHECK: CallExpr(
|
||||
// CHECK-NOT: ImplicitCastExpr
|
||||
// CHECK-NEXT: DeclRefExpr: f(
|
||||
// CHECK: DeclRefExpr: test::f(
|
||||
f();
|
||||
// CHECK: UnaryOperator: ++(
|
||||
++i;
|
||||
// CHECK: BinaryOperator: =(
|
||||
i = i;
|
||||
}
|
||||
|
||||
} // end namespace test
|
||||
|
||||
// CHECK: UsingDirectiveDecl: test(
|
||||
using namespace test;
|
||||
|
||||
// CHECK: TypedefDecl: nat;unsigned int;(
|
||||
typedef unsigned nat;
|
||||
// CHECK: TypeAliasDecl: real;double;(
|
||||
|
@ -29,10 +38,10 @@ using real = double;
|
|||
class Base {
|
||||
};
|
||||
|
||||
// CHECK: CXXRecordDecl: X;class X;(
|
||||
// CHECK: CXXRecordDecl: X;X;(
|
||||
class X : Base {
|
||||
int m;
|
||||
// CHECK: CXXMethodDecl: foo(const char *(int)
|
||||
// CHECK: CXXMethodDecl: X::foo(const char *(int)
|
||||
// CHECK: ParmVarDecl: i(int)(
|
||||
const char *foo(int i) {
|
||||
if (i == 0)
|
||||
|
@ -44,11 +53,16 @@ class X : Base {
|
|||
|
||||
// CHECK: AccessSpecDecl: public(
|
||||
public:
|
||||
// CHECK: CXXConstructorDecl: X(void (char, int){{( __attribute__\(\(thiscall\)\))?}})(
|
||||
X(char, int) : Base(), m(0) {
|
||||
// CHECK: MemberExpr(
|
||||
int not_initialized;
|
||||
// All initialized members, bases and delegating initializers are are
|
||||
// appended, separated by commas.
|
||||
// CHECK: CXXConstructorDecl: X::X(void (char, int){{( __attribute__\(\(thiscall\)\))?}})Base,X::m,(
|
||||
X(char c, int) : Base(), m(0) {
|
||||
// CHECK: MemberExpr: X::m(
|
||||
int x = m;
|
||||
}
|
||||
// CHECK: CXXConstructorDecl: X::X(void (char))X,(
|
||||
X(char s) : X(s, 4) {}
|
||||
};
|
||||
|
||||
#define M (void)1
|
||||
|
|
|
@ -11,18 +11,18 @@ void foo() {
|
|||
}
|
||||
}
|
||||
|
||||
// CHECK: Match DeclRefExpr: foo{{.*}} to DeclRefExpr: inner::foo
|
||||
// CHECK: Match DeclRefExpr: src::foo{{.*}} to DeclRefExpr: dst::inner::foo
|
||||
void main() { inner::foo(); }
|
||||
|
||||
// CHECK: Match StringLiteral: foo{{.*}} to StringLiteral: foo
|
||||
const char *b = "f" "o" "o";
|
||||
|
||||
// unsigned is canonicalized to unsigned int
|
||||
// CHECK: Match TypedefDecl: nat;unsigned int;{{.*}} to TypedefDecl: nat;unsigned int;
|
||||
// CHECK: Match TypedefDecl: src::nat;unsigned int;{{.*}} to TypedefDecl: dst::nat;unsigned int;
|
||||
typedef unsigned nat;
|
||||
|
||||
// CHECK: Match VarDecl: p(int){{.*}} to VarDecl: prod(double)
|
||||
// CHECK: Update VarDecl: p(int){{.*}} to prod(double)
|
||||
// CHECK: Match VarDecl: src::p(int){{.*}} to VarDecl: dst::prod(double)
|
||||
// CHECK: Update VarDecl: src::p(int){{.*}} to dst::prod(double)
|
||||
// CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: *
|
||||
double prod = 1 * 2 * 10;
|
||||
// CHECK: Update DeclRefExpr
|
||||
|
@ -49,7 +49,7 @@ int um = 1 + 7;
|
|||
|
||||
namespace {
|
||||
// match with parents of different type
|
||||
// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1
|
||||
// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: (anonymous namespace)::f1
|
||||
void f1() {{ (void) __func__;;; }}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ void f2() { ;; {{;}} }
|
|||
void f1() {
|
||||
// CompoundStmt: 3 matched descendants, subtree sizes 4 and 5
|
||||
// Jaccard similarity = 3 / (4 + 5 - 3) = 3 / 6 >= 0.5
|
||||
// CHECK: Match FunctionDecl: f1(void (void))(1) to FunctionDecl: f1(void (void))(1)
|
||||
// CHECK: Match FunctionDecl: f1(void ())(1) to FunctionDecl: f1(void ())(1)
|
||||
// CHECK: Match CompoundStmt(2) to CompoundStmt(2)
|
||||
// CHECK: Match CompoundStmt(4) to CompoundStmt(3)
|
||||
// CHECK: Match CompoundStmt(5) to CompoundStmt(4)
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
// match, move
|
||||
// CHECK: <span id='L[[L:[0-9]+]]' tid='R[[R:[0-9]+]]' title='FunctionDecl
|
||||
// CHECK-NEXT: [[L]] -> [[R]]
|
||||
// CHECK-NEXT: foo(void (void))' class='m'>void foo()
|
||||
// CHECK-NEXT: src::foo(void ())' class='u m'>void foo()
|
||||
|
||||
// match
|
||||
// CHECK: <span id='L[[L:[0-9]+]]' tid='R[[R:[0-9]+]]' title='FunctionDecl
|
||||
// CHECK-NEXT: [[L]] -> [[R]]
|
||||
// CHECK-NEXT: main(void (void))'>void main()
|
||||
// CHECK-NEXT: src::main(void ())' class='u'>void main()
|
||||
|
||||
// deletion
|
||||
// CHECK: <span id='L[[L:[0-9]+]]' tid='R-1' title='IntegerLiteral
|
||||
|
|
|
@ -41,4 +41,5 @@ void f3() {
|
|||
// CHECK: Delete NullStmt(22)
|
||||
;; {{;;;;;;}}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: %clang_cc1 -E %s > %t.src.cpp
|
||||
// RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST
|
||||
// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s
|
||||
// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- -std=c++11 | FileCheck %s
|
||||
//
|
||||
// Test the top-down matching of identical subtrees only.
|
||||
|
||||
|
|
|
@ -316,6 +316,18 @@ static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
|
|||
const diff::Node &N = Tree.getNode(Id);
|
||||
OS << "{";
|
||||
printNodeAttributes(OS, Tree, Id);
|
||||
auto Identifier = N.getIdentifier();
|
||||
auto QualifiedIdentifier = N.getQualifiedIdentifier();
|
||||
if (Identifier) {
|
||||
OS << R"(,"identifier":")";
|
||||
printJsonString(OS, *Identifier);
|
||||
OS << R"(")";
|
||||
if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
|
||||
OS << R"(,"qualified_identifier":")";
|
||||
printJsonString(OS, *QualifiedIdentifier);
|
||||
OS << R"(")";
|
||||
}
|
||||
}
|
||||
OS << R"(,"children":[)";
|
||||
if (N.Children.size() > 0) {
|
||||
printNodeAsJson(OS, Tree, N.Children[0]);
|
||||
|
|
Loading…
Reference in New Issue