[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

llvm-svn: 332799
diff --git a/clang/test/SemaCXX/anonymous-struct.cpp b/clang/test/SemaCXX/anonymous-struct.cpp
index bb7e6eb..f89d199 100644
--- a/clang/test/SemaCXX/anonymous-struct.cpp
+++ b/clang/test/SemaCXX/anonymous-struct.cpp
@@ -16,7 +16,7 @@
   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 {
diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp
index ed26660..f814a06 100644
--- a/clang/test/SemaCXX/cxx98-compat.cpp
+++ b/clang/test/SemaCXX/cxx98-compat.cpp
@@ -241,13 +241,13 @@
     ~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 @@
   };
 
   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}}
   };
 }
 
diff --git a/clang/test/TableGen/DiagnosticBase.inc b/clang/test/TableGen/DiagnosticBase.inc
index afa85f5..e9a8b97 100644
--- a/clang/test/TableGen/DiagnosticBase.inc
+++ b/clang/test/TableGen/DiagnosticBase.inc
@@ -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; }
diff --git a/clang/test/TableGen/DiagnosticDocs.inc b/clang/test/TableGen/DiagnosticDocs.inc
new file mode 100644
index 0000000..4b11405
--- /dev/null
+++ b/clang/test/TableGen/DiagnosticDocs.inc
@@ -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
+================
+}];
+}
diff --git a/clang/test/TableGen/emit-diag-docs.td b/clang/test/TableGen/emit-diag-docs.td
new file mode 100644
index 0000000..b2ecba7
--- /dev/null
+++ b/clang/test/TableGen/emit-diag-docs.td
@@ -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">;
+
+}
diff --git a/clang/test/TableGen/text-substitution.td b/clang/test/TableGen/text-substitution.td
new file mode 100644
index 0000000..aafdbe4
--- /dev/null
+++ b/clang/test/TableGen/text-substitution.td
@@ -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",
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index a812cd1..f5cc72b 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -57,7 +57,8 @@
 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'),
 ]