Skip to content

Commit 270ce96

Browse files
committed
[clang] consistently quote expressions in diagnostics
This changes the expression diagnostic printer to always add quotes, and removes hardcoded quotes from the diagnostic format strings. In some cases, a placeholder could be filled by either an expression or as a string. In order to quote this consistently, a new modifier was added, which can be used to quote strings. One diagnostic was relying on unquoted expressions in order to generate code suggestions. This diagnostic is converted to use fixit hints instead.
1 parent d057811 commit 270ce96

File tree

51 files changed

+214
-189
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+214
-189
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ Improvements to Clang's diagnostics
283283
- Clang now respects the current language mode when printing expressions in
284284
diagnostics. This fixes a bunch of `bool` being printed as `_Bool`, and also
285285
a bunch of HLSL types being printed as their C++ equivalents.
286+
- Clang now consistently quotes expressions in diagnostics.
286287
- When printing types for diagnostics, clang now doesn't suppress the scopes of
287288
template arguments contained within nested names.
288289
- The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ def note_constexpr_null_subobject : Note<
143143
"access array element of|perform pointer arithmetic on|"
144144
"access real component of|"
145145
"access imaginary component of}0 null pointer">;
146-
def note_constexpr_null_callee : Note<
147-
"'%0' evaluates to a null function pointer">;
146+
def note_constexpr_null_callee
147+
: Note<"%0 evaluates to a null function pointer">;
148148
def note_constexpr_function_param_value_unknown : Note<
149149
"function parameter %0 with unknown value cannot be used in a constant "
150150
"expression">;

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1639,7 +1639,7 @@ def note_omp_ctx_compatible_set_and_selector_for_property
16391639
"'match(%2={%1(%0)})'">;
16401640
def warn_omp_ctx_incompatible_score_for_property
16411641
: Warning<"the context selector '%0' in the context set '%1' cannot have a "
1642-
"score ('%2'); score ignored">,
1642+
"score (%quoted2); score ignored">,
16431643
InGroup<OpenMPClauses>;
16441644
def warn_omp_more_one_device_type_clause
16451645
: Warning<"more than one 'device_type' clause is specified">,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,10 @@ def err_expr_not_string_literal : Error<"expression is not a string literal">;
167167
def ext_predef_outside_function : Warning<
168168
"predefined identifier is only valid inside function">,
169169
InGroup<DiagGroup<"predefined-identifier-outside-function">>;
170-
def ext_init_from_predefined : ExtWarn<
171-
"initializing an array from a '%0' predefined identifier is a Microsoft extension">,
172-
InGroup<MicrosoftInitFromPredefined>;
170+
def ext_init_from_predefined
171+
: ExtWarn<"initializing an array from a %0 predefined identifier is a "
172+
"Microsoft extension">,
173+
InGroup<MicrosoftInitFromPredefined>;
173174
def ext_string_literal_from_predefined : ExtWarn<
174175
"expansion of predefined identifier '%0' to a string literal is a Microsoft extension">,
175176
InGroup<MicrosoftStringLiteralFromPredefined>;
@@ -3019,14 +3020,14 @@ def note_substituted_constraint_expr_is_ill_formed : Note<
30193020
"because substituted constraint expression is ill-formed%0">;
30203021
def note_constraint_references_error
30213022
: Note<"constraint depends on a previously diagnosed expression">;
3022-
def note_atomic_constraint_evaluated_to_false : Note<
3023-
"%select{and|because}0 '%1' evaluated to false">;
3024-
def note_concept_specialization_constraint_evaluated_to_false : Note<
3025-
"%select{and|because}0 '%1' evaluated to false">;
3023+
def note_atomic_constraint_evaluated_to_false
3024+
: Note<"%select{and|because}0 %1 evaluated to false">;
3025+
def note_concept_specialization_constraint_evaluated_to_false
3026+
: Note<"%select{and|because}0 %1 evaluated to false">;
30263027
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
30273028
"%select{and|because}0 %1 does not satisfy %2">;
3028-
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
3029-
"%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">;
3029+
def note_atomic_constraint_evaluated_to_false_elaborated
3030+
: Note<"%select{and|because}0 %1 (%2 %3 %4) evaluated to false">;
30303031
def note_is_deducible_constraint_evaluated_to_false : Note<
30313032
"cannot deduce template arguments for %0 from %1">;
30323033
def err_constrained_virtual_method : Error<
@@ -3046,14 +3047,14 @@ def note_expr_requirement_expr_substitution_error : Note<
30463047
"%select{and|because}0 '%1' would be invalid: %2">;
30473048
def note_expr_requirement_expr_unknown_substitution_error : Note<
30483049
"%select{and|because}0 '%1' would be invalid">;
3049-
def note_expr_requirement_noexcept_not_met : Note<
3050-
"%select{and|because}0 '%1' may throw an exception">;
3050+
def note_expr_requirement_noexcept_not_met
3051+
: Note<"%select{and|because}0 %1 may throw an exception">;
30513052
def note_expr_requirement_type_requirement_substitution_error : Note<
30523053
"%select{and|because}0 '%1' would be invalid: %2">;
30533054
def note_expr_requirement_type_requirement_unknown_substitution_error : Note<
30543055
"%select{and|because}0 '%1' would be invalid">;
3055-
def note_expr_requirement_constraints_not_satisfied : Note<
3056-
"%select{and|because}0 type constraint '%1' was not satisfied:">;
3056+
def note_expr_requirement_constraints_not_satisfied
3057+
: Note<"%select{and|because}0 type constraint %1 was not satisfied:">;
30573058
def note_expr_requirement_constraints_not_satisfied_simple : Note<
30583059
"%select{and|because}0 %1 does not satisfy %2:">;
30593060
def note_type_requirement_substitution_error : Note<
@@ -4352,9 +4353,9 @@ def note_reference_is_return_value : Note<"%0 returns a reference">;
43524353

