[cfe-dev] How do I use Clang Static Analyzer custom checkers in standing alone tool, or invoke the Clang Static Analyzer from such a tool?

Konstantin Rebrov via cfe-dev cfe-dev at lists.llvm.org
Sat Jul 20 13:19:50 PDT 2019


Hello fellow software developers.

I have a question. I am working on a software development project. In this
project I am creating a standing alone refactoring tool which uses the
LLVM/Clang libraries. This software development project entails source to
source rewriting of C files, performing refactorings on them. These
refactorings include: removing unsupported functions, rewriting problematic
constructs, removal of global variables, turning dynamic memory allocations
of arrays into variable length stack arrays. I am using AST matchers for
this task. My code includes the following libraries:
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/Support/CommandLine.h"

Basically my refactoring tool is a C++ command line application. It takes a
list of *.c source files, uses the AST matchers and call back classes to
perform the refactorings on these files, and then save the modifications to
those same files. In order to make the structure of my project more clear,
I am uploading the main C++ source file. This file includes all the header
files for the call back classes which implement the refactorings.

refactoring_tool.cpp
RemovePointerMatchCallback.h

This refactoring tool runs on a large legacy code base of C files. For
performing the obvious refactorings I have been using the AST matchers. Now
I've exhausted their capabilities, meaning that I have completed
implementing the obvious refactorings using AST matchers. Now for
implementing the more advanced refactorings I need to use the Clang Static
Analyzer libraries. I have been studying how to create my own checkers for
the Clang Static Analyzer using this lecture:
2012 LLVM Developers’ Meeting: A. Zaks & J. Rose “Building a Checker in 24
hours”
<nabble_embed>https://www.youtube.com/watch?v=kdxlsP5QVPw</nabble_embed>

I also bought the book "Getting Started with LLVM Core Libraries" by Bruno
Cardoso Lopes and Rafael Auler. Chapter 9 of that book teaches you how to
write your own checker for the Clang Static Analyzer.

If I understood correctly, the usual way to use your own custom checkers
would be to install them into the Clang Static Analyzer. Then you run the
Clang Static Analyzer as you would usually do it, using an IDE like XCode
or via the command line itself: clang --analyze -Xanalyzer
-analyzer-checker=core source.c

I have some kind of an issue here. In order to make you understand, I
should explain you my assignment. I need to detect redundant computations
and function calls returning some result. They are assigned to some
variable, and then that variable is never referenced:
variable = sqrt(pow(variable, 3) + pow(variable, 3) * 8) / 2;

Don't ask me why that code is like that, I didn't write it. There are
multiple examples such as this. The code base that I'm refactoring is also
supposed to be optimized for super high performance, so we can't have
redundant calculations like this, when the result is never even used.
Creating a refactoring tool to search and replace these bad constructs is
my job.

When I run code such as the above through the Clang Static Analyzer, it
shows me this message:
warning: Value stored to 'variable' is never read

I need to tap into the power of the Static Analyzer in detecting
problematic code constructs such as these. I'm trying to make my own
checker that would not only just detect such occurences and emit a warning,
but also to create replacements of these occurences with nothing. I want my
checker to just edit the source file that it's processing and perform the
replacements just like the AST matcher call backs do.

My direct supervisor said that the whole C++ refactoring tool that I'm
writing has to be a single executable standing alone, and that it needs to
process the source code files and perform the refactorings in place. Now in
the main function of this refactoring tool I'm invoking the Clang Compiler
and providing AST matchers to it:
// Run the tool
        auto result = tool.runAndSave(newFrontendActionFactory(&mf).get());
        if (result != 0) {
                errs() << "Error in the Refactoring Tool: " << result <<
"\n";
                return result;
        }

As I will be also writing a custom static analysis checker, I also need to
add this to the main function, and I need to have some way to invoke the
Clang Static Analyzer from the main function of the executable. Is there a
function similar to clang::tooling::RefactoringTool::runAndSave(), but for
the Clang Static Analyzer, that will install my custom static analysis
checkers and run the Clang Static Analyzer over the legacy source code
files provided in the command line of my executable tool? This is what I
need.

If there is no such "hot and ready to eat" function, how does the Clang
Static Analyzer work, what kind of code in the LLVM libraries starts up the
Clang Static Analyzer and runs it over the input files? If it's necessary,
I will create such a function to invoke the Clang Static Analyzer from my
code, myself and use it like that. I am not afraid to dig into the LLVM
source code to see how can I duplicate the startup and file processing
behaviors of the Clang Static Analyzer in my own code. But I think that
would be the hard way of doing things. If there is an easier way to invoke
the Clang Static Analyzer with my custom checkers programatically, please
let me know.

And I want my static analysis checkers to actually perform the
replacements, to remove the problematic code constructs are they are found.
To do this I think that I would need to know the SourceLocation of these
detected code constructs. It is not enough for me to simply print a warning
to the screen. The C code base is HUGE and it would take an inordinate
amount of time to perform the replacements manually.
The lecture from the 2012 LLVM Developers’ Meeting that I watched, Anna
Zaks and Jordan Rose didn't really explain how to perform the replacements
inside the static analysis checkers. They just had their custom checker
print a warning to the screen. However, as I explained, my requirements are
different.
Also, one of the requirements is that my application needs to perform the
replacements in place. The company needs a standing alone internal tool.
The whole process needs to be seamless. My application needs to be easily
used by the other engineers. They are electrical engineers and wouldn't
know any better, so that's why I'm automating all this stuff for them.

What changes should I add to my Makefile to be able to link to the Clang
Static Analyzer's API? I want to use it in my project.

So far I've achieved some large amount of progress using the AST matchers
and now I need to extend my application with the static analysis checkers
as for my direct supervisor's assignment. Please don't scold me too much if
I am wrong somewhere. This is my first real development job, and first time
using the Clang/LLVM libraries.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20190720/cbf4f334/attachment.html>
-------------- next part --------------
/*
 * As part of LLVM coding standards, you should organize your include statements by putting local headers
 * first, followed by Clang and LLVM API headers. When two headers pertain to the same category,
 * order them alphabetically.
 */
// Header files for CallBack classes, each of which implements a single refactoring.
#include "FindVariablesMatchCallback.h"
#include "MakeStaticMatchCallback.h"
#include "RemoveHypotMatchCallback.h"
#include "RemoveMemcpyMatchCallback.h"
#include "RemovePointerMatchCallback.h"
#include "RemoveVariablesMatchCallback.h"

// Header files for Clang libraries.
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/Support/CommandLine.h"

#include <sys/types.h>
#include <unistd.h>

using llvm::outs;
using llvm::errs;
using llvm::raw_ostream;
using llvm::report_fatal_error;

using llvm::cl::opt;
using llvm::cl::desc;
using llvm::cl::Positional;
using llvm::cl::OneOrMore;
using llvm::cl::extrahelp;
using llvm::cl::ValueRequired;
using llvm::cl::SetVersionPrinter;
using llvm::cl::ParseCommandLineOptions;

using clang::tooling::RefactoringTool;
using clang::tooling::newFrontendActionFactory;
using clang::tooling::CompilationDatabase;
using clang::tooling::FixedCompilationDatabase;
using clang::ast_matchers::MatchFinder;

/* Command line options description: */

// <bool> Says that this option takes no argument, and is to be treated as a bool value only.
// If this option is set, then the variable becomes true, otherwise it becomes false.
// This line of code has to be above the ParseCommandLineOptions function call, otherwise
// it will fail to parse the -debug option out, and the refactoring_tool executable will
// fail to recognize that command line option.
opt<bool> DebugOutput("debug", desc("This option enables diagnostic output."));

// Options to turn on various refactorings are optional.
opt<bool> RunAll("all", desc("This options turns on all supported refactorings."));
opt<bool> RunRemoveMemcpy("remove-memcpy", desc("This option turns on replacement of memcpy()."));
opt<bool> RunMakeStatic("make-static", desc("This option turns all dynamic memory allocations "
                                            "into stack ones, gets rid of calloc() and free()."));
opt<bool> RunRemovePointer("remove-pointer", desc("This option turns on removal of the global pointer."));
opt<bool> RunRemoveHypot("remove-hypot", desc("This option turns on replacement of hypot()."));
opt<bool> RunRemoveVariables("remove-variables", desc("This option removes unreferenced variables."));

// Option specifies the build path/directory.
opt<string> BuildPath(Positional, desc("[<build-path>]"));
// Options specifying the source files to refactor are one or more required.
opt<string> SourcePaths(Positional, desc("<source0> [... <sourceN>]"), OneOrMore, ValueRequired);

// Define an additional help message to be printed.
extrahelp CommonHelp(
    "\nArguments above mentioned in [ ] are optional (not required).\n"
    "<build-path> should be specified if specific compiler options\n"
    "are not provided on the command line.\n"
);

// Global variable is non static so that it can be externed into other translation units.
bool print_debug_output = false;


int main(int argc, const char **argv)
{
	// Format should be:
	// $ refactoring_tool tool_specific options -- clang_specific_options (not used)
    //  OR
    // $ refactoring_tool [options] [<build-path>] <source0> [... <sourceN>] --
	// By default, input file(s) are treated as positional arguments of the tool-specific part
    // of the options.

	/* Command line parsing: */

    // Define the information to be printed with the -version option.
    // Use a C++11 lambda function as the VersionPrinterTy func parameter to SetVersionPrinter().
    // Adjacent string literals are automatically concatenated in C and C++.
    SetVersionPrinter(
        [](raw_ostream& os) {
            const string version_information = "McLeod Refactoring Tool\n"
                                               "By Konstantin Rebrov\n"
                                               "development version 2.5\n";
            os << version_information;
        }
    );

	// Parses the command line arguments for you.
	ParseCommandLineOptions(argc, argv);
    string ErrorMessage;

    // Try to build a compilation datavase directly from the command-line.
    std::unique_ptr<CompilationDatabase> Compilations(
        FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage)
    );
    // If that failed.
    if (!Compilations) {
        // Load the compilation database using the given directory.
        // Destroys the old object pointed to by the unique_ptr (if it exists), and acquires
        // ownership of the rhs unique_ptr (or rather the underlying CompilationDatabase that it's
        // pointing to).
        Compilations = CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage);
        // And if that failed.
        if (!Compilations) {
            errs() << "ERROR: Could not build compilation database.\n";
            // Calls installed error handlers, prints a message generated by the llvm standard
            // library, and gracefully exits the program.
            report_fatal_error(ErrorMessage);
        }
    }

    // ParseCommandLineOptions has to be called before DebugOutput can be used.
    // It sets the value of the DebugOutput depending on the presence or absence of the
    // -debug flag in the command line arguments.
    // cl::opt<T> is a class which has an operator T() method, in this case it is used to convert
    // to bool. It's easier to work with build in data types than classes.
    print_debug_output = DebugOutput;

    // If the user specified -all option, then all refactorings should be enabled.
    if (RunAll) {
        RunRemoveMemcpy    = true;
        RunMakeStatic      = true;
        RunRemovePointer   = true;
        RunRemoveHypot     = true;
        RunRemoveVariables = true;
    }

	/* Run the Clang compiler for the each input file separately
     * (one input file - one output file).
     *	This is default ClangTool behaviour.
	 */
	// The first argument is a list of compilations.
	// The second argument is a list of source files to parse.
	RefactoringTool tool(*Compilations, SourcePaths);

    if (print_debug_output) {
	    outs() << "Starting match finder\n";
        outs() << "\n\n";
    }

    // This first MatchFinder is responsible for applying replacements in the first round.
	MatchFinder mf;

    /* Only add the AST matchers if the options enabling these refactorings are activated. */

	//// Remove memcpy details
	// Make the RemoveMemcpyMatchCallback class be able to recieve the match results.
	RemoveMemcpyMatchCallback remove_memcpy_match_callback(&tool.getReplacements());
    if (RunRemoveMemcpy) {
	    remove_memcpy_match_callback.getASTmatchers(mf);
    }

	//// Make static details
	MakeStaticMatchCallback make_static_match_callback(&tool.getReplacements());
    if (RunMakeStatic) {
	    make_static_match_callback.getASTmatchers(mf);
    }

    //// Remove pointer details
    RemovePointerMatchCallback remove_pointer_match_callback(&tool.getReplacements());
    if (RunRemovePointer) {
        remove_pointer_match_callback.getASTmatchers(mf);
    }

    //// Remove hypot details
    RemoveHypotMatchCallback remove_hypot_match_callback(&tool.getReplacements());
    if (RunRemoveHypot) {
        remove_hypot_match_callback.getASTmatchers(mf);
    }

    //// Remove variables details
    // NOTE: default constuctor takes no arguments.
    // FindVariablesMatchCallback does not do any replacements, it only counts the variables.
    FindVariablesMatchCallback find_variables_match_callback;
    if (RunRemoveVariables) {
        find_variables_match_callback.getASTmatchers(mf);
    }

	// Run the tool
	auto result = tool.runAndSave(newFrontendActionFactory(&mf).get());
	if (result != 0) {
		errs() << "Error in the Refactoring Tool: " << result << "\n";
		return result;
	}

    // Create a second RefactoringTool to run the second round of refactorings.
    // The first argument is a list of compilations.
	// The second argument is a list of source files to parse.
	RefactoringTool tool2(*Compilations, SourcePaths);
    // Create a second MatchFinder to run the new RefactoringTool through the source code again,
    // to apply replacements in the second round, mainly RemoveVariablesMatchCallback.
    MatchFinder mf2;

    //// Remove variables details
    // This one actually removes the variables.
    RemoveVariablesMatchCallback remove_variables_match_callback(&tool.getReplacements());
    if (RunRemoveVariables) {
        /* These Step 1 and Step 2 MUST be called in this order ALWAYS! */

        // Step 1: Get the list of variables to remove from the find_variables_match_callback.
        // Connect the remove_variables_match_callback with the find_variables_match_callback.
        find_variables_match_callback.collectResults(remove_variables_match_callback.getVector());

        // Step 2: Get the AST matchers describing them.
        remove_variables_match_callback.getASTmatchers(mf2);
    }

    // Run the new Refactoring Tool to apply replacements in the second round.
	auto result2 = tool2.runAndSave(newFrontendActionFactory(&mf2).get());
	if (result2 != 0) {
	    errs() << "Error in the Refactoring Tool: " << result2 << "\n";
	    return result2;
    }

    char* args[4];
    args[0] = "--analyze";
    args[1] = "-Xanalyzer";
    args[2] = "-analyzer-checker=core";
    args[3] = "navsses_model/complete_system_io.c";
    if (fork() == 0) {
        execve("clang", args, nullptr);
    }

    // Print diagnostic output.
    if (print_debug_output) {
        unsigned int num_refactorings = 0;

        if (RunRemoveMemcpy) {
            unsigned int num_matches_found = remove_memcpy_match_callback.getNumMatchesFound();
            unsigned int num_replacements = remove_memcpy_match_callback.getNumReplacements();
            num_refactorings += num_replacements;
	        outs() << "Found " << num_matches_found << " memcpy() matches\n";
	        outs() << "Performed " << num_replacements << " memcpy() replacements\n";
        }

        if (RunMakeStatic) {
            unsigned int num_free_calls = make_static_match_callback.num_free_calls();
            unsigned int num_calloc_calls = make_static_match_callback.num_calloc_calls();
            num_refactorings += num_free_calls;
            num_refactorings += num_calloc_calls;
            outs() << "Found " << num_free_calls << " calls to free()\n";
	        outs() << "Found " << num_free_calls << " calls to calloc()\n";
        }

        if (RunRemovePointer) {
            unsigned int num_global_pointers = remove_pointer_match_callback.getNumGlobalPointerRemovals();
            unsigned int num_pointer_uses = remove_pointer_match_callback.getNumPointerUseReplacements();
            unsigned int num_pointer_dereferences = remove_pointer_match_callback.getNumPointerDereferenceReplacements();
            num_refactorings += num_global_pointers;
            num_refactorings += num_pointer_uses;
            num_refactorings += num_pointer_dereferences;
            outs() << "Removed " << num_global_pointers << " global pointers.\n";
            outs() << "Replaced " << num_pointer_uses << " pointer uses.\n";
            outs() << "Replaced " << num_pointer_dereferences << " pointer dereferences.\n";
        }

        if (RunRemoveHypot) {
            unsigned int num_hypot_replacements = remove_hypot_match_callback.getNumHypotReplacements();
            num_refactorings += num_hypot_replacements;
            outs() << "Replaced " << num_hypot_replacements << " calls to hypot()\n";
        }

        if (RunRemoveVariables) {
            unsigned int num_unused_variables = remove_variables_match_callback.getNumUnusedVariableRemovals();
            num_refactorings += num_unused_variables;
            outs() << "Removed " << num_unused_variables << " unused variables.\n";
        }

        outs() << '\n' << "Performed " << num_refactorings << " total refactorings\n";
    }

	return 0;
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Makefile
Type: application/octet-stream
Size: 3301 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20190720/cbf4f334/attachment.obj>
-------------- next part --------------
#ifndef REMOVE_POINTER_MATCH_CALLBACK_H
#define REMOVE_POINTER_MATCH_CALLBACK_H

#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Core/Replacement.h"

#include <cstring>
#include <map>
#include <string>

using std::string;
using std::map;

using llvm::raw_ostream;

using clang::Expr;
using clang::Decl;
using clang::MemberExpr;
using clang::SourceManager;
using clang::SourceLocation;
using clang::tooling::Replacements;
using clang::ast_matchers::MatchFinder;

class RemovePointerMatchCallback : public MatchFinder::MatchCallback
{
  public:
    /**
     * This is the constructor, which initializes all the member variables of the class.
     * The explicit keyword means that we cannot use the assignment = operator to initialize an
     * instance of this class.
     *
     * @param map<string, Replacements> * replacements - A pointer to a std::map of strings and
     *                                    Replacements objects, which is what we will use to
     *                                    actually perform the source code replacements in the
     *                                    refactoring process.
     */
    explicit RemovePointerMatchCallback(map<string, Replacements> * replacements)
      : replacements(replacements), SM(nullptr) {}

    /**
     * This method creates and "returns" the AST matchers that match expressions specifically
     * handled by this CallBack class, through the pass by reference parameter.
     *
     * @param MatchFinder& mf - A non const reference to the MatchFinder in the main() function.
     *                     When this object is passed into this method, it is modified, the AST
     *                     matchers are added to it. This is my solution for "returning" multiple
     *                     AST matchers of possibly different types.
     */
    void getASTmatchers(MatchFinder& mf);

    /**
     * This method is called every time the AST matcher matches a corresponding expression
     * in the AST. It then performs a refactoring on that expression.
     * However this method does not do the actual refactoring, rather it determines what type of
     * expresssion was matched and then it calls helper methods to perform the actual refactoring.
     *
     * @param const MatchResult& result - The matched result returned from the AST matcher.
     */
    void run(const MatchFinder::MatchResult& result) override;

    /**
	 * Returns the number of successful global pointer removals.
	 */
	unsigned int getNumGlobalPointerRemovals() const { return num_global_pointers; }

    /**
	 * Returns the number of successful pointer use replacements.
	 */
	unsigned int getNumPointerUseReplacements() const { return num_pointer_uses; }

    /**
	 * Returns the number of successful pointer dereference replacements.
	 */
	unsigned int getNumPointerDereferenceReplacements() const { return num_pointer_dereferences; }

  private:
    /**
     * This is a first level helper method called by run().
     * Being given a declaration of a global pointer to structure,
     * this method deletes that declaration from the source code.
     * If that refactoring is successful, it increments num_global_pointers.
     *
     * @param const Decl* decl - The declaration of the global pointer.
     */
    void remove_global_pointer(const Decl* decl);

    /**
     * This is a first level helper method called by run().
     * Being given an Expr* that can be cast to a DeclRefExpr*, representing a use case of
     * that global pointer, where it is used as an address value without being dereferenced,
     * this method replaces that pointer use with the address of the structure to which that
     * pointer points.
     * If that refactoring is successful, it increments num_pointer_uses.
     *
     * @param const Expr* expr - An aexpression representing that global pointer being used as
     *                           an address value.
     */
    void replace_pointer_use(const Expr* expr);

    /**
     * This is a first level helper method called by run().
     * Being given a MemberExpr representing dereferencing a global pointer to obtain a field in
     * the structure, this method replaces that expression in the source code with just that struct
     * that the pointer is pointing to, using the dot operator to access that same field.
     * Example:
     * complete_system_io_M->Timing  to  complete_system_io_M_.Timing
     *
     * But there's a catch: The -> operator MemberExpr can be of two kinds, explicit and implicit.
     * "Explicit" here means that you typed the -> operator in your source code:  pointer->field
     * "Implicit" here means that the -> operator was the result of a macro expansion where that
     * pointer was passed into a macro which dereferenced that pointer:  macro(pointer)
     * In the implicit case these is one more thing: that the -> operator can be explicitly written
     * inside an argument to a macro: macro(pointer->field).
     *
     * These three cases are handled by other functions. replace_pointer_arrow() determines what
     * scenario we have in the code, and then it calls helper functions to do the actual
     * replacements.
     *
     * @param cosnt MemberExpr* expr - A member expression, specifically an arrow expression.
     */
    void replace_pointer_arrow(const MemberExpr* expr);

    /**
     * This is a second level helper method called by replace_pointer_arrow().
     * It performs the replacement only inside a macro.
     * Being given a Expr* representing a base expression, the pointer which is used,
     * and a SourceLocation, representing that base expression's starting location,
     * this method handles the above mentioned implicit case.
     * If the global pointer is the argument to a macro,
     * macro(pointer)
     * Then this function replaces that pointer with the address of the structure to which the
     * pointer points.
     * macro(&structure)
     * If that refactoring is successful, it increments num_pointer_uses.
     *
     * @param const Expr* baseExp - Represents a base expression, the global pointer which is
     *                    passed into the macro, and dereferenced by it.
     *
     * @param SourceLocation loc1 - Represents the starting location of the base expression.
     */
    void replace_pointer_use_macro(const Expr* baseExp, SourceLocation loc1);

