Why is the AST different when using ClangTool

Hello,

why do I get different ASTs when when running clang::tooling::ClangTool and clang(13.0.1) itself on the same piece of code? This happens under Linux (Ubuntu 20.04)

My source:

struct Foo {
  int i;
};

Foo createAElidable();

void func() { Foo foo = createF(); }

Foo createAElidable() { return Foo(); }

AST from “clang++ -Xclang -ast-dump source.cpp”:

TranslationUnitDecl #1 0x55b695de8978 <<invalid sloc>> <invalid sloc>
|-TypedefDecl #68 0x55b695de9298 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'#44
| `-BuiltinType #44 0x55b695de8f30 '__int128'
|-TypedefDecl #69 0x55b695de9310 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'#45
| `-BuiltinType #45 0x55b695de8f50 'unsigned __int128'
|-TypedefDecl #78 0x55b695de96d0 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'#71
| `-RecordType #71 0x55b695de9410 '__NSConstantString_tag'
|   `-CXXRecord 0x55b695de9370 '__NSConstantString_tag'
|-TypedefDecl #80 0x55b695de9778 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'#79
| `-PointerType #79 0x55b695de9730 'char *'
|   `-BuiltinType #4 0x55b695de8a30 'char'
|-TypedefDecl #88 0x55b695e2aac0 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'#87
| `-ConstantArrayType #87 0x55b695e2aa60 '__va_list_tag [1]' 1
|   `-RecordType #82 0x55b695de9870 '__va_list_tag'
|     `-CXXRecord 0x55b695de97d8 '__va_list_tag'
|-CXXRecordDecl #89 0x55b695e2ab20 <test_clang_ccode/test27_copy_elision1.cpp:1:1, line:3:1> line:1:8 referenced struct Foo definition
| |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| | |-DefaultConstructor exists trivial
| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
| | |-MoveConstructor exists simple trivial
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial
| |-CXXRecordDecl #91 0x55b695e2ac50 <col:1, col:8> col:8 implicit struct Foo
| |-FieldDecl #92 0x55b695e2ad00 <line:2:3, col:7> col:7 referenced i 'int'#7
| |-CXXDestructorDecl #104 0x55b695e2b128 <line:1:8> col:8 implicit referenced ~Foo 'void () noexcept'#106 inline default trivial
| |-CXXConstructorDecl #107 0x55b695e2b268 <col:8> col:8 implicit referenced Foo 'void () noexcept'#106 inline default trivial
| |-CXXConstructorDecl #110 0x55b695e2b390 <col:8> col:8 implicit constexpr Foo 'void (const Foo &)'#112 inline default trivial noexcept-unevaluated 0x55b695e2b390
| | `-ParmVarDecl #113 0x55b695e2b4c0 <col:8> col:8 'const Foo &'#109
| `-CXXConstructorDecl #115 0x55b695e2b580 <col:8> col:8 implicit used constexpr Foo 'void (Foo &&) noexcept'#121 inline default trivial
|   |-ParmVarDecl #118 0x55b695e2b6b0 <col:8> col:8 used 'Foo &&'#114
|   |-CXXCtorInitializer Field 0x55b695e2ad00 'i' 'int'#7
|   | `-ImplicitCastExpr #125 0x55b695e5a4c0 <col:8> 'int'#7 <LValueToRValue>
|   |   `-MemberExpr #124 0x55b695e5a488 <col:8> 'int'#7 xvalue .i 0x55b695e2ad00
|   |     `-CXXStaticCastExpr #123 0x55b695e5a450 <col:8> 'Foo'#90 xvalue static_cast<struct Foo &&> <NoOp>
|   |       `-DeclRefExpr #122 0x55b695e2b8a8 <col:8> 'Foo'#90 lvalue ParmVar 0x55b695e2b6b0 '' 'Foo &&'#114
|   `-CompoundStmt 0x55b695e5a508 <col:8>
|-FunctionDecl #94 0x55b695e2adc0 <line:5:1, col:21> col:5 used createAElidable 'Foo ()'#93
|-FunctionDecl #97 0x55b695e2af10 <line:7:1, col:44> col:6 func 'void ()'#96
| `-CompoundStmt 0x55b695e5a588 <col:13, col:44>
|   `-DeclStmt 0x55b695e5a570 <col:15, col:42>
|     `-VarDecl #98 0x55b695e2afc8 <col:15, col:41> col:19 foo 'Foo'#90 cinit
|       `-ExprWithCleanups #127 0x55b695e5a550 <col:25, col:41> 'Foo'#90
|         `-CXXConstructExpr #126 0x55b695e5a518 <col:25, col:41> 'Foo'#90 'void (Foo &&) noexcept'#121 elidable
|           `-MaterializeTemporaryExpr #119 0x55b695e2b738 <col:25, col:41> 'Foo'#90 xvalue
|             `-CallExpr #103 0x55b695e2b100 <col:25, col:41> 'Foo'#90
|               `-ImplicitCastExpr #102 0x55b695e2b0e0 <col:25> 'Foo (*)()'#101 <FunctionToPointerDecay>
|                 `-DeclRefExpr #100 0x55b695e2b088 <col:25> 'Foo ()'#93 lvalue Function 0x55b695e2adc0 'createAElidable' 'Foo ()'#93
`-FunctionDecl #128 0x55b695e5a5c0 prev 0x55b695e2adc0 <line:9:1, col:39> col:5 used createAElidable 'Foo ()'#93
  `-CompoundStmt 0x55b695e5a738 <col:23, col:39>
    `-ReturnStmt 0x55b695e5a728 <col:25, col:36>
      `-ExprWithCleanups #135 0x55b695e5a708 <col:32, col:36> 'Foo'#90
        `-CXXConstructExpr #134 0x55b695e5a6d0 <col:32, col:36> 'Foo'#90 'void (Foo &&) noexcept'#121 elidable
          `-MaterializeTemporaryExpr #133 0x55b695e5a6b0 <col:32, col:36> 'Foo'#90 xvalue
            `-CXXTemporaryObjectExpr #132 0x55b695e5a678 <col:32, col:36> 'Foo'#90 'void () noexcept'#106 zeroing

Source of the program using ClangTool:

#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"

using namespace clang;

class TuPrinter : public ast_matchers::MatchFinder::MatchCallback {
public:
  void run(const ast_matchers::MatchFinder::MatchResult& Result) override {
    if (const auto* tu = Result.Nodes.getNodeAs<clang::TranslationUnitDecl>("TU")) {
      tu->dumpColor();
    }
  }
};

static llvm::cl::OptionCategory MyToolCategory("my-tool options");                 // NOLINT
static llvm::cl::extrahelp CommonHelp(tooling::CommonOptionsParser::HelpMessage);  // NOLINT
static llvm::cl::extrahelp MoreHelp("\nMore help text...\n");                      // NOLINT

int main(int argc, const char** argv) {
  auto ExpectedParser = tooling::CommonOptionsParser::create(argc, argv, MyToolCategory);
  if (!ExpectedParser) {
    llvm::errs() << ExpectedParser.takeError();
    return 1;
  }

  tooling::CommonOptionsParser& OptionsParser = ExpectedParser.get();
  tooling::ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());

  TuPrinter tuPrinter;
  ast_matchers::MatchFinder tuFinder;
  tuFinder.addMatcher(ast_matchers::translationUnitDecl().bind("TU"), &tuPrinter);

  return Tool.run(tooling::newFrontendActionFactory(&tuFinder).get());
}

AST from said program:

