Add a warning for misleading indentation similar to GCC's -Wmisleading-indentation
Details
Diff Detail
Event Timeline
clang/include/clang/Basic/DiagnosticGroups.td | ||
---|---|---|
830 | Move it to All/Most GCC enables this with -Wall |
Small nits.
Generally, I am fine with this. Aaron?
clang/include/clang/Basic/DiagnosticGroups.td | ||
---|---|---|
866 | Still typo here |
Thanks.
But please wait for Aaron/Nico.
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1198 | SourceManager &SM clang-format this diff? |
Thank you for working on this, it's very useful functionality!
clang/include/clang/Basic/DiagnosticGroups.td | ||
---|---|---|
866 | Why is this in Most? Shouldn't it be in All, like GCC? | |
clang/include/clang/Basic/DiagnosticParseKinds.td | ||
65 | How about: misleading indentation; %select{statement|declaration}0 is not part of the previous '%select{if|else|for|while}1'? Right now, you only seem to be covering statements, but I presume you want to also handle cases like: if (foo) bar(); int baz; This also fixes a typo with "misleading" and handles localization better by using a selection for the "previous" construct. | |
clang/test/SemaCXX/warn-misleanding-indentation.cpp | ||
1 ↗ | (On Diff #230788) | The test case file name has a typo: misleanding -> misleading You should also add a run line that tests this in C mode, because the parsing rules can be subtly different in C and C++. |
8 ↗ | (On Diff #230788) | I'd like to see a test case where the misleading indentation is not harmful, because we shouldn't diagnose that. e.g., if (foo) bar(); ; // this is not really a misleading indentation (-Wextra-semi-stmt covers it for style) I'd also like to see a test where the indentation isn't on a statement but is still misleading. e.g., if (foo) bar(); int x; // This should diagnose if (foo) bar(); typedef int frobble; // This should diagnose if (foo) bar(); [[]]; // No idea how I feel about this diagnosing or not We should have some test cases for degenerate indentation, e.g., if (foo) bar(); // GCC doesn't think this is misleading, we shouldn't either baz(); // This is not misleading indentation And some tests for other forms of the constructs, like if constexpr (whatever) and for (foo : bar) I won't ask for a test case involving zero-width space characters. ;-) |
int bar(void); int* foo(int* dst) { if (dst) return dst + \ bar(); return nullptr; }
This code is kinda weird, but please add it as a test (be sure we do not warn here).
Another case (but I am fine if you decide to not implement it yet, not blocking request)
void bar(void); void foo(int* dst) { if (dst) return bar(); }
User forgot to write ; behind return.
clang/test/SemaCXX/warn-misleading-indentation.cpp | ||
---|---|---|
19 | Please use the full diagnostic text unless the identical full text has already been verified on the same kind of construct previously (same goes for most of the diagnostics here). We only cut out repetitive bits of frequent diagnostics, generally speaking, but we want the full diagnostics to be checked whenever it's a unique diagnostic. | |
60–72 | A few more test cases: // No diagnostics from GCC on this void f0(int i) { if (1) i = 2; else if (i == 3) i = 4; i = 5; } // Or this #define TEST i = 5 void f0(int i) { if (1) i = 2; else i = 5; TEST; } void f0(int i) { if (1) i = 2; else if (i == 3) i = 4; i = 5; // This one is diagnosed } void f0(int i) { if (1) i = 2; else if (i == 3) {i = 4;} i = 5; // As is this one } |
void bar(void); void foo(int* dst) { if (dst) return bar(); }
Well, for this case (void function) we could warn. But okay to ignore it for now.
yeah i saw. this version of the patch builds all llvm subproject(not tested on llgo) without warnings. there is only one case in llvm's code in which this warning is more agressive thant GCC's.
if (1) i = 0; // comment i = 1; // clang + this patch warns through the comment wereas gcc doesn't.
i don't believe a warning in this case is too bad.
Please land code fixes as NFC patch and then try to reland this patch.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
2532 ↗ | (On Diff #231534) | Please land as separated NFCI commit. |
I just found out that Parser::isCXXDeclarationStatement does more then just disambiguation it can emit diagnostics. which can cause error on correct code. so we can't use it in this context to disambiguate.
so it is not possible to easily disambiguate between statements and declaration. i changed the warning message to reflect that but the message isn't so good now. gcc just calls everything a statement with is perhaps a good solution.
clang/include/clang/Parse/Parser.h | ||
---|---|---|
2269 | Extra change? Your code does not use isCXXDeclarationStatement. |
there is only one case in llvm's code in which this warning is more agressive thant GCC's.
I think that's okay. If after-commit feedback shows this is bad, we can always tune this diagnostic more and do not warn in that case.
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1239 | I dont think we need this. Tests will fail anyway if someone removes check() calls. | |
1341 | What is wrong with code you used some rev ago? if (usable) check(); Now you uselessly instantiate MIChecker since if Usable = false, check is not called. I like the older code more... If (usable) checkForMisleadingIndention(...) Was good and accepted. |
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1341 | previous patch gave up on else if because we can't know wether there are braces. this revision can produce correct diagnostics on else if |
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1341 | the MisleadingIndentationChecker gathers information during its construction this allows having more context and removes many false positives. but I can bring back |
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1336–1337 | This looks incorrect to me. Consider a case like: if (0) { } else [[gsl::suppress("foo")]] if (1) { } I'm a little uneasy calling anything but ParseStatement() here as that is what needs to be parsed at this stage. |
LGTM with some minor nits.
clang/lib/Parse/ParseStmt.cpp | ||
---|---|---|
1219 | Spurious whitespace, but it would be useful to add a newline above the Check() declaration for visual separation. | |
1239 | This can be hoisted (negated) into the preceding if statement. | |
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
2532 ↗ | (On Diff #231534) | Yeah, these changes seem unrelated to the patch. |
As an FYI, this appears to cause several false positive warnings with the Linux kernel:
../drivers/video/fbdev/core/fbmem.c:665:3: warning: misleading indentation; statement is not part of the previous 'else' [-Wmisleading-indentation] if (fb_logo.depth > 4 && depth > 4) { ^ ../drivers/video/fbdev/core/fbmem.c:661:2: note: previous statement is here else ^ ../drivers/video/fbdev/core/fbmem.c:1075:3: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] return ret; ^ ../drivers/video/fbdev/core/fbmem.c:1072:2: note: previous statement is here if (!ret) ^ 2 warnings generated.
Corresponding to:
https://elixir.bootlin.com/linux/v5.4.1/source/drivers/video/fbdev/core/fbmem.c#L665
and
https://elixir.bootlin.com/linux/v5.4.1/source/drivers/video/fbdev/core/fbmem.c#L1072
fbmem.c case reduced:
Looks like kernel uses tabs here
So we have BinaryOperator <line:5:3, col:9>
If we use spaces: BinaryOperator <line:5:9, col:15> 'int' '='
clang/test/Parser/warn-misleading-indentation.cpp | ||
---|---|---|
209 ↗ | (On Diff #231962) | Please add newline. |
i did a few test on the linux kernel on prior version of this patchs and the mix of spaces and tabs caused some false positives.
for the mix of space and tabs. gcc has a -ftabstop=X to specify how large tabs should be counted as.
clang has it as well i am going to check that it is taken in account.
i will investigate
the warning was not affected by -ftabstop. i made a patch that fix this.
https://reviews.llvm.org/D71037
and I agree that mixing tabs and space is a horrible idea for many reasons.
IMO that is misleading indentation. A single space followed by a tab? WTF
I guess I will take a look over all of the warnings that cropped up and see if this is the case for all of them. If it is, I will just send patches to fix those (with the justification of the warning and the fact that it would be a checkpatch warning), rather than adding -ftabstop to KBUILD_CFLAGS.
$ ./scripts/checkpatch.pl --terse --types LEADING_SPACE -f drivers/video/fbdev/core/fbmem.c drivers/video/fbdev/core/fbmem.c:665: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:666: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:667: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:668: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:669: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:670: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:671: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:672: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:673: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:674: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:675: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:676: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:677: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:678: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:1063: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:1064: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:1070: WARNING: please, no spaces at the start of a line drivers/video/fbdev/core/fbmem.c:1075: WARNING: please, no spaces at the start of a line
Hey, cool! I gave this a shot myself many years ago at http://llvm.org/bugs/show_bug.cgi?id=18938 That bug has a list of interesting test cases I had collected back then. Maybe you want to check and see how this does on those cases?
i have not tested the performance impact. but i tried to do the heavier checks last to minimize the impact.
i added your tests file to the improvement commit.
As a follow up to my previous post, I have sent patches to clean up all of the warnings that I see in the Linux kernel. However, I found one that I do believe is a false positive:
../drivers/staging/uwb/allocator.c:353:3: warning: misleading indentation; statement is not part of the previous 'else' [-Wmisleading-indentation] alloc_found: ^ ../drivers/staging/uwb/allocator.c:350:2: note: previous statement is here else ^ 1 warning generated.
Corresponding to https://github.com/torvalds/linux/blob/2187f215ebaac73ddbd814696d7c7fa34f0c3de0/drivers/staging/uwb/allocator.c#L346-L353.
Simplified:
$ cat test.c int a(int b, int c) { if (b) goto label; if (c) return 0; label: return 1; } $ clang -Wmisleading-indentation -o /dev/null -c test.c test.c:8:3: warning: misleading indentation; statement is not part of the previous 'if' [-Wmisleading-indentation] label: ^ test.c:5:2: note: previous statement is here if (c) ^ 1 warning generated.
goto labels are unaffected by indentation so there should be no warning here. While I think that the labels should be unindented for style, the driver is marked as obsolete and is scheduled to be deleted so I am not sure such a patch would be welcomed.
(re-ping; I think this false positive for goto label case is important to be fixed before 10 release)
@Tyker Hi, I noticed a case that isn't caught by -Wmisleading-indentation. In code that uses two space indents, there's a corner case that isn't caught when the preceding if uses curly braces. I've noticed a couple instances of this in lldb.
For example:
if (cond) { then1(); then2(); // ... } else else1(); else2();
The else2(); statement doesn't trigger the warning.
It seems that the logic is to use the column of the else keyword, not the column of the right brace. When using a four space indent (any indent != 2), the warning is emitted:
} else else1(); else2();
@Tyker in case you didn't see my previous message, I'm curious if you'd be willing to take a look at the bug. Thanks!
Move it to All/Most
GCC enables this with -Wall