[Clang Tablegen][RFC] Allow Early Textual Substitutions in `Diagnostic` messages.

Summary:
There are cases where the same string or select is repeated verbatim in a lot of diagnostics. This can be a pain to maintain and update. Tablegen provides no way stash the common text somewhere and reuse it in the diagnostics, until now!

This patch allows diagnostic texts to contain `%sub{<definition-name>}`, where `<definition-name>` names a Tablegen record of type `TextSubstitution`. These substitutions are done early, before the diagnostic string is otherwise processed. All `%sub` modifiers will be replaced before the diagnostic definitions are emitted.

The substitution must specify all arguments used by the substitution, and modifier indexes in the substitution are re-numbered accordingly. For example:

```
def select_ovl_candidate : TextSubstitution<"%select{function|constructor}0%select{| template| %2}1">;
```
when used as
```
"candidate `%sub{select_ovl_candidate}3,2,1 not viable"
```
will act as if we wrote:
```
"candidate %select{function|constructor}3%select{| template| %1}2 not viable"
```

Reviewers: rsmith, rjmccall, aaron.ballman, a.sidorin

Reviewed By: rjmccall

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D46740

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@332799 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier 2018-05-19 03:12:04 +00:00
parent 5b04748157
commit a6a3cf5fce
11 changed files with 1104 additions and 317 deletions

View File

@ -319,6 +319,32 @@ they should be discussed before they are added. If you are creating a lot of
repetitive diagnostics and/or have an idea for a useful formatter, please bring
it up on the cfe-dev mailing list.
**"sub" format**
Example:
Given the following record definition of type ``TextSubstitution``:
.. code-block:: text
def select_ovl_candidate : TextSubstitution<
"%select{function|constructor}0%select{| template| %2}1">;
which can be used as
.. code-block:: text
def note_ovl_candidate : Note<
"candidate %sub{select_ovl_candidate}3,2,1 not viable">;
and will act as if it was written
``"candidate %select{function|constructor}3%select{| template| %1}2 not viable"``.
Description:
This format specifier is used to avoid repeating strings verbatim in multiple
diagnostics. The argument to ``%sub`` must name a ``TextSubstitution`` tblgen
record. The substitution must specify all arguments used by the substitution,
and the modifier indexes in the substitution are re-numbered accordingly. The
substituted text must itself be a valid format string before substitution.
.. _internals-producing-diag:
Producing the Diagnostic

View File

@ -39,6 +39,15 @@ def SFINAE_Suppress : SFINAEResponse;
def SFINAE_Report : SFINAEResponse;
def SFINAE_AccessControl : SFINAEResponse;
// Textual substitutions which may be performed on the text of diagnostics
class TextSubstitution<string Text> {
string Substitution = Text;
// TODO: These are only here to allow substitutions to be declared inline with
// diagnostics
string Component = "";
string CategoryName = "";
}
// Diagnostic Categories. These can be applied to groups or individual
// diagnostics to specify a category.
class DiagCategory<string Name> {

View File

@ -1622,13 +1622,16 @@ def warn_call_to_pure_virtual_member_function_from_ctor_dtor : Warning<
"overrides of %0 in subclasses are not available in the "
"%select{constructor|destructor}1 of %2">;
def select_special_member_kind : TextSubstitution<
"%select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}0">;
def note_member_declared_at : Note<"member is declared here">;
def note_ivar_decl : Note<"instance variable is declared here">;
def note_bitfield_decl : Note<"bit-field is declared here">;
def note_implicit_param_decl : Note<"%0 is an implicit parameter">;
def note_member_synthesized_at : Note<
"in implicit %select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}0 for %1 "
"in implicit %sub{select_special_member_kind}0 for %1 "
"first required here">;
def err_missing_default_ctor : Error<
"%select{constructor for %1 must explicitly initialize the|"
@ -1641,12 +1644,10 @@ def note_due_to_dllexported_class : Note<
def err_illegal_union_or_anon_struct_member : Error<
"%select{anonymous struct|union}0 member %1 has a non-trivial "
"%select{constructor|copy constructor|move constructor|copy assignment "
"operator|move assignment operator|destructor}2">;
"%sub{select_special_member_kind}2">;
def warn_cxx98_compat_nontrivial_union_or_anon_struct_member : Warning<
"%select{anonymous struct|union}0 member %1 with a non-trivial "
"%select{constructor|copy constructor|move constructor|copy assignment "
"operator|move assignment operator|destructor}2 is incompatible with C++98">,
"%sub{select_special_member_kind}2 is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def note_nontrivial_virtual_dtor : Note<
@ -1665,8 +1666,7 @@ def note_nontrivial_no_copy : Note<
"%select{base class|field|an object}0 of type %3">;
def note_nontrivial_user_provided : Note<
"because %select{base class of |field of |}0type %1 has a user-provided "
"%select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}2">;
"%sub{select_special_member_kind}2">;
def note_nontrivial_in_class_init : Note<
"because field %0 has an initializer">;
def note_nontrivial_param_type : Note<
@ -1733,9 +1733,7 @@ def err_covariant_return_type_class_type_more_qualified : Error<
// C++ implicit special member functions
def note_in_declaration_of_implicit_special_member : Note<
"while declaring the implicit "
"%select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}1"
"while declaring the implicit %sub{select_special_member_kind}1"
" for %0">;
// C++ constructors
@ -3837,13 +3835,7 @@ def note_ovl_candidate_bad_target : Note<
"%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from"
" %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">;
def note_implicit_member_target_infer_collision : Note<
"implicit %select{"
"default constructor|"
"copy constructor|"
"move constructor|"
"copy assignment operator|"
"move assignment operator|"
"destructor}0 inferred target collision: call to both "
"implicit %sub{select_special_member_kind}0 inferred target collision: call to both "
"%select{__device__|__global__|__host__|__host__ __device__}1 and "
"%select{__device__|__global__|__host__|__host__ __device__}2 members">;
@ -3885,9 +3877,7 @@ def err_ovl_deleted_oper : Error<
"overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">;
def err_ovl_deleted_special_oper : Error<
"object of type %0 cannot be %select{constructed|copied|moved|assigned|"
"assigned|destroyed}1 because its %select{default constructor|"
"copy constructor|move constructor|copy assignment operator|"
"move assignment operator|destructor}1 is implicitly deleted">;
"assigned|destroyed}1 because its %sub{select_special_member_kind}1 is implicitly deleted">;
def err_ovl_no_viable_subscript :
Error<"no viable overloaded operator[] for type %0">;
def err_ovl_no_oper :
@ -7767,9 +7757,8 @@ def err_defaulted_special_member_quals : Error<
"an explicitly-defaulted %select{copy|move}0 assignment operator may not "
"have 'const'%select{, 'constexpr'|}1 or 'volatile' qualifiers">;
def err_defaulted_special_member_volatile_param : Error<
"the parameter for an explicitly-defaulted %select{<<ERROR>>|"
"copy constructor|move constructor|copy assignment operator|"
"move assignment operator|<<ERROR>>}0 may not be volatile">;
"the parameter for an explicitly-defaulted %sub{select_special_member_kind}0 "
"may not be volatile">;
def err_defaulted_special_member_move_const_param : Error<
"the parameter for an explicitly-defaulted move "
"%select{constructor|assignment operator}0 may not be const">;
@ -7781,17 +7770,13 @@ def err_defaulted_copy_assign_not_ref : Error<
"the parameter for an explicitly-defaulted copy assignment operator must be an "
"lvalue reference type">;
def err_incorrect_defaulted_exception_spec : Error<
"exception specification of explicitly defaulted %select{default constructor|"
"copy constructor|move constructor|copy assignment operator|move assignment "
"operator|destructor}0 does not match the "
"calculated one">;
"exception specification of explicitly defaulted "
"%sub{select_special_member_kind}0 does not match the calculated one">;
def err_incorrect_defaulted_constexpr : Error<
"defaulted definition of %select{default constructor|copy constructor|"
"move constructor|copy assignment operator|move assignment operator}0 "
"defaulted definition of %sub{select_special_member_kind}0 "
"is not constexpr">;
def err_out_of_line_default_deletes : Error<
"defaulting this %select{default constructor|copy constructor|move "
"constructor|copy assignment operator|move assignment operator|destructor}0 "
"defaulting this %sub{select_special_member_kind}0 "
"would delete it after its first declaration">;
def warn_vbase_moved_multiple_times : Warning<
"defaulted move assignment operator of %0 will move assign virtual base "

View File

@ -16,7 +16,7 @@ struct E {
struct {
S x;
#if __cplusplus <= 199711L
// expected-error@-2 {{anonymous struct member 'x' has a non-trivial constructor}}
// expected-error@-2 {{anonymous struct member 'x' has a non-trivial default constructor}}
#endif
};
static struct {

View File

@ -241,13 +241,13 @@ namespace UnionOrAnonStructMembers {
~NonTrivDtor(); // expected-note 2{{user-provided destructor}}
};
union BadUnion {
NonTrivCtor ntc; // expected-warning {{union member 'ntc' with a non-trivial constructor is incompatible with C++98}}
NonTrivCtor ntc; // expected-warning {{union member 'ntc' with a non-trivial default constructor is incompatible with C++98}}
NonTrivCopy ntcp; // expected-warning {{union member 'ntcp' with a non-trivial copy constructor is incompatible with C++98}}
NonTrivDtor ntd; // expected-warning {{union member 'ntd' with a non-trivial destructor is incompatible with C++98}}
};
struct Wrap {
struct {
NonTrivCtor ntc; // expected-warning {{anonymous struct member 'ntc' with a non-trivial constructor is incompatible with C++98}}
NonTrivCtor ntc; // expected-warning {{anonymous struct member 'ntc' with a non-trivial default constructor is incompatible with C++98}}
NonTrivCopy ntcp; // expected-warning {{anonymous struct member 'ntcp' with a non-trivial copy constructor is incompatible with C++98}}
NonTrivDtor ntd; // expected-warning {{anonymous struct member 'ntd' with a non-trivial destructor is incompatible with C++98}}
};
@ -348,7 +348,7 @@ namespace rdar11736429 {
};
union S {
X x; // expected-warning{{union member 'x' with a non-trivial constructor is incompatible with C++98}}
X x; // expected-warning{{union member 'x' with a non-trivial default constructor is incompatible with C++98}}
};
}

View File

@ -1,35 +1,130 @@
// Define the diagnostic mappings.
class DiagMapping;
def MAP_IGNORE : DiagMapping;
def MAP_WARNING : DiagMapping;
def MAP_ERROR : DiagMapping;
def MAP_FATAL : DiagMapping;
//===--- DiagnosticBase.inc - A test file mimicking Diagnostic.td ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the TableGen core definitions for the diagnostics
// and diagnostic control.
//
//===----------------------------------------------------------------------===//
// See the Internals Manual, section The Diagnostics Subsystem for an overview.
// Define the diagnostic severities.
class Severity<string N> {
string Name = N;
}
def SEV_Ignored : Severity<"Ignored">;
def SEV_Remark : Severity<"Remark">;
def SEV_Warning : Severity<"Warning">;
def SEV_Error : Severity<"Error">;
def SEV_Fatal : Severity<"Fatal">;
// Define the diagnostic classes.
class DiagClass;
def CLASS_NOTE : DiagClass;
def CLASS_REMARK : DiagClass;
def CLASS_WARNING : DiagClass;
def CLASS_EXTENSION : DiagClass;
def CLASS_ERROR : DiagClass;
// Responses to a diagnostic in a SFINAE context.
class SFINAEResponse;
def SFINAE_SubstitutionFailure : SFINAEResponse;
def SFINAE_Suppress : SFINAEResponse;
def SFINAE_Report : SFINAEResponse;
def SFINAE_AccessControl : SFINAEResponse;
// Textual substitutions which may be performed on the text of diagnostics
class TextSubstitution<string Text> {
string Substitution = Text;
// TODO: These are only here to allow substitutions to be declared inline with
// diagnostics
string Component = "";
string CategoryName = "";
}
// Diagnostic Categories. These can be applied to groups or individual
// diagnostics to specify a category.
class DiagCategory<string Name> {
string CategoryName = Name;
}
// Diagnostic Groups.
class DiagGroup<string Name, list<DiagGroup> subgroups = []> {
string GroupName = Name;
list<DiagGroup> SubGroups = subgroups;
string CategoryName = "";
code Documentation = [{}];
}
class InGroup<DiagGroup G> { DiagGroup Group = G; }
//class IsGroup<string Name> { DiagGroup Group = DiagGroup<Name>; }
include "DiagnosticDocs.inc"
// All diagnostics emitted by the compiler are an indirect subclass of this.
class Diagnostic<string text, DiagClass DC, DiagMapping defaultmapping> {
string Text = text;
DiagClass Class = DC;
DiagMapping DefaultMapping = defaultmapping;
DiagGroup Group;
string CategoryName = "";
class Diagnostic<string text, DiagClass DC, Severity defaultmapping> {
/// Component is specified by the file with a big let directive.
string Component = ?;
string Text = text;
DiagClass Class = DC;
SFINAEResponse SFINAE = SFINAE_Suppress;
bit AccessControl = 0;
bit WarningNoWerror = 0;
bit ShowInSystemHeader = 0;
Severity DefaultSeverity = defaultmapping;
DiagGroup Group;
string CategoryName = "";
}
class Error<string str> : Diagnostic<str, CLASS_ERROR, MAP_ERROR>;
class Warning<string str> : Diagnostic<str, CLASS_WARNING, MAP_WARNING>;
class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, MAP_IGNORE>;
class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, MAP_WARNING>;
class Note<string str> : Diagnostic<str, CLASS_NOTE, MAP_FATAL/*ignored*/>;
class SFINAEFailure {
SFINAEResponse SFINAE = SFINAE_SubstitutionFailure;
}
class NoSFINAE {
SFINAEResponse SFINAE = SFINAE_Report;
}
class AccessControl {
SFINAEResponse SFINAE = SFINAE_AccessControl;
}
class ShowInSystemHeader {
bit ShowInSystemHeader = 1;
}
class SuppressInSystemHeader {
bit ShowInSystemHeader = 0;
}
// FIXME: ExtWarn and Extension should also be SFINAEFailure by default.
class Error<string str> : Diagnostic<str, CLASS_ERROR, SEV_Error>, SFINAEFailure {
bit ShowInSystemHeader = 1;
}
// Warnings default to on (but can be default-off'd with DefaultIgnore).
// This is used for warnings about questionable code; warnings about
// accepted language extensions should use Extension or ExtWarn below instead.
class Warning<string str> : Diagnostic<str, CLASS_WARNING, SEV_Warning>;
// Remarks can be turned on with -R flags and provide commentary, e.g. on
// optimizer decisions.
class Remark<string str> : Diagnostic<str, CLASS_REMARK, SEV_Ignored>;
// Extensions are warnings about accepted language extensions.
// Extension warnings are default-off but enabled by -pedantic.
class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Ignored>;
// ExtWarns are warnings about accepted language extensions.
// ExtWarn warnings are default-on.
class ExtWarn<string str> : Diagnostic<str, CLASS_EXTENSION, SEV_Warning>;
// Notes can provide supplementary information on errors, warnings, and remarks.
class Note<string str> : Diagnostic<str, CLASS_NOTE, SEV_Fatal/*ignored*/>;
class DefaultIgnore { Severity DefaultSeverity = SEV_Ignored; }
class DefaultWarn { Severity DefaultSeverity = SEV_Warning; }
class DefaultError { Severity DefaultSeverity = SEV_Error; }
class DefaultFatal { Severity DefaultSeverity = SEV_Fatal; }
class DefaultWarnNoWerror {
bit WarningNoWerror = 1;
}
class DefaultRemark { Severity DefaultSeverity = SEV_Remark; }

View File

@ -0,0 +1,75 @@
def GlobalDocumentation {
code Intro =[{..
-------------------------------------------------------------------
NOTE: This file is automatically generated by running clang-tblgen
-gen-diag-docs. Do not edit this file by hand!!
-------------------------------------------------------------------
.. Add custom CSS to output. FIXME: This should be put into <head> rather
than the start of <body>.
.. raw:: html
<style>
table.docutils {
width: 1px;
}
table.docutils td {
border: none;
padding: 0 0 0 0.2em;
vertical-align: middle;
white-space: nowrap;
width: 1px;
font-family: monospace;
}
table.docutils tr + tr {
border-top: 0.2em solid #aaa;
}
.error {
font-family: monospace;
font-weight: bold;
color: #c00;
}
.warning {
font-family: monospace;
font-weight: bold;
color: #80a;
}
.remark {
font-family: monospace;
font-weight: bold;
color: #00c;
}
.diagtext {
font-family: monospace;
font-weight: bold;
}
</style>
.. FIXME: rST doesn't support formatting this, so we format all <td> elements
as monospace font face instead.
.. |nbsp| unicode:: 0xA0
:trim:
.. Roles generated by clang-tblgen.
.. role:: error
.. role:: warning
.. role:: remark
.. role:: diagtext
.. role:: placeholder(emphasis)
=========================
Diagnostic flags in Clang
=========================
.. contents::
:local:
Introduction
============
This page lists the diagnostic flags currently supported by Clang.
Diagnostic flags
================
}];
}

View File

@ -0,0 +1,78 @@
// RUN: clang-tblgen -gen-diag-docs -I%S %s -o - 2>&1 | \
// RUN: FileCheck --strict-whitespace %s
include "DiagnosticBase.inc"
def MyGroup : DiagGroup<"MyGroupName">;
def MyKinds : TextSubstitution<"%select{food|forests}0">;
def MyGoodBad : TextSubstitution<"%select{good|bad}0">;
def MySubNested : TextSubstitution<"%sub{MyGoodBad}1 %sub{MyKinds}2 are %sub{MyGoodBad}1 according to %0">;
// CHECK: -WMyGroupName
// CHECK: **Diagnostic text:**
let Group = MyGroup in {
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my diff text`|
// CHECK-NEXT: +-----------------------------------------------------------+
def CheckDiff : Warning<"%diff{$ is not $|this is my diff text}0,1">;
// CHECK: |:warning:`warning:` |nbsp| :placeholder:`A` |nbsp| :diagtext:`is my modifier test` |nbsp| :placeholder:`B`|
// CHECK-NEXT: +----------------------------------------------------------------------------------------------------------+
def CheckModifier : Warning<"%0 is my modifier test %1">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`This is the` |nbsp| :placeholder:`A` |nbsp| :diagtext:`test I've written`|
// CHECK-NEXT: +---------------------------------------------------------------------------------------------------------------+
def CheckOrdinal : Warning<"This is the %ordinal0 test I've written">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`I wrote` |nbsp| |+----------------+| |nbsp| :diagtext:`tests`|
// CHECK-NEXT: | ||:diagtext:`no` || |
// CHECK-NEXT: | |+----------------+| |
// CHECK-NEXT: | ||:diagtext:`one` || |
// CHECK-NEXT: | |+----------------+| |
// CHECK-NEXT: | ||:placeholder:`A`|| |
// CHECK-NEXT: | |+----------------+| |
// CHECK-NEXT: +------------------------------------------------------+------------------+-------------------------+
def CheckPlural : Warning<"I wrote %plural{0:no|1:one|:%0}0 tests">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`bad type` |nbsp| :placeholder:`A`|
// CHECK-NEXT: +-----------------------------------------------------------------------+
def CheckQ : Warning<"bad type %q0">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`My test`|+-------------+| |nbsp| :diagtext:`are the best!`|
// CHECK-NEXT: | || || |
// CHECK-NEXT: | |+-------------+| |
// CHECK-NEXT: | ||:diagtext:`s`|| |
// CHECK-NEXT: | |+-------------+| |
// CHECK-NEXT: +----------------------------------------------+---------------+---------------------------------+
def CheckS : Warning<"My test%s0 are the best!">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my select test:` |nbsp| |+---------------+|
// CHECK-NEXT: | ||:diagtext:`one`||
// CHECK-NEXT: | |+---------------+|
// CHECK-NEXT: | ||:diagtext:`two`||
// CHECK-NEXT: | |+---------------+|
// CHECK-NEXT: +----------------------------------------------------------------------+-----------------+
def CheckSelect : Warning<"this is my select test: %select{one|two}0 and it is %select{good|bad}1">;
// CHECK: +-------------------------------------------------------+------------------+--------+---------------------+-------------------------------+------------------+--------------------------------------------------------+
// CHECK-NEXT: |:warning:`warning:` |nbsp| :diagtext:`They say` |nbsp| |+----------------+| |nbsp| |+-------------------+| |nbsp| :diagtext:`are` |nbsp| |+----------------+| |nbsp| :diagtext:`according to` |nbsp| :placeholder:`D`|
// CHECK-NEXT: | ||:diagtext:`good`|| ||:diagtext:`food` || ||:diagtext:`good`|| |
// CHECK-NEXT: | |+----------------+| |+-------------------+| |+----------------+| |
// CHECK-NEXT: | ||:diagtext:`bad` || ||:diagtext:`forests`|| ||:diagtext:`bad` || |
// CHECK-NEXT: | |+----------------+| |+-------------------+| |+----------------+| |
// CHECK-NEXT: +-------------------------------------------------------+------------------+--------+---------------------+-------------------------------+------------------+--------------------------------------------------------+
def CheckSubstitution : Warning<"They say %sub{MySubNested}3,1,0">;
// CHECK: |:warning:`warning:` |nbsp| :diagtext:`this is my warning text`|
// CHECK-NEXT: +--------------------------------------------------------------+
def CheckText : Warning<"this is my warning text">;
}

View File

@ -0,0 +1,38 @@
// RUN: clang-tblgen -gen-clang-diags-defs -I%S %s -o - 2>&1 | \
// RUN: FileCheck --strict-whitespace %s
include "DiagnosticBase.inc"
def yes_no : TextSubstitution<"%select{yes|no}0">;
def says_yes : TextSubstitution<"%1 says %sub{yes_no}0">;
def sub_test_rewrite : TextSubstitution<
"SELECT! %select{one|two}3. "
"DIFF! %diff{$ is $|or not}0,1. "
"PLURAL! %plural{0:zero items|[1,2]:one or two item|:multiple items}2. "
"ORDINAL! %ordinal1. "
"S! item%s2. "
"Q! %q4. "
"PLACEHOLDER! %5."
"OBJCCLASS! %objcclass0. "
"OBJCINSTANCE! %objcinstance1. ">;
// CHECK: DIAG(test_rewrite,
// CHECK-SAME: SELECT! %select{one|two}2.
// CHECK-SAME: DIFF! %diff{$ is $|or not}5,4.
// CHECK-SAME: PLURAL! %plural{0:zero items|[1,2]:one or two item|:multiple items}3.
// CHECK-SAME: ORDINAL! %ordinal4.
// CHECK-SAME: S! item%s3.
// CHECK-SAME: Q! %q1.
// CHECK-SAME: PLACEHOLDER! %0.OBJCCLASS!
// CHECK-SAME: %objcclass5. OBJCINSTANCE!
// CHECK-SAME: %objcinstance4. DONE!",
def test_rewrite: Error<"%sub{sub_test_rewrite}5,4,3,2,1,0 DONE!">;
def test_sub_basic : Error<"%sub{yes_no}0">;
// CHECK: test_sub_basic
// CHECK-SAME: "%select{yes|no}0",
def test_sub_nested : Error<"%sub{says_yes}2,4">;
// CHECK: test_sub_nested
// CHECK-SAME: "%4 says %select{yes|no}2",

View File

@ -57,7 +57,8 @@ config.substitutions.append(('%PATH%', config.environment['PATH']))
tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir]
tools = [
'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'opt',
'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'clang-tblgen',
'opt',
ToolSubst('%clang_func_map', command=FindTool(
'clang-func-mapping'), unresolved='ignore'),
]

File diff suppressed because it is too large Load Diff