TranslationUnitDecl #1 0x55e5fc489ce8 <<invalid sloc>> <invalid sloc>
|-TypedefDecl #68 0x55e5fc48a608 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'#44
| `-BuiltinType #44 0x55e5fc48a2a0 '__int128'
|-TypedefDecl #69 0x55e5fc48a680 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'#45
| `-BuiltinType #45 0x55e5fc48a2c0 'unsigned __int128'
|-TypedefDecl #78 0x55e5fc48aa40 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'#71
| `-RecordType #71 0x55e5fc48a780 '__NSConstantString_tag'
|   `-CXXRecord 0x55e5fc48a6e0 '__NSConstantString_tag'
|-TypedefDecl #80 0x55e5fc48aae8 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'#79
| `-PointerType #79 0x55e5fc48aaa0 'char *'
|   `-BuiltinType #4 0x55e5fc489da0 'char'
|-TypedefDecl #88 0x55e5fc4cbab0 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag [1]'#87
| `-ConstantArrayType #87 0x55e5fc4cba50 '__va_list_tag [1]' 1
|   `-RecordType #82 0x55e5fc48abe0 '__va_list_tag'
|     `-CXXRecord 0x55e5fc48ab48 '__va_list_tag'
|-CXXRecordDecl #89 0x55e5fc4cbb10 </home/tyrdal/projects/emx-llvm/main/test_clang_ccode/test27_copy_elision1.cpp:1:1, line:3:1> line:1:8 referenced struct Foo definition
| |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| | |-DefaultConstructor exists trivial
| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param
| | |-MoveConstructor exists simple trivial
| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment exists simple trivial needs_implicit
| | `-Destructor simple irrelevant trivial
| |-CXXRecordDecl #91 0x55e5fc4cbc40 <col:1, col:8> col:8 implicit struct Foo
| |-FieldDecl #92 0x55e5fc4cbcf0 <line:2:3, col:7> col:7 i 'int'#7
| |-CXXDestructorDecl #104 0x55e5fc4cc118 <line:1:8> col:8 implicit referenced ~Foo 'void () noexcept'#106 inline default trivial
| |-CXXConstructorDecl #109 0x55e5fc4cc360 <col:8> col:8 implicit referenced Foo 'void () noexcept'#106 inline default trivial
| |-CXXConstructorDecl #114 0x55e5fc4cc480 <col:8> col:8 implicit constexpr Foo 'void (const Foo &)'#116 inline default trivial noexcept-unevaluated 0x55e5fc4cc480
| | `-ParmVarDecl #117 0x55e5fc4cc5b0 <col:8> col:8 'const Foo &'#113
| `-CXXConstructorDecl #119 0x55e5fc4cc670 <col:8> col:8 implicit constexpr Foo 'void (Foo &&)'#121 inline default trivial noexcept-unevaluated 0x55e5fc4cc670
|   `-ParmVarDecl #122 0x55e5fc4cc7a0 <col:8> col:8 'Foo &&'#118
|-FunctionDecl #94 0x55e5fc4cbdb0 <line:5:1, col:21> col:5 used createAElidable 'Foo ()'#93
|-FunctionDecl #97 0x55e5fc4cbf00 <line:7:1, col:44> col:6 func 'void ()'#96
| `-CompoundStmt 0x55e5fc4cc258 <col:13, col:44>
|   `-DeclStmt 0x55e5fc4cc240 <col:15, col:42>
|     `-VarDecl #98 0x55e5fc4cbfb8 <col:15, col:41> col:19 foo 'Foo'#90 cinit
|       `-CallExpr #103 0x55e5fc4cc0f0 <col:25, col:41> 'Foo'#90
|         `-ImplicitCastExpr #102 0x55e5fc4cc0d0 <col:25> 'Foo (*)()'#101 <FunctionToPointerDecay>
|           `-DeclRefExpr #100 0x55e5fc4cc078 <col:25> 'Foo ()'#93 lvalue Function 0x55e5fc4cbdb0 'createAElidable' 'Foo ()'#93
`-FunctionDecl #107 0x55e5fc4cc290 prev 0x55e5fc4cbdb0 <line:9:1, col:39> col:5 used createAElidable 'Foo ()'#93
  `-CompoundStmt 0x55e5fc4fcc30 <col:23, col:39>
    `-ReturnStmt 0x55e5fc4fcc20 <col:25, col:36>
      `-CXXTemporaryObjectExpr #124 0x55e5fc4fcbe8 <col:32, col:36> 'Foo'#90 'void () noexcept'#106 zeroing

As you can see there are clear differences between the two. In the second version there are e.g. no MaterializeTemporaryEprs. I would expect that both emit the same AST. Am I doing something wrong?

This looks like a difference between -std=c++14 and -std=c++17 or later. Not sure why it would be different between clang and tooling though.

Indeed, if I call clang++ with std=c++14 then the output contains those MaterializeTemporaryExprs. Using c++17 spits out the other ast. Still I think clang and tooling should result in the same ast given the same parameters.