This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy][analyzer][WIP] Clang-tidy reverse integration into Static Analyzer.
Needs ReviewPublic

Authored by NoQ on Jan 25 2021, 3:19 PM.

Details

Summary

This patch allows building a clang binary that has clang-tidy checks embedded into the static analyzer. I discussed these plans briefly in https://lists.llvm.org/pipermail/cfe-dev/2020-October/067002.html.

WIP because i haven't handled any build-time settings yet. This new facility will be under a CMake flag and most likely off by default (unless everybody suddenly loves this option, but even then, I still have to turn it into an option). I'll make sure to implement (and hopefully test) a reasonable behavior for all various combinations of CMake flags (eg., clang-tidy enabled/disabled, static analyzer enabled/disabled, static-analyzer-into-clang-tidy integration enabled/disabled, etc.).

This patch introduces a frontend flag -analyzer-tidy-checker=... that accepts clang-tidy checker strings (eg., -analyzer-tidy-checker=bugprone-*,-cert-*,cert-env33-c). As long as at least one such flag is supplied, ClangTidyContext is instantiated with the given checker list and a clang-tidy AST consumer gets multiplexed with AnalysisConsumer so that to run clang-tidy checkers. Diagnostics from these checks are pumped into a PathDiagnosticConverterDiagnosticConsumer (D94476) and displayed similarly to static analyzer diagnostics, eg. as HTML or Plist reports:

This patch suggests enabling a few clang-tidy checks by default in Clang Driver (under the --analyze flag, obviously, and the new reverse integration should be enabled first). Requirements for enabling a clang-tidy check by default would be similar to requirements of enabling a static analyzer check by default (finds real bugs by design, understandable diagnostics, etc.). Additionally, on-by-default clang-tidy checks should have static analyzer bug categories assigned to them (eg., "Logic error", "Memory error", etc.) in the diagnostic converter (a relatively convenient way to do so is provided).

If bug categories aren't assigned, the default bug category for clang-tidy check x-y-z is Clang-Tidy [x] and the default bug type is Clang-Tidy [x-y-z]. This looks pretty ok in scan-build's index.html:

That said, we should probably provide an option to disable bug category conversion and use it whenever an arbitrary clang-tidy config is expected. As usual, we're optimizing for a reasonable default rather than for configurability.

I've considered a different approach to enabling/disabling clang-tidy checks (but didn't implement it in this patch) that's more static analyzer-like: -analyzer-checker tidy.bugprone.assert-side-effect etc. Here we treat x in x-y-z as a static analyzer package, hence the dot. This approach is less flexible because it doesn't allow arbitrary wildcards, so we still have to have the current approach but we should probably implement both. One thing to think about here would be to figure out whether each individual off-by-default clang-tidy check is treated as "alpha" ("we're not supporting it, don't use it") or opt-in ("we're supporting it but it's still of by default"). Probably we could additionally separate them into alpha.tidy.* and optin.tidy.*.

Clang-tidy check-specific options are not implemented yet. Again, we could provide a separate frontend flag, or we could interface them through AnalyzerOptions like this: -analyzer-config tidy.bugprone.assert-side-effect:AssertMacros=assert,Assert,ASSERT. And, again, we should probably do both.

Diff Detail

Event Timeline

NoQ created this revision.Jan 25 2021, 3:19 PM
NoQ requested review of this revision.Jan 25 2021, 3:19 PM
NoQ edited the summary of this revision. (Show Details)Jan 25 2021, 3:21 PM
alexfh added inline comments.Jan 28 2021, 6:50 PM
clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
54–60

Isn't this a layering violation, since clang-tidy depends on clangStaticAnalyzerCore and clangStaticAnalyzerFrontend?

NoQ added inline comments.Jan 28 2021, 11:02 PM
clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
54–60

Yes, absolutely.

That said, the only purpose of clang-tidy's dependency on libStaticAnalyzer* is integration of static analyzer into clang tidy which is definitely not something we want to enable when we're baking clang-tidy back into clang. It never makes sense to run static analyzer through clang-tidy integration into static analyzer.

So ideally these two dependencies are temporally separated. I could make these dependencies mutually exclusive by making the upcoming option of baking clang-tidy into clang explicitly incompatible with CLANG_TIDY_ENABLE_STATIC_ANALYZER.