    /**
     * This is a second helper method called by replace_pointer_arrow().
     * It replaces a pointer dereference such as pointer->field with a direct dot member access
     * using the structure to which that pointer points:  structure.field
     * If that refactoring is successful, it increments num_pointer_dereferences.
     *
     * @param const Expr* baseExpr - Represents the base expression of the arrow expression,
     *                               the pointer itself which is dereferenced.
     */
    void replace_pointer_dereference(const Expr* baseExp);

    /**
     * This function prints the full expression, the filename where this expression originated,
     * the row number (line number), and the column number. It is used for diagnostic purposes.
     *
     * @param const Expr* expr - An expression. This pointer can point to an Expr object or an
     *                    object of any data type derived from that class.
     *
     * @param raw_ostream& output - A reference to an LLVM raw output stream, which is
     *                     an extremely fast bulk output stream that can only output to a stream.
     *                     Data can be written to a different destination depending on the value of
     *                     this parameter. It can be llvm::outs(), llvm::errs(), or llvm::nulls().
     *                     The reference is non-const because writing output to an instance of a
     *                     stream class causes that object to be modified.
     *
     * @param const SourceLocation& loc_start - The starting location of the passed in expression.
     *              Passing it in as a parameter means that we don't have to know precisely what
     *              type the expression is, which we must know in order to determine the
     *              SourceLocation manually.
     *
     * @param const int numchars = -1 - This parameter is optional. If it is omitted, it gets the
     *                                  default value -1.
     *                  A string representation of an expression is comprised of a SourceLocation
     *                  and some number of characters following that location.
     *             *  If numbers < 0, which may occur if that argument is not provided, or a
     *                negative value is intentionally passed in,
     *                  The program tries to estimate the number of characters comprising that
     *                  expression. This is the default setting.
     *             *  If numbers >= 0, which is only if that argument is explicitly passed in,
     *                  Then a string starting at the SourceLocation with numchars following
     *                  characters is printed to the specified LLVM raw output stream.
     */
    void outputExpression(const Expr* expr, raw_ostream& output, const SourceLocation& loc_start, int numchars = -1) const;

    /**
     * This function prints the full declaration, the filename where this expression originated,
     * the row number (line number), and the column number. It is used for diagnostic purposes.
     *
     * @param const Decl* decl - A declaration. This pointer can point to a Decl object or an
     *                    object of any data type derived from that class.
     *
     * @param raw_ostream& output - A reference to an LLVM raw output stream, which is
     *                     an extremely fast bulk output stream that can only output to a stream.
     *                     Data can be written to a different destination depending on the value of
     *                     this parameter. It can be llvm::outs(), llvm::errs(), or llvm::nulls().
     *                     The reference is non-const because writing output to an instance of a
     *                     stream class causes that object to be modified.
     *
     * @param const SourceLocation& loc_start - The starting location of the passed in declaration.
     *              Passing it in as a parameter means that we don't have to know precisely what
     *              type the declaration is, which we must know in order to determine the
     *              SourceLocation manually.
     */
    void outputDeclaration(const Decl* decl, raw_ostream& output, const SourceLocation& loc_start) const;

    /**
     * @param const Expr* expression - A pointer to an instance of clang::Expr,
     *        or one of it's derived types.
     *
     * @return string - A string representation of the passed in expression,
     *         the exact string text of that expression.
     */
    string getExprAsString(const Expr* expression) const;

    /**
     * @param const Decl* declaration - A pointer to an instance of clang::Decl,
     *        or one of it's derived types.
     *
     * @return string - A string representation of the passed in declaration,
     *         the exact string text of that declaration.
     */
    string getDeclAsString(const Decl* declaration) const;

    /* Private member variables. */

    map<string, Replacements>* replacements;
    // This class handles loading and caching of source files into memory.
    // It is the middleman between the refactoring tool and the actual C source code which is
    // being analyzed. This enables us to do source code replacements.
    SourceManager* SM;
    unsigned int num_global_pointers{0};
    unsigned int num_pointer_uses{0};
    unsigned int num_pointer_dereferences{0};
	// Add other variables here as needed.
};

#endif /* REMOVE_POINTER_MATCH_CALLBACK_H */


More information about the cfe-dev mailing list