tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

adding-a-check.rst (4815B)


      1 .. _add_a_check:
      2 
      3 Adding a check
      4 ==============
      5 
      6 After you've completed a matcher using clang-query, it's time to take it to the next step and turn it into C++ and run it on the whole m-c codebase and see what happens.
      7 
      8 Clang plugins live in `build/clang-plugin <https://searchfox.org/mozilla-central/source/build/clang-plugin>`_ and here we'll cover what is needed to add one. To see how the most recent check was added, you can look at the log for `Checks.inc <https://hg.mozilla.org/mozilla-central/log/tip/build/clang-plugin/Checks.inc>`_ which is one of the necessary files to edit.  That's also what we'll be covering next.
      9 
     10 Boilerplate Steps to Add a New Check
     11 ------------------------------------
     12 
     13 First pick a name. Pick something that makes sense without punctuation, in no more than 8 words or so.  For this example we'll call it "Missing Else In Enum Comparisons".
     14 
     15 #. Add it alphabetically in build/clang-plugin/Checks.inc, ChecksIncludes.inc, and moz.build
     16 #. ``cd build/clang-plugin && touch MissingElseInEnumComparisons.h MissingElseInEnumComparisons.cpp``
     17 #. Copy the contents of an existing, simple .h file (e.g. `build/clang-plugin/ScopeChecker.h <https://searchfox.org/mozilla-central/source/build/clang-plugin/ScopeChecker.h>`_) and edit the class name and header guards.
     18 #. Create the following boilerplate for your implementation:
     19 
     20 ::
     21 
     22  /* This Source Code Form is subject to the terms of the Mozilla Public
     23   * License, v. 2.0. If a copy of the MPL was not distributed with this
     24   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     25 
     26  #include "MissingElseInEnumComparisons.h"
     27  #include "CustomMatchers.h"
     28 
     29  void MissingElseInEnumComparisons::registerMatchers(MatchFinder *AstMatcher) {
     30 
     31  }
     32 
     33  void MissingElseInEnumComparisons::check(const MatchFinder::MatchResult &Result) {
     34 
     35  }
     36 
     37 
     38 Converting your matcher to C++
     39 ------------------------------
     40 With the boilerplate out of the way, now we can focus on converting the matcher over to C++.  Once it's in C++ you'll also be able to take advantage of techniques that will make your matcher easier to read and understand.
     41 
     42 The gist of converting your matcher is to take the following pseudo-code and paste your entire matcher in where 'foo' is; keeping the `.bind("node")` there:
     43 
     44 ::
     45 
     46  AstMatcher->addMatcher(
     47      traverse(TK_IgnoreUnlessSpelledInSource,
     48          foo
     49          .bind("node")),
     50      this);
     51 
     52 
     53 It honest to god is usually that easy.  Here's a working example where I pasted in straight from Compiler Explorer:
     54 
     55 ::
     56 
     57  AstMatcher->addMatcher(
     58    traverse(TK_IgnoreUnlessSpelledInSource,
     59      ifStmt(allOf(
     60              has(
     61                   binaryOperator(
     62                       has(
     63                           declRefExpr(hasType(enumDecl().bind("enum")))
     64                       )
     65                   )
     66               ),
     67               hasElse(
     68                   ifStmt(allOf(
     69                      unless(hasElse(anything())),
     70                      has(
     71                           binaryOperator(
     72                               has(
     73                                   declRefExpr(hasType(enumDecl()))
     74                               )
     75                           )
     76                       )
     77                   ))
     78              )
     79           ))
     80          .bind("node")),
     81      this);
     82 
     83 
     84 
     85 If for some reason you're not using the ``IgnoreUnlessSpelledInSource`` Traversal Mode, remove the call to traverse and the corresponding closing paren.  (Also, if you're comparing this code to existing source code, know that because this traversal mode is a new clang feature, most historical clang checks do not use it.)
     86 
     87 Wiring up Warnings and Errors
     88 -----------------------------
     89 To get started with a some simple output, just take the boilerplate warning here and stick it in:
     90 
     91 ::
     92 
     93  const auto *MatchedDecl = Result.Nodes.getNodeAs<IfStmt>("node");
     94    diag(MatchedDecl->getIfLoc(),
     95        "Enum comparisons in an if/else if block without a trailing else.",
     96        DiagnosticIDs::Warning);
     97 
     98 
     99 You'll need to edit two things:
    100 
    101 #. Make sure "node" matches whatever you put in `.bind()` up above.
    102 #. ``getNodeAs<IfStmt>`` needs to be changed to whatever type of element "node" is. Above, we bind "node" to an ifStmt so that's what we need to cast it to. Doing this step incorrectly will cause clang to crash during compilation as if there was some internal compiler error.
    103 
    104 
    105 Running it on Central
    106 ----------------------
    107 After this, the next thing to do is to add ``ac_add_options --enable-clang-plugin`` to your .mozconfig and do a build. Your plugin will be automatically compiled and used across the entire codebase.  I suggest using ``./mach build | tee output.txt`` and then ``grep "Enum comparisons" output.txt | cut -d " " -f 3- | sort | uniq``.  (The ``cut`` is there to get rid of the timestamp in the line.)