But if we want to support building both clang-tidy with static analyzer and static analyzer with clang-tidy from the same sources into the same build directory, that'll probably involve either building two variants of clang-tidy (one with static analyzer for standalone clang-tidy binary and one without to use inside clang binary only) or two variants of static analyzer (one with clang-tidy for the clang binary and one without to use inside clang-tidy binary only).

Do you have any preference on how should i untangle this?

alexfh set the repository for this revision to rG LLVM Github Monorepo.Jan 29 2021, 5:59 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 29 2021, 5:59 AM

Thank you for working on this, I think it's fantastic effort!

I'll make sure to implement (and hopefully test) a reasonable behavior for all various combinations of CMake flags (eg., clang-tidy enabled/disabled, static analyzer enabled/disabled, static-analyzer-into-clang-tidy integration enabled/disabled, etc.).

Will we be able to stand up a buildbot to explicitly test this configuration?

This patch introduces a frontend flag -analyzer-tidy-checker=...

FWIW, the usual clang-tidy nomenclature is to call these "checks" rather than "checkers", so it might make sense to expose -analyzer-tidy-checks=... to be analogous to clang-tidy -checks=...

As long as at least one such flag is supplied, ClangTidyContext is instantiated with the given checker list and a clang-tidy AST consumer gets multiplexed with AnalysisConsumer so that to run clang-tidy checkers.

clang-tidy currently runs the clang static analyzer, so I worry a little bit about duplicated diagnostics. I assume that we'll disable running the static analyzer checks a second time through clang-tidy?

alexfh added a comment.Feb 3 2021, 3:37 PM

Artem, could you set the repository to rG LLVM Github Monorepo when uploading patches as mentioned in https://llvm.org/docs/Phabricator.html#requesting-a-review-via-the-web-interface ? This way you'll allow pre-merge checks to run.

This patch introduces a frontend flag -analyzer-tidy-checker=...

FWIW, the usual clang-tidy nomenclature is to call these "checks" rather than "checkers", so it might make sense to expose -analyzer-tidy-checks=... to be analogous to clang-tidy -checks=...

I vaguely remember discussing (most probably, with @gribozavr2) this difference and coming to a conclusion that there's no strong reason to keep the nomenclature difference between the static analyzer and clang-tidy. This didn't go much farther beyond creating the clang-tools-extra/test/clang-tidy/checkers/ directory, but we could revisit this, since we're looking into more interaction between the tools.

clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
54–60

I hope we could break the dependency cycle without "flipping" the direction of dependencies based on a CMake switch. This way it would be easier to replicate the dependency structure in other BUILD systems.

Now the dependencies look very roughly like this:

clangStaticAnalyzerCore:
clangStaticAnalyzerCheckers: clangStaticAnalyzerCore
clangStaticAnalyzerFrontend: clangStaticAnalyzerCheckers clangStaticAnalyzerCore
clangFrontendTool: clangStaticAnalyzerFrontend

clangTidy: clangStaticAnalyzerCore clangStaticAnalyzerFrontend
clang-tidy check libraries gathered under $ALL_CLANG_TIDY_CHECKS
clangTidyMain: clangTidy
clang-tidy: clangTidyMain clangTidy $ALL_CLANG_TIDY_CHECKS

Untangled version could look like this:

clangStaticAnalyzerCore:
clangStaticAnalyzerCheckers: clangStaticAnalyzerCore
clangStaticAnalyzerFrontend: clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangTidyInterface
clangStaticAnalyzerFrontendWithClangTidy: clangTidyImpl $ALL_CLANG_TIDY_CHECKS
clangFrontendTool: clangStaticAnalyzerFrontendWithClangTidy

clangTidyInterface: (with ClangTidy.h, ClangTidyOptions.h, ClangTidyDiagnosticConsumer.h and ClangTidyProfiling.h, for example)
clangTidyImpl: clangTidyInterface clangStaticAnalyzerCore clangStaticAnalyzerFrontend
clang-tidy check libraries gathered under $ALL_CLANG_TIDY_CHECKS
clangTidyMain: clangTidyImpl
clang-tidy: clangTidyMain clangTidyImpl $ALL_CLANG_TIDY_CHECKS

What do you think?

jansvoboda11 added inline comments.
clang/lib/Frontend/CompilerInvocation.cpp
640

Can you please use the option marshalling infrastructure instead? https://clang.llvm.org/docs/InternalsManual.html#adding-new-command-line-option

Herald added a project: Restricted Project. · View Herald TranscriptJun 3 2022, 1:16 PM