diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4917,3 +4917,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, all of debugging pragmas are subject to change. + +`dump` +------ +Accepts either a single identifier or an expression. When a single identifier is passed, +the lookup results for the identifier are printed to `stderr`. When an expression is passed, +the AST for the expression is printed to `stderr`. The expression is an unevaluated operand, +so things like overload resolution and template instantiations are performed, +but the expression has no runtime effects. +Type- and value-dependent expressions are not supported yet. + +This facility is designed to aid with testing name lookup machinery. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -126,6 +126,7 @@ from the invocation point, with no path components included). - Clang now supports ``__builtin_assume_separate_storage`` that indicates that its arguments point to objects in separate storage allocations. +- Clang now supports expressions in ``#pragma clang __debug dump``. New Compiler Flags ------------------ diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -63,6 +63,10 @@ "character literal with user-defined suffix cannot be used here">; def err_invalid_numeric_udl : Error< "numeric literal with user-defined suffix cannot be used here">; +def warn_pragma_debug_missing_argument : Warning< + "missing argument to debug command '%0'">, InGroup; +def warn_pragma_debug_unexpected_argument : Warning< + "unexpected argument to debug command">, InGroup; } 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 @@ -655,10 +655,6 @@ "missing debug command">, InGroup; def warn_pragma_debug_unexpected_command : Warning< "unexpected debug command '%0'">, InGroup; -def warn_pragma_debug_missing_argument : Warning< - "missing argument to debug command '%0'">, InGroup; -def warn_pragma_debug_unexpected_argument : Warning< - "unexpected argument to debug command">, InGroup; def warn_pragma_debug_unknown_module : Warning< "unknown module '%0'">, InGroup; // #pragma module diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1300,6 +1300,10 @@ def warn_no_support_for_eval_method_source_on_m32 : Warning< "Setting the floating point evaluation method to `source` on a target" " without SSE is not supported.">, InGroup; +// - #pragma __debug +def warn_pragma_debug_dependent_argument : Warning< + "%select{value|type}0-dependent expression passed as an argument to debug " + "command">, InGroup; // OpenCL EXTENSION pragma (OpenCL 1.1 [9.1]) def warn_pragma_expected_colon : Warning< 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 @@ -10703,6 +10703,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 @@ -706,10 +706,36 @@ 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(); + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + ExprResult E = ParseExpression(); + if (!E.isUsable() || E.get()->containsErrors()) { + // Diagnostics were emitted during parsing. No action needed. + } else if (E.get()->getDependence() != ExprDependence::None) { + PP.Diag(StartLoc, diag::warn_pragma_debug_dependent_argument) + << E.get()->isTypeDependent() + << 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,50 @@ // // 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}} +} + +template +struct S { + static constexpr const T *str = "string"; +}; + +template <> +struct S { + static constexpr const wchar_t *str = L"wide string"; +}; + +void func() { + #pragma clang __debug dump S::str; + // PRAGMA: DeclRefExpr {{.*}} 'const wchar_t *const' lvalue Var {{.*}} 'str' 'const wchar_t *const' +} + +#pragma clang __debug dump this is nonsense // expected-error {{invalid use of 'this'}} + +#endif