43534354
def note_pointer_declared_here : Note<
43544355
"pointer %0 declared here">;
4355-
def warn_division_sizeof_ptr : Warning<
4356-
"'%0' will return the size of the pointer, not the array itself">,
4357-
InGroup<DiagGroup<"sizeof-pointer-div">>;
4356+
def warn_division_sizeof_ptr
4357+
: Warning<"%0 will return the size of the pointer, not the array itself">,
4358+
InGroup<DiagGroup<"sizeof-pointer-div">>;
43584359
def warn_division_sizeof_array : Warning<
43594360
"expression does not compute the number of elements in this array; element "
43604361
"type is %0, not %1">,
@@ -5577,8 +5578,9 @@ def note_function_member_spec_matched : Note<
55775578
def err_template_recursion_depth_exceeded : Error<
55785579
"recursive template instantiation exceeded maximum depth of %0">,
55795580
DefaultFatal, NoSFINAE;
5580-
def err_constraint_depends_on_self : Error<
5581-
"satisfaction of constraint '%0' depends on itself">, NoSFINAE;
5581+
def err_constraint_depends_on_self
5582+
: Error<"satisfaction of constraint %0 depends on itself">,
5583+
NoSFINAE;
55825584
def note_template_recursion_depth : Note<
55835585
"use -ftemplate-depth=N to increase recursive template instantiation depth">;
55845586

@@ -7094,8 +7096,8 @@ def warn_precedence_bitwise_rel : Warning<
70947096
InGroup<Parentheses>;
70957097
def note_precedence_bitwise_first : Note<
70967098
"place parentheses around the %0 expression to evaluate it first">;
7097-
def note_precedence_silence : Note<
7098-
"place parentheses around the '%0' expression to silence this warning">;
7099+
def note_precedence_silence : Note<"place parentheses around the %quoted0 "
7100+
"expression to silence this warning">;
70997101

71007102
def warn_precedence_conditional : Warning<
71017103
"operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">,
@@ -7113,9 +7115,11 @@ def warn_consecutive_comparison : Warning<
71137115
def warn_enum_constant_in_bool_context : Warning<
71147116
"converting the enum constant to a boolean">,
71157117
InGroup<IntInBoolContext>, DefaultIgnore;
7116-
def warn_left_shift_in_bool_context : Warning<
7117-
"converting the result of '<<' to a boolean; did you mean '(%0) != 0'?">,
7118-
InGroup<IntInBoolContext>, DefaultIgnore;
7118+
def warn_left_shift_in_bool_context
7119+
: Warning<"converting the result of '<<' to a boolean; did you mean to "
7120+
"compare with '0'?">,
7121+
InGroup<IntInBoolContext>,
7122+
DefaultIgnore;
71197123
def warn_logical_instead_of_bitwise : Warning<
71207124
"use of logical '%0' with constant operand">,
71217125
InGroup<DiagGroup<"constant-logical-operand">>;
@@ -10308,9 +10312,11 @@ def warn_dangling_pointer_assignment : Warning<
1030810312
"object backing %select{|the pointer }0%1 "
1030910313
"will be destroyed at the end of the full-expression">,
1031010314
InGroup<DanglingAssignment>;
10311-
def warn_dangling_reference_captured : Warning<
10312-
"object whose reference is captured by '%0' will be destroyed at the end of "
10313-
"the full-expression">, InGroup<DanglingCapture>;
10315+
def warn_dangling_reference_captured
10316+
: Warning<"object whose reference is captured by %0 will be destroyed at "
10317+
"the end of "
10318+
"the full-expression">,
10319+
InGroup<DanglingCapture>;
1031410320
def warn_dangling_reference_captured_by_unknown : Warning<
1031510321
"object whose reference is captured will be destroyed at the end of "
1031610322
"the full-expression">, InGroup<DanglingCapture>;
@@ -13009,15 +13015,15 @@ def note_acc_atomic_too_many_stmts
1300913015
def note_acc_atomic_expected_binop : Note<"expected binary operation on right "
1301013016
"hand side of assignment operator">;
1301113017
def note_acc_atomic_mismatch_operand
13012-
: Note<"left hand side of assignment operation('%0') must match one side "
13013-
"of the sub-operation on the right hand side('%1' and '%2')">;
13018+
: Note<"left hand side of assignment operation(%0) must match one side "
13019+
"of the sub-operation on the right hand side(%1 and %2)">;
1301413020
def note_acc_atomic_mismatch_compound_operand
1301513021
: Note<"variable %select{|in unary expression|on right hand side of "
1301613022
"assignment|on left hand side of assignment|on left hand side of "
13017-
"compound assignment|on left hand side of assignment}2('%3') must "
13023+
"compound assignment|on left hand side of assignment}2(%3) must "
1301813024
"match variable used %select{|in unary expression|on right hand "
1301913025
"side of assignment|<not possible>|on left hand side of compound "
13020-
"assignment|on left hand side of assignment}0('%1') from the first "
13026+
"assignment|on left hand side of assignment}0(%1) from the first "
1302113027
"statement">;
1302213028
def err_acc_declare_required_clauses
1302313029
: Error<"no valid clauses specified in OpenACC 'declare' directive">;
@@ -13037,9 +13043,9 @@ def err_acc_multiple_references
1303713043
: Error<"variable referenced in '%0' clause of OpenACC 'declare' directive "
1303813044
"was already referenced">;
1303913045
def err_acc_routine_not_func
13040-
: Error<"OpenACC routine name '%0' does not name a function">;
13046+
: Error<"OpenACC routine name %0 does not name a function">;
1304113047
def err_acc_routine_overload_set
13042-
: Error<"OpenACC routine name '%0' names a set of overloads">;
13048+
: Error<"OpenACC routine name %0 names a set of overloads">;
1304313049
def err_acc_magic_static_in_routine
1304413050
: Error<"function static variables are not permitted in functions to which "
1304513051
"an OpenACC 'routine' directive applies">;

clang/lib/AST/ASTDiagnostic.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,6 @@ void clang::FormatASTNodeDiagnosticArgument(
512512
const Expr *E = reinterpret_cast<Expr *>(Val);
513513
assert(E && "Received null Expr!");
514514
E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy());
515-
// FIXME: Include quotes when printing expressions.
516-
NeedQuotes = false;
517515
break;
518516
}
519517
}

clang/lib/Basic/Diagnostic.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,20 +1144,25 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
11441144

11451145
switch (Kind) {
11461146
// ---- STRINGS ----
1147-
case DiagnosticsEngine::ak_std_string: {
1148-
const std::string &S = getArgStdStr(ArgNo);
1149-
assert(ModifierLen == 0 && "No modifiers for strings yet");
1150-
EscapeStringForDiagnostic(S, OutStr);
1151-
break;
1152-
}
1147+
case DiagnosticsEngine::ak_std_string:
11531148
case DiagnosticsEngine::ak_c_string: {
1154-
const char *S = getArgCStr(ArgNo);
1155-
assert(ModifierLen == 0 && "No modifiers for strings yet");
1156-
1157-
// Don't crash if get passed a null pointer by accident.
1158-
if (!S)
1159-
S = "(null)";
1149+
StringRef S = [&]() -> StringRef {
1150+
if (Kind == DiagnosticsEngine::ak_std_string)
1151+
return getArgStdStr(ArgNo);
1152+
const char *SZ = getArgCStr(ArgNo);
1153+
// Don't crash if get passed a null pointer by accident.
1154+
return SZ ? SZ : "(null)";
1155+
}();
1156+
bool Quoted = false;
1157+
if (ModifierIs(Modifier, ModifierLen, "quoted")) {
1158+
Quoted = true;
1159+
OutStr.push_back('\'');
1160+
} else {
1161+
assert(ModifierLen == 0 && "unknown modifier for string");
1162+
}
11601163
EscapeStringForDiagnostic(S, OutStr);
1164+
if (Quoted)
1165+
OutStr.push_back('\'');
11611166
break;
11621167
}
11631168
// ---- INTEGERS ----

clang/lib/Sema/SemaChecking.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11683,7 +11683,10 @@ static void DiagnoseIntInBoolContext(Sema &S, Expr *E) {
1168311683
S.Diag(ExprLoc, diag::warn_left_shift_always)
1168411684
<< (Result.Val.getInt() != 0);
1168511685
else if (E->getType()->isSignedIntegerType())
11686-
S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context) << E;
11686+
S.Diag(ExprLoc, diag::warn_left_shift_in_bool_context)
11687+
<< FixItHint::CreateInsertion(E->getBeginLoc(), "(")
11688+
<< FixItHint::CreateInsertion(S.getLocForEndOfToken(E->getEndLoc()),
11689+
") != 0");
1168711690
}
1168811691
}
1168911692

clang/test/AST/ByteCode/literals.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ namespace CompoundLiterals {
920920
// null pointer suggests we're doing something odd during constant expression
921921
// evaluation: I think it's still taking 'x' as being null from the call to
922922
// f3() rather than tracking the assignment happening in the VLA.
923-
constexpr int f3(int *x, int (*y)[*(x=(int[]){1,2,3})]) { // both-warning {{object backing the pointer x will be destroyed at the end of the full-expression}}
923+
constexpr int f3(int *x, int (*y)[*(x=(int[]){1,2,3})]) { // both-warning {{object backing the pointer 'x' will be destroyed at the end of the full-expression}}
924924
return x[0]; // both-note {{read of dereferenced null pointer is not allowed in a constant expression}}
925925
}
926926
constexpr int h = f3(0,0); // both-error {{constexpr variable 'h' must be initialized by a constant expression}} \

clang/test/CodeGenCXX/sections.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ int D = 1;
1818
#pragma data_seg(".data")
1919
int a = 1;
2020
extern const Mutable mutable_custom_section;
21-
const Mutable mutable_custom_section; // expected-warning {{`#pragma const_seg` for section ".my_const" will not apply to 'mutable_custom_section' due to the presence of a mutable field}}
21+
const Mutable mutable_custom_section; // expected-warning {{`#pragma const_seg` for section '".my_const"' will not apply to 'mutable_custom_section' due to the presence of a mutable field}}
2222
extern const Normal normal_custom_section;
2323
const Normal normal_custom_section;
2424
struct NonTrivialDtor {
2525
~NonTrivialDtor();
2626
};
2727
extern const NonTrivialDtor non_trivial_dtor_custom_section;
28-
const NonTrivialDtor non_trivial_dtor_custom_section; // expected-warning {{`#pragma const_seg` for section ".my_const" will not apply to 'non_trivial_dtor_custom_section' due to the presence of a non-trivial destructor}}
28+
const NonTrivialDtor non_trivial_dtor_custom_section; // expected-warning {{`#pragma const_seg` for section '".my_const"' will not apply to 'non_trivial_dtor_custom_section' due to the presence of a non-trivial destructor}}
2929
struct NonTrivialCtor {
3030
NonTrivialCtor();
3131
};
3232
extern const NonTrivialCtor non_trivial_ctor_custom_section;
33-
const NonTrivialCtor non_trivial_ctor_custom_section; // expected-warning {{`#pragma const_seg` for section ".my_const" will not apply to 'non_trivial_ctor_custom_section' due to the presence of a non-trivial constructor}}
33+
const NonTrivialCtor non_trivial_ctor_custom_section; // expected-warning {{`#pragma const_seg` for section '".my_const"' will not apply to 'non_trivial_ctor_custom_section' due to the presence of a non-trivial constructor}}
3434
#pragma data_seg(push, label, ".data2")
3535
extern const int b;
3636
const int b = 1;

clang/test/FixIt/fixit-bool.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -std=c++26 -Wint-in-bool-context %s 2>&1 | FileCheck %s
2+
3+
int x;
4+
bool t1 = x << x;
5+
// CHECK-LABEL: 4:13: warning: converting the result of '<<' to a boolean
6+
// CHECK: fix-it:"{{.*}}":{4:11-4:11}:"("
7+
// CHECK-NEXT: fix-it:"{{.*}}":{4:17-4:17}:") != 0"

0 commit comments

Comments
 (0)