android_webview
apps
ash
base
build
build_overrides
buildtools
cc
chrome
chromecast
chromeos
clank
codelabs
components
content
crypto
dbus
device
docs
accessibility
autofill
chromeos
design
enterprise
experiments
fuchsia
gpu
graphics
images
infra
intl
ios
linux
login
mac
media
memory
memory-infra
patterns
privacy
privacy_budget
process
security
speed
speed_metrics
standards
telemetry_extension
testing
transcripts
ui
updater
webapps
website
webui
workflow
DIR_METADATA
OWNERS
README.md
accessibility.md
ad_tagging.md
adding_to_third_party.md
android_accessing_cpp_enums_in_java.md
android_accessing_cpp_features_in_java.md
android_accessing_cpp_switches_in_java.md
android_build_instructions.md
android_cast_build_instructions.md
android_debugging_instructions.md
android_dynamic_feature_modules.md
android_emulator.md
android_isolated_splits.md
android_jni_ownership_best_practices.md
android_logging.md
android_native_libraries.md
android_studio.md
angle_in_chromium.md
api_keys.md
asan.md
atom.md
benchmark_performance_regressions.md
bfcache.md
bitmap_pipeline.md
branch_gardener.md
building_old_revisions.md
callback.md
ccache_mac.md
chrome_browser_design_principles.md
chrome_os_logging.md
chrome_settings.md
chromedriver_status.md
chromeos_build_instructions.md
chromeos_glossary.md
chromium_browser_vs_google_chrome.md
cipd_and_3pp.md
cl_respect.md
cl_tips.md
clang.md
clang_code_coverage_wrapper.md
clang_format.md
clang_gardening.md
clang_sheriffing.md
clang_static_analyzer.md
clang_tidy.md
clang_tool_refactoring.md
clangd.md
clion.md
closure_compilation.md
cocoa_tips_and_tricks.md
code_review_owners.md
code_reviews.md
commit_checklist.md
component_build.md
configuration.md
contributing.md
cq_fault_attribution.md
cr_respect.md
cr_user_manual.md
cross_platform_ui.md
cygwin_dll_remapping_failure.md
dangling_ptr.md
dangling_ptr_guide.md
dbus_mojo_connection_service.md
debugging_with_crash_keys.md
dependencies.md
deterministic_builds.md
disassemble_code.md
documentation_best_practices.md
documentation_guidelines.md
early-hints.md
eclipse.md
emacs.md
erc_irc.md
flag_expiry.md
flag_guarding_guidelines.md
flag_ownership.md
frame_trees.md
gardener.md
gcs_dependencies.md
gdbinit.md
get_the_code.md
git_cookbook.md
git_submodules.md
git_tips.md
google_chrome_branded_builds.md
google_play_services.md
graphical_debugging_aid_chromium_views.md
gwp_asan.md
history_manipulation_intervention.md
how_cc_works.md
how_to_add_your_feature_flag.md
how_to_extend_web_test_framework.md
idn.md
initialize_blink_features.md
inlined_stack_traces.md
installation_at_vmware.md
ios_build_instructions.md
ios_infra.md
ios_voiceover.md
kiosk_mode.md
life_of_a_frame.md
lldbinit.md
mac_arm64.md
mac_build_instructions.md
mac_lld.md
modifying_session_history_serialization.md
modules.md
mojo_and_services.md
mojo_ipc_conversion.md
mojo_testing.md
native_relocations.md
navbar.md
navigation-request-navigation-state.gv
navigation-request-navigation-state.png
navigation.md
navigation_concepts.md
network_traffic_annotations.md
no_sources_assignment_filter.md
orderfile.md
origin_trials_integration.md
ozone_overview.md
parsing_test_results.md
pgo.md
piranha_plant.md
process_model_and_site_isolation.md
profiling.md
profiling_content_shell_on_android.md
proxy_auto_config.md
qtcreator.md
release_branch_guidance.md
render-frame-host-lifecycle-state.gv
render-frame-host-lifecycle-state.png
render_document.md
rust-unsafe.md
rust.md
seccomp_sandbox_crash_dumping.md
servicification.md
session_history.md
sheriff.md
shutdown.md
special_case_urls.md
static_initializers.md
sublime_ide.md
system_hardening_features.md
tab_helpers.md
threading_and_tasks.md
threading_and_tasks_faq.md
threading_and_tasks_testing.md
toolchain_support.md
tour_of_luci_ui.md
tpm_quick_ref.md
translation_screenshots.md
unretained_dangling_ptr_guide.md
unsafe_buffers.md
updating_clang.md
updating_clang_format_binaries.md
use_counter_wiki.md
useful_urls.md
user_data_dir.md
user_data_storage.md
user_handle_mapping.md
vanilla_msysgit_workflow.md
vscode.md
vscode_python.md
webview_policies.md
win_cross.md
win_order_files.md
windows_build_instructions.md
windows_native_window_occlusion_tracking.md
windows_pwa_integration.md
windows_shortcut_and_taskbar_handling.md
windows_split_dll.md
windows_virtual_desktop_handling.md
wmax_tokens.md
working_remotely_with_android.md
writing_clang_plugins.md
extensions
fuchsia_web
gin
google_apis
gpu
headless
infra
internal
ios
ios_internal
ipc
media
mojo
native_client
native_client_sdk
net
pdf
ppapi
printing
remoting
rlz
sandbox
services
signing_keys
skia
sql
storage
styleguide
testing
third_party
tools
ui
url
v8
webkit
.clang-format
.clang-tidy
.clangd
.git-blame-ignore-revs
.gitallowed
.gitattributes
.gitignore
.gitmodules
.gn
.mailmap
.rustfmt.toml
.vpython3
.yapfignore
ATL_OWNERS
AUTHORS
BUILD.gn
CODE_OF_CONDUCT.md
CPPLINT.cfg
CRYPTO_OWNERS
DEPS
DIR_METADATA
LICENSE
LICENSE.chromium_os
OWNERS
PRESUBMIT.py
PRESUBMIT_test.py
PRESUBMIT_test_mocks.py
README.md
WATCHLISTS
codereview.settings

Since almost all Google domains support HTTPS, and it is a good practice to use HTTPS wherever possible, thus in this patch, I changed most HTTP links to https whose domains are known to support HTTPS well. Modifications are generated by running these commands in src/docs directory: sed -i 's/http:\/\/www.chromium.org/https:\/\/www.chromium.org/g' *.md sed -i 's/http:\/\/developer.android.com/https:\/\/developer.android.com/g' *.md sed -i 's/http:\/\/dev.chromium.org/https:\/\/dev.chromium.org/g' *.md sed -i 's/http:\/\/build.chromium.org/https:\/\/build.chromium.org/g' *.md sed -i 's/http:\/\/src.chromium.org/https:\/\/src.chromium.org/g' *.md sed -i 's/http:\/\/crbug.com/https:\/\/crbug.com/g' *.md sed -i 's/http:\/\/groups.google.com/https:\/\/groups.google.com/g' *.md sed -i 's/http:\/\/cs.chromium.org/https:\/\/cs.chromium.org/g' *.md sed -i 's/http:\/\/codereview.chromium.org/https:\/\/codereview.chromium.org/g' *.md BUG= Review-Url: https://codereview.chromium.org/2545363002 Cr-Commit-Position: refs/heads/master@{#436501}
161 lines
6.8 KiB
Markdown
161 lines
6.8 KiB
Markdown
# Don't write a clang plugin
|
||
|
||
[TOC]
|
||
|
||
Make sure you really want to write a clang plugin.
|
||
|
||
* The clang plugin api is not stable. If you write a plugin, _you_ are
|
||
responsible for making sure it's updated when we update clang.
|
||
* If you're adding a generally useful warning, it should be added to upstream
|
||
clang, not to a plugin.
|
||
* You should not use a clang plugin to do things that can be done in a
|
||
PRESUBMIT check (e.g. checking that the headers in a file are sorted).
|
||
|
||
Valid reasons for writing a plugin are for example:
|
||
|
||
* You want to add a chromium-specific error message.
|
||
* You want to write an automatic code rewriter.
|
||
|
||
In both cases, please inform
|
||
[clang@chromium.org](https://groups.google.com/a/chromium.org/group/clang/topics)
|
||
of your plans before you pursue them.
|
||
|
||
# Having said that
|
||
|
||
clang currently has minimal documentation on its plugin interface; it's mostly
|
||
doxygen annotations in the source. This is an attempt to be half map to the
|
||
header files/half tutorial.
|
||
|
||
# Building your plugin
|
||
|
||
## Just copy the clang build system
|
||
|
||
I suggest you make a new dir in `llvm/tools/clang/examples/` and copy the
|
||
Makefile from `PrintFunctionNames` there. This way, you'll just leverage the
|
||
existing clang build system. You can then build your plugin with
|
||
|
||
make -C llvm/tools/clang/examples/myplugin
|
||
|
||
See [Using plugins](clang.md) on how to use your plugin while building chromium
|
||
with clang.
|
||
|
||
## Use the interface in tools/clang/plugins/ChromeClassTester.h
|
||
|
||
Here's a canned interface that filters code, only passing class definitions in
|
||
non-blacklisted headers. The users of `ChromeClassTester` are good code to study
|
||
to see what you can do.
|
||
|
||
## Or if you're doing something really different, copy PrintFunctionNames.cpp
|
||
|
||
`PrintFunctionNames.cpp` is a plugin in the clang distribution. It is the Hello
|
||
World of plugins. As a most basic skeleton, it's a good starting point. Change
|
||
all the identifiers that start with `PrintFunction` to your desired name. Take
|
||
note of the final line:
|
||
|
||
```cpp
|
||
static FrontendPluginRegistry::Add<PrintFunctionNamesAction>
|
||
X("print-fns", "print function names");
|
||
```
|
||
|
||
This registers your `PluginASTAction` with a string plugin name that can be
|
||
invoked on the command line. Note that everything else is in an anonymous
|
||
namespace; all other symbols aren't exported.
|
||
|
||
Your `PluginASTAction` subclass exists just to build your `ASTConsumer`, which
|
||
receives declarations, sort of like a SAX parser.
|
||
|
||
## Your ASTConsumer
|
||
|
||
There is doxygen documentation on when each `ASTConsumer::Handle` method is
|
||
called in `llvm/tools/clang/include/clang/AST/ASTConsumer.h`. For this
|
||
tutorial, I'll assume you only want to look at type definitions (struct, class,
|
||
enum definitions), so we'll start with:
|
||
|
||
```cpp
|
||
class TagConsumer : public ASTConsumer {
|
||
public:
|
||
virtual void HandleTagDeclDefinition(TagDecl *D) {
|
||
}
|
||
};
|
||
```
|
||
|
||
The data type passed in is the `Decl`, which is a giant class hierarchy spanning
|
||
the following files:
|
||
|
||
* `llvm/tools/clang/include/clang/AST/DeclBase.h`: declares the `Decl` class,
|
||
along with some utility classes you won't use.
|
||
* `llvm/tools/clang/include/clang/AST/Decl.h`: declares subclasses of `Decl`,
|
||
for example, `FunctionDecl` (a function declaration), `TagDecl` (the base class for struct/class/enum/etc), `TypedefDecl`, etc.
|
||
* `llvm/tools/clang/include/clang/AST/DeclCXX.h`: C++ specific types.
|
||
You'll find most Decl subclasses dealing with templates here,
|
||
along with things like `UsingDirectiveDecl`, `CXXConstructorDecl`, etc.
|
||
|
||
The interface on these classes is massive; We'll only cover some of the basics,
|
||
but some basics about source location and errors.
|
||
|
||
## Emitting Errors
|
||
|
||
Lots of location information is stored in the `Decl` tree. Most `Decl`
|
||
subclasses have multiple methods that return a `SourceLocation`, but lets use
|
||
`TagDecl::getInnerLocStart()` as an example. (`SourceLocation` is defined in
|
||
`llvm/tools/clang/include/clang/Basic/SourceLocation.h`, for reference.)
|
||
|
||
Errors are emitted to the user through the `CompilerInstance`. You will probably want to pass the `CompilerInstance` object passed to `ASTAction::CreateASTConsumer` to your ASTConsumer subclass for reporting. You interact with the user through the `Diagnostic` object. You could report errors to the user like this:
|
||
|
||
```cpp
|
||
void emitWarning(CompilerInstance& instance, SourceLocation loc, const char* error) {
|
||
FullSourceLoc full(loc, instance.getSourceManager());
|
||
unsigned id = instance.getCustomDiagID(Diagnostic::Warning, error);
|
||
DiagnosticBuilder B = instance.getDiagnostics().Report(full, id);
|
||
}
|
||
```
|
||
|
||
(The above is the simplest error reporting. See
|
||
`llvm/tools/clang/include/clang/Basic/Diagnostic.h` for all the things you can
|
||
do, like `FixItHint`s if you want to get fancy!)
|
||
|
||
## Downcast early, Downcast often
|
||
|
||
The clang library will give you the most general types possible. For example
|
||
`TagDecl` has comparably minimal interface. The library is designed so you will
|
||
be downcasting all the time, and you won't use the standard `dynamic_cast<>()`
|
||
builtin to do it. Instead, you'll use llvm/clang's home built RTTI system:
|
||
|
||
```cpp
|
||
virtual void HandleTagDeclDefinition(TagDecl* tag) {
|
||
if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) {
|
||
// Do stuff with |record|.
|
||
}
|
||
}
|
||
```
|
||
|
||
## A (not at all exhaustive) list of things you can do with (CXX)RecordDecl
|
||
|
||
* Iterate across all constructors (`CXXRecordDecl::ctor_begin()`,
|
||
`CXXReocrdDecl::ctor_end()`)
|
||
* `CXXRecordDecl::isPOD()`: is this a Plain Old Datatype (a type that has no
|
||
construction or destruction semantics)?
|
||
* Check if certain properties of the class: `CXXRecordDecl::isAbstract()`,
|
||
`CXXRecordDecl::hasTrivialConstructor()`,
|
||
`CXXRecordDecl::hasTrivialDestructor()`, etc.
|
||
* Iterate across all fields/member variables (`RecordDecl::field_begin()`,
|
||
`RecordDecl::field_end()`)
|
||
* Iterate across all of the base classes of a record type
|
||
(`CXXRecordDecl::bases_begin()`, `CXXRecordDecl::bases_end()`)
|
||
* Get the simple string name `NamedDecl::getNameAsString()`. (This method is
|
||
deprecated, but the replacement assert()s on error conditions). (If you had
|
||
`struct One {}`, this method would return "One".)
|
||
|
||
## Modifying existing plugins
|
||
|
||
If you want to add additional checks to the existing plugins, be sure to add the
|
||
new diagnostic behind a flag (there are several examples of this in the plugins
|
||
already). The reason for this is that the plugin is bundled with clang, and the
|
||
new check will get deployed with the next clang roll. If your check fires, then
|
||
the next clang roll would now be blocked on cleaning up the whole codebase for
|
||
your check – and even if the check doesn't fire at the moment, maybe that
|
||
regresses until the next clang roll happens. If your new check is behind a flag,
|
||
then the clang roll can happen first, and you can add the flag to enable your
|
||
check after that, and then turn on the check everywhere once you know that the
|
||
codebase is clean.
|