Skip to content

Commit

Permalink
[Clang Tablegen][RFC] Allow Early Textual Substitutions in `Diagnosti…
Browse files Browse the repository at this point in the history
…c` 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
  • Loading branch information
EricWF committed May 19, 2018
1 parent 926b0b2 commit b87be18
Show file tree
Hide file tree
Showing 11 changed files with 1,106 additions and 319 deletions.
26 changes: 26 additions & 0 deletions clang/docs/InternalsManual.rst
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/Diagnostic.td
Expand Up @@ -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> {
Expand Down
49 changes: 17 additions & 32 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -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|"
Expand All @@ -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<
Expand All @@ -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<
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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">;

Expand Down Expand Up @@ -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 :
Expand Down Expand Up @@ -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">;
Expand All @@ -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 "
Expand Down
2 changes: 1 addition & 1 deletion clang/test/SemaCXX/anonymous-struct.cpp
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions clang/test/SemaCXX/cxx98-compat.cpp
Expand Up @@ -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}}
};
Expand Down Expand Up @@ -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}}
};
}

Expand Down
133 changes: 114 additions & 19 deletions 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 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 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 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; }

0 comments on commit b87be18

Please sign in to comment.