LLVM Clang Class Name Format Verification Plugin

Xcode written by LLVM Clang compiler for code class name format checking plug-in

Source Code

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
 
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
 
namespace WSHPlugin {
    class MJHandler : public MatchFinder::MatchCallback {
    private:
        CompilerInstance &ci;
        
    public:
        MJHandler(CompilerInstance &ci) :ci(ci) {}
        
        void run(const MatchFinder::MatchResult &Result) {
            if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
                size_t pos = decl->getName().find('_');
                if (pos != StringRef::npos) {
                    DiagnosticsEngine &D = ci.getDiagnostics();
                    SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
                    D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "Warm Tip: Class names cannot be underlined"));
                }
            }
        }
    };
    
    class MJASTConsumer: public ASTConsumer {
    private:
        MatchFinder matcher;
        MJHandler handler;
        
    public:
        MJASTConsumer(CompilerInstance &ci) :handler(ci) {
            matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
        }
        
        void HandleTranslationUnit(ASTContext &context) {
            matcher.matchAST(context);
        }
    };
    
    class MJASTAction: public PluginASTAction {
    public:
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
        
        bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
    };
}
 
static FrontendPluginRegistry::Add<WSHPlugin::MJASTAction>
X("WSHPlugin", "The WSHPlugin is my first clang-plugin.");

Source Code Analysis

Because, LLVM yes C++The development environment, so the code is all passed through C++To write.

1. Import header file

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
Import the header Clang header file, Clang is the front-end development editor for C, C++, Objective-C languages. In the Clang command, we can see that one of the alleviations in program variation is through the spanning tree phase. In the program compilation spanning tree phase, we write plug-ins. Therefore, in addition to the C++ header file of iostream, we also need to import the header file of AST spanning tree.

2. Namespaces

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
Namespace use is used to simplify the use of C++ code to directly name members and methods within a namespace.
namespace WSHPlugin {...}

3. Register a plug-in

static FrontendPluginRegistry::Add<WSHPlugin::MJASTAction>
X("WSHPlugin", "The WSHPlugin is my first clang-plugin.");
(1) Set a static method to indicate that memory is only loaded once when this code statement is called. Frontend Plugin Registry (Frontend Plugin Registry - Front End Plugin Registry registration);
(2) Call FrontendPluginRegistry's Add method to place MJASAction in the WSHPlugin namespace as a parameter - indicating the behavior of a spanning tree. The parameters passed in are "Name of Frame" - WSHPlugin and "Description of Frame" - The WSHPlugin is my first clang-plugin."
(3) Add<...> is followed by a function template, passing in parameters according to the template;

4. Create a Handler class - Manager class

    class MJHandler : public MatchFinder::MatchCallback {
    private:
        CompilerInstance &ci;
        
    public:
        MJHandler(CompilerInstance &ci) :ci(ci) {}
        
        void run(const MatchFinder::MatchResult &Result) {
            if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
                size_t pos = decl->getName().find('_');
                if (pos != StringRef::npos) {
                    DiagnosticsEngine &D = ci.getDiagnostics();
                    SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
                    D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "Warm Tip: Class names cannot be underlined"));
                }
            }
        }
    };
(1) MJHandler inherits MatchCallback fallback classes under MatchFinder;
(2) Define an instance object ci to complete compilation;
(3) Create a constructor for a class, but do nothing;
(4) Define a runtime method, and the parameter passed in is the result object of MatchFinder::MatchResult of type const (prevent modification, more secure);
(5) Judgment conditions
if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
    size_t pos = decl->getName().find('_');
    if (pos != StringRef::npos) {
        ...
    }
}
(6) Diagnostics - Diagnostics && Diagnostics Engine - Diagnostic Engine [Obtain diagnostic information by completing the object ci compilation of the instance];
DiagnosticsEngine &D = ci.getDiagnostics();
(7) SourceLocation loc - Returns the location of the source code by locating the error warning and diagnostic information to determine that there was an error on one line of code and to record his error information;
SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
(8) Prompt error information (DiagnosticsEngine::Error - Error under Diagnostic Engine) [D.getCustomDiagID - Get ID information of the dialog box] by passing the saved location information and warm prompt content that needs to be displayed to the Report() function;
D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "Warm Tip: Class names cannot be underlined"));

5. Create a second class, MJASTConsumer - User mode for spanning trees

 class MJASTConsumer: public ASTConsumer {
    private:
        MatchFinder matcher;
        MJHandler handler;
        
    public:
        MJASTConsumer(CompilerInstance &ci) :handler(ci) {
            matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
        }
        
        void HandleTranslationUnit(ASTContext &context) {
            matcher.matchAST(context);
        }
    };
(1) MJASTConsumer inherits from ASTConsumer and creates two members through MatchFinder and MJHandler;
(2) Design a constructor;

6. Create a third class of MJASTAction - AST actions

class MJASTAction: public PluginASTAction {
    public:
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
        
        bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
    };
unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
An AST user ASTConsumer is created and returned
 bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }

Keywords: C C++ iOS source code analysis

Added by jimrains on Fri, 17 Sep 2021 10:39:45 +0300