diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4875,3 +4875,21 @@ * ``_rotwr`` * ``_lrotl`` * ``_lrotr`` + +Debugging the Compiler +====================== + +Clang supports a number of pragma directives that help debugging the compiler itself. +Syntax is the following: `#pragma clang __debug `. +Note that all of them are a subject to change. + +`dump` +------ +Accepts a single identifier or an expression. When a single identifier is passed, +prints name lookup results. When an expression is passed, prints its AST. +Expression is evaluated as if it appears in the program. +Type- and value-dependent expressions are not supported yet. + +This facility is designed to aid with testing name lookup machinery. +Expression form addresses some of shortcomings of single identifier form, +namely inability to test ADL and overload resolution. diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -659,6 +659,10 @@ "missing argument to debug command '%0'">, InGroup; def warn_pragma_debug_unexpected_argument : Warning< "unexpected argument to debug command">, InGroup; +def warn_pragma_debug_type_dependent_argument : Warning< + "type-dependent expression passed as an argument to debug command">, InGroup; +def warn_pragma_debug_value_dependent_argument : Warning< + "value-dependent expression passed as an argument to debug command">, InGroup; def warn_pragma_debug_unknown_module : Warning< "unknown module '%0'">, InGroup; // #pragma module diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10694,6 +10694,9 @@ /// Called on #pragma clang __debug dump II void ActOnPragmaDump(Scope *S, SourceLocation Loc, IdentifierInfo *II); + /// Called on #pragma clang __debug dump E + void ActOnPragmaDump(Expr *E); + /// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch void ActOnPragmaDetectMismatch(SourceLocation Loc, StringRef Name, StringRef Value); diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1066,21 +1066,11 @@ PP.EnterToken(Crasher, /*IsReinject*/ false); } } else if (II->isStr("dump")) { - Token Identifier; - PP.LexUnexpandedToken(Identifier); - if (auto *DumpII = Identifier.getIdentifierInfo()) { - Token DumpAnnot; - DumpAnnot.startToken(); - DumpAnnot.setKind(tok::annot_pragma_dump); - DumpAnnot.setAnnotationRange( - SourceRange(Tok.getLocation(), Identifier.getLocation())); - DumpAnnot.setAnnotationValue(DumpII); - PP.DiscardUntilEndOfDirective(); - PP.EnterToken(DumpAnnot, /*IsReinject*/false); - } else { - PP.Diag(Identifier, diag::warn_pragma_debug_missing_argument) - << II->getName(); - } + Token DumpAnnot; + DumpAnnot.startToken(); + DumpAnnot.setKind(tok::annot_pragma_dump); + DumpAnnot.setAnnotationRange(SourceRange(Tok.getLocation())); + PP.EnterToken(DumpAnnot, /*IsReinject*/false); } else if (II->isStr("diag_mapping")) { Token DiagName; PP.LexUnexpandedToken(DiagName); diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/Basic/PragmaKinds.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/Token.h" #include "clang/Parse/LoopHint.h" @@ -706,10 +707,39 @@ void Parser::HandlePragmaDump() { assert(Tok.is(tok::annot_pragma_dump)); - IdentifierInfo *II = - reinterpret_cast(Tok.getAnnotationValue()); - Actions.ActOnPragmaDump(getCurScope(), Tok.getLocation(), II); ConsumeAnnotationToken(); + if (Tok.is(tok::eod)) { + PP.Diag(Tok, diag::warn_pragma_debug_missing_argument) << "dump"; + } else if (NextToken().is(tok::eod)) { + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok, diag::warn_pragma_debug_unexpected_argument); + ConsumeAnyToken(); + ExpectAndConsume(tok::eod); + return; + } + IdentifierInfo *II = Tok.getIdentifierInfo(); + Actions.ActOnPragmaDump(getCurScope(), Tok.getLocation(), II); + ConsumeToken(); + } else { + SourceLocation StartLoc = Tok.getLocation(); + ExprResult E = ParseExpression(); + if (!E.isUsable()) { + PP.Diag(StartLoc, diag::warn_pragma_debug_unexpected_argument) + << SourceRange(StartLoc, Tok.getLocation()); + } else if (E.get()->containsErrors()) { + // Diagnostics were emitted during parsing. No action needed. + } else if (E.get()->isTypeDependent()) { + PP.Diag(StartLoc, diag::warn_pragma_debug_type_dependent_argument) + << SourceRange(StartLoc, Tok.getLocation()); + } else if (E.get()->isValueDependent()) { + PP.Diag(StartLoc, diag::warn_pragma_debug_value_dependent_argument) + << SourceRange(StartLoc, Tok.getLocation()); + } else { + Actions.ActOnPragmaDump(E.get()); + } + SkipUntil(tok::eod, StopBeforeMatch); + } + ExpectAndConsume(tok::eod); } void Parser::HandlePragmaWeak() { diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -5841,3 +5841,7 @@ LookupName(R, S); R.dump(); } + +void Sema::ActOnPragmaDump(Expr *E) { + E->dump(); +} diff --git a/clang/test/AST/ast-dump-lookups.cpp b/clang/test/AST/ast-dump-lookups.cpp --- a/clang/test/AST/ast-dump-lookups.cpp +++ b/clang/test/AST/ast-dump-lookups.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++11 -ast-dump -ast-dump-filter Test %s | FileCheck -check-prefix DECLS %s // RUN: %clang_cc1 -std=c++11 -ast-dump-lookups -ast-dump-filter Test %s | FileCheck -check-prefix LOOKUPS %s // RUN: %clang_cc1 -std=c++11 -ast-dump -ast-dump-lookups -ast-dump-filter Test %s | FileCheck -check-prefix DECLS-LOOKUPS %s -// RUN: %clang_cc1 -std=c++11 -DPRAGMA -fsyntax-only %s 2>&1 | FileCheck -check-prefix PRAGMA %s +// RUN: %clang_cc1 -std=c++11 -DPRAGMA -fsyntax-only -verify %s 2>&1 | FileCheck -check-prefix PRAGMA %s namespace Test { typedef int T; @@ -51,3 +51,32 @@ // // DECLS-LOOKUPS: Dumping Test: // DECLS-LOOKUPS-NEXT: Lookup map is in primary DeclContext + +#ifdef PRAGMA +namespace Test { + struct S { + const S& operator+(const S&) { return *this; } + }; + void foo(S) {} +} + +#pragma clang __debug dump foo(Test::S{}) +// PRAGMA: CallExpr {{.*}} adl +// PRAGMA-NEXT: |-ImplicitCastExpr {{.*}} +// PRAGMA-NEXT: | `-DeclRefExpr {{.*}} 'void (S)' lvalue Function {{.*}} 'foo' 'void (S)' + +#pragma clang __debug dump Test::S{} + Test::S{} +// PRAGMA: CXXOperatorCallExpr {{.*}} +// PRAGMA-NEXT: |-ImplicitCastExpr {{.*}} +// PRAGMA-NEXT: | `-DeclRefExpr {{.*}} 'const S &(const S &)' lvalue CXXMethod {{.*}} 'operator+' 'const S &(const S &)' + +#pragma clang __debug dump &Test::S::operator+ +// PRAGMA: UnaryOperator {{.*}} +// PRAGMA-NEXT: `-DeclRefExpr {{.*}} 'const S &(const S &)' CXXMethod {{.*}} 'operator+' 'const S &(const S &)' + +template +void bar() { +#pragma clang __debug dump T{} // expected-warning {{type-dependent expression}} +#pragma clang __debug dump +I // expected-warning {{value-dependent expression}} +} +#endif \ No newline at end of file