Re-apply r160319 "Don't crash when emitting fixits following Unicode chars"
This time, make sure we don't try to print fixits with newline characters, since they don't have a valid column width, and they don't look good anyway. PR13417 (and originally <rdar://problem/11877454>) llvm-svn: 160561
This commit is contained in:
parent
8c6c8a98b6
commit
9dd82c1d94
|
@ -230,6 +230,9 @@ print something like:
|
|||
|
||||
<p>When this is disabled, Clang will print "test.c:28: warning..." with no
|
||||
column number.</p>
|
||||
|
||||
<p>The printed column numbers count bytes from the beginning of the line; take
|
||||
care if your source contains multibyte characters.</p>
|
||||
</dd>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
@ -396,6 +399,9 @@ exprs.c:47:15:{47:8-47:14}{47:17-47:24}: error: invalid operands to binary expre
|
|||
</pre>
|
||||
|
||||
<p>The {}'s are generated by -fdiagnostics-print-source-range-info.</p>
|
||||
|
||||
<p>The printed column numbers count bytes from the beginning of the line; take
|
||||
care if your source contains multibyte characters.</p>
|
||||
</dd>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
@ -416,6 +422,9 @@ respectively). Both the file name and the insertion string escape backslash (as
|
|||
"\\"), tabs (as "\t"), newlines (as "\n"), double
|
||||
quotes(as "\"") and non-printable characters (as octal
|
||||
"\xxx").</p>
|
||||
|
||||
<p>The printed column numbers count bytes from the beginning of the line; take
|
||||
care if your source contains multibyte characters.</p>
|
||||
</dd>
|
||||
|
||||
<dt id="opt_fno-elide-type">
|
||||
|
|
|
@ -1124,23 +1124,28 @@ std::string TextDiagnostic::buildFixItInsertionLine(
|
|||
std::string FixItInsertionLine;
|
||||
if (Hints.empty() || !DiagOpts.ShowFixits)
|
||||
return FixItInsertionLine;
|
||||
unsigned PrevHintEnd = 0;
|
||||
unsigned PrevHintEndCol = 0;
|
||||
|
||||
for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end();
|
||||
I != E; ++I) {
|
||||
if (!I->CodeToInsert.empty()) {
|
||||
// We have an insertion hint. Determine whether the inserted
|
||||
// code is on the same line as the caret.
|
||||
// code contains no newlines and is on the same line as the caret.
|
||||
std::pair<FileID, unsigned> HintLocInfo
|
||||
= SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin());
|
||||
if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second)) {
|
||||
if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) &&
|
||||
StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) {
|
||||
// Insert the new code into the line just below the code
|
||||
// that the user wrote.
|
||||
unsigned HintColNo
|
||||
// Note: When modifying this function, be very careful about what is a
|
||||
// "column" (printed width, platform-dependent) and what is a
|
||||
// "byte offset" (SourceManager "column").
|
||||
unsigned HintByteOffset
|
||||
= SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1;
|
||||
// hint must start inside the source or right at the end
|
||||
assert(HintColNo<static_cast<unsigned>(map.bytes())+1);
|
||||
HintColNo = map.byteToColumn(HintColNo);
|
||||
|
||||
// The hint must start inside the source or right at the end
|
||||
assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1);
|
||||
unsigned HintCol = map.byteToColumn(HintByteOffset);
|
||||
|
||||
// If we inserted a long previous hint, push this one forwards, and add
|
||||
// an extra space to show that this is not part of the previous
|
||||
|
@ -1149,32 +1154,27 @@ std::string TextDiagnostic::buildFixItInsertionLine(
|
|||
//
|
||||
// Note that if this hint is located immediately after the previous
|
||||
// hint, no space will be added, since the location is more important.
|
||||
if (HintColNo < PrevHintEnd)
|
||||
HintColNo = PrevHintEnd + 1;
|
||||
if (HintCol < PrevHintEndCol)
|
||||
HintCol = PrevHintEndCol + 1;
|
||||
|
||||
// FIXME: if the fixit includes tabs or other characters that do not
|
||||
// take up a single column per byte when displayed then
|
||||
// I->CodeToInsert.size() is not a column number and we're mixing
|
||||
// units (columns + bytes). We should get printable versions
|
||||
// of each fixit before using them.
|
||||
unsigned LastColumnModified
|
||||
= HintColNo + I->CodeToInsert.size();
|
||||
|
||||
if (LastColumnModified <= static_cast<unsigned>(map.bytes())) {
|
||||
// If we're right in the middle of a multibyte character skip to
|
||||
// the end of it.
|
||||
while (map.byteToColumn(LastColumnModified) == -1)
|
||||
++LastColumnModified;
|
||||
LastColumnModified = map.byteToColumn(LastColumnModified);
|
||||
}
|
||||
// FIXME: This function handles multibyte characters in the source, but
|
||||
// not in the fixits. This assertion is intended to catch unintended
|
||||
// use of multibyte characters in fixits. If we decide to do this, we'll
|
||||
// have to track separate byte widths for the source and fixit lines.
|
||||
assert((size_t)llvm::sys::locale::columnWidth(I->CodeToInsert) ==
|
||||
I->CodeToInsert.size());
|
||||
|
||||
// This relies on one byte per column in our fixit hints.
|
||||
// This should NOT use HintByteOffset, because the source might have
|
||||
// Unicode characters in earlier columns.
|
||||
unsigned LastColumnModified = HintCol + I->CodeToInsert.size();
|
||||
if (LastColumnModified > FixItInsertionLine.size())
|
||||
FixItInsertionLine.resize(LastColumnModified, ' ');
|
||||
assert(HintColNo+I->CodeToInsert.size() <= FixItInsertionLine.size());
|
||||
std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
|
||||
FixItInsertionLine.begin() + HintColNo);
|
||||
|
||||
PrevHintEnd = LastColumnModified;
|
||||
std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(),
|
||||
FixItInsertionLine.begin() + HintCol);
|
||||
|
||||
PrevHintEndCol = LastColumnModified;
|
||||
} else {
|
||||
FixItInsertionLine.clear();
|
||||
break;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only %s 2>&1 | FileCheck -strict-whitespace %s
|
||||
// PR13312
|
||||
// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck -check-prefix=CHECK-MACHINE %s
|
||||
|
||||
struct Foo {
|
||||
int bar;
|
||||
};
|
||||
|
||||
// PR13312
|
||||
void test1() {
|
||||
struct Foo foo;
|
||||
(&foo)☃>bar = 42;
|
||||
|
@ -12,4 +13,21 @@ void test1() {
|
|||
// Make sure we emit the fixit right in front of the snowman.
|
||||
// CHECK: {{^ \^}}
|
||||
// CHECK: {{^ ;}}
|
||||
|
||||
// CHECK-MACHINE: fix-it:"{{.*}}fixit-unicode.c":{11:9-11:9}:";"
|
||||
}
|
||||
|
||||
|
||||
int printf(const char *, ...);
|
||||
void test2() {
|
||||
printf("∆: %d", 1L);
|
||||
// CHECK: warning: format specifies type 'int' but the argument has type 'long'
|
||||
// Don't crash emitting a fixit after the delta.
|
||||
// CHECK: printf("
|
||||
// CHECK: : %d", 1L);
|
||||
// Unfortunately, we can't actually check the location of the printed fixit,
|
||||
// because different systems will render the delta differently (either as a
|
||||
// character, or as <U+2206>.) The fixit should line up with the %d regardless.
|
||||
|
||||
// CHECK-MACHINE: fix-it:"{{.*}}fixit-unicode.c":{23:16-23:18}:"%ld"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue