{"id":6549,"date":"2014-02-28T18:39:59","date_gmt":"2014-02-28T15:39:59","guid":{"rendered":"http:\/\/railsware.com\/blog\/?p=6549"},"modified":"2021-08-12T13:17:15","modified_gmt":"2021-08-12T10:17:15","slug":"creation-and-using-clang-plugin-with-xcode","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/creation-and-using-clang-plugin-with-xcode\/","title":{"rendered":"Creating and using Clang plugin with Xcode"},"content":{"rendered":"\n<p>This tutorial describes how to create Clang plugin and covers the next things:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>environment setup<\/li><li>basic plugin setup<\/li><li>setup Xcode project for plugin development<\/li><li>warnings reporting<\/li><li>errors reporting<\/li><li>Xcode integration<\/li><li>interactive hints for errors\/warnings riddance<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">tl;dr<\/h3>\n\n\n\n<p>Clang Rocks!!!<\/p>\n\n\n\n<p>You can find the plugin <a title=\"Plugin here\" href=\"https:\/\/github.com\/AlexDenisov\/ToyClangPlugin\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Intro<\/h3>\n\n\n\n<p>While working on <a title=\"BloodMagic\" href=\"https:\/\/github.com\/railsware\/BloodMagic\" target=\"_blank\" rel=\"noopener\">BloodMagic<\/a>, I realised that it&#8217;d be nice to have a tool for checking semantic errors related to BM usage. For example: in the interface property marked as <code>lazy<\/code>, but not defined as <code>@dynamic<\/code> in the implementation, or property marked as <code>lazy<\/code>, but class container doesn&#8217;t support injections.<\/p>\n\n\n\n<p>I concluded that I need to work with and I need a full-featured parser.<\/p>\n\n\n\n<p>I&#8217;ve tried different approaches: <a title=\"flex\" href=\"http:\/\/en.wikipedia.org\/wiki\/Flex_lexical_analyser\" target=\"_blank\" rel=\"noopener\">flex<\/a>+<a title=\"bison\" href=\"http:\/\/en.wikipedia.org\/wiki\/GNU_bison\" target=\"_blank\" rel=\"noopener\">bison<\/a>, <a title=\"libclang\" href=\"http:\/\/clang.llvm.org\/doxygen\/group__CINDEX.html\" target=\"_blank\" rel=\"noopener\">libclang<\/a>, but ultimately I decided to write a Clang plugin.<\/p>\n\n\n\n<p>Just for testing purposes I&#8217;ve started a simple plugin with the following goals:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>use Xcode for development<\/li><li>integrate ready plugin into Xcode and use it workaday<\/li><li>plugin should report warnings, errors and propose interactive hints for fixes (via Xcode UI)<\/li><\/ul>\n\n\n\n<p>Features of the test plugin:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>report warning in case of class&#8217; name starts with lowercase letter<\/li><li>report error in case of class&#8217; name contains underscore <code>_<\/code><\/li><li>propose hints for fixes<\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Environment setup<\/h4>\n\n\n\n<p>For plugin development we need llvm\/clang, built from source<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">cd \/opt\nsudo mkdir llvm\nsudo chown `whoami` llvm\ncd llvm\nexport LLVM_HOME=`pwd`<\/pre>\n\n\n\n<p>Current <code>clang<\/code> version on my system &#8211; 3.3.1, so let&#8217;s build respective version:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">git clone -b release_33 https:\/\/github.com\/llvm-mirror\/llvm.git llvm\ngit clone -b release_33 https:\/\/github.com\/llvm-mirror\/clang.git llvm\/tools\/clang\ngit clone -b release_33 https:\/\/github.com\/llvm-mirror\/clang-tools-extra.git llvm\/tools\/clang\/tools\/extra\ngit clone -b release_33 https:\/\/github.com\/llvm-mirror\/compiler-rt.git llvm\/projects\/compiler-rt\n \nmkdir llvm_build\ncd llvm_build\ncmake ..\/llvm -DCMAKE_BUILD_TYPE:STRING=Release\nmake -j`sysctl -n hw.logicalcpu`<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Basic plugin setup<\/h4>\n\n\n\n<p>Create directory for plugin<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">cd $LLVM_HOME\nmkdir toy_clang_plugin; cd toy_clang_plugin<\/pre>\n\n\n\n<p>Our plugin based on example from Clang repo and here is it&#8217;s structure:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">ToyClangPlugin.exports\nCMakeLists.txt\nToyClangPlugin.cpp<\/pre>\n\n\n\n<p>We&#8217;ll use one source file for simplification<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">#include \"clang\/Frontend\/FrontendPluginRegistry.h\"\n#include \"clang\/AST\/AST.h\"\n#include \"clang\/AST\/ASTConsumer.h\"\n#include \"clang\/Frontend\/CompilerInstance.h\"\n \nusing namespace clang;\n \nnamespace\n{\n    class ToyConsumer : public ASTConsumer\n    {\n    };\n    \n    class ToyASTAction : public PluginASTAction\n    {\n    public:\n        virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &amp;Compiler,\n                                                      llvm::StringRef InFile)\n        {\n            return new ToyConsumer;\n        }\n        \n        bool ParseArgs(const CompilerInstance &amp;CI, const\n                       std::vector&lt;std::string&gt;&amp; args) {\n            return true;\n        }\n    };\n}\n \nstatic clang::FrontendPluginRegistry::Add&lt;ToyASTAction&gt;\nX(\"ToyClangPlugin\", \"Toy Clang Plugin\");<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:default decode:true\">cmake_minimum_required (VERSION 2.6)\nproject (ToyClangPlugin)\n\nset( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\/bin )\nset( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\/lib )\nset( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}\/lib )\n\nset( LLVM_HOME \/opt\/llvm )\nset( LLVM_SRC_DIR ${LLVM_HOME}\/llvm )\nset( CLANG_SRC_DIR ${LLVM_HOME}\/llvm\/tools\/clang )\nset( LLVM_BUILD_DIR ${LLVM_HOME}\/llvm_build )\nset( CLANG_BUILD_DIR ${LLVM_HOME}\/llvm_build\/tools\/clang)\n\nadd_definitions (-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS)\nadd_definitions (-D_GNU_SOURCE -DHAVE_CLANG_CONFIG_H)\n\nset (CMAKE_CXX_COMPILER \"${LLVM_BUILD_DIR}\/bin\/clang++\")\nset (CMAKE_CC_COMPILER \"${LLVM_BUILD_DIR}\/bin\/clang\")\n\nset (CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\n  -fPIC\n  -fno-common\n  -Woverloaded-virtual\n  -Wcast-qual\n  -fno-strict-aliasing\n  -pedantic\n  -Wno-long-long\n  -Wall\n  -Wno-unused-parameter\n  -Wwrite-strings\n  -fno-exceptions \n  -fno-rtti\")\n\nset (CMAKE_MODULE_LINKER_FLAGS \"-Wl,-flat_namespace -Wl,-undefined -Wl,suppress\")\n\nset (LLVM_LIBS\n  LLVMJIT\n  LLVMX86CodeGen\n  LLVMX86AsmParser\n  LLVMX86Disassembler\n  LLVMExecutionEngine\n  LLVMAsmPrinter\n  LLVMSelectionDAG\n  LLVMX86AsmPrinter\n  LLVMX86Info\n  LLVMMCParser\n  LLVMCodeGen\n  LLVMX86Utils\n  LLVMScalarOpts\n  LLVMInstCombine\n  LLVMTransformUtils\n  LLVMipa\n  LLVMAnalysis\n  LLVMTarget\n  LLVMCore\n  LLVMMC\n  LLVMSupport\n  LLVMBitReader\n  LLVMOption\n)\n\nmacro(add_clang_plugin name)\n  set (srcs ${ARGN})\n\n  include_directories( \"${LLVM_SRC_DIR}\/include\"\n    \"${CLANG_SRC_DIR}\/include\"\n    \"${LLVM_BUILD_DIR}\/include\"\n    \"${CLANG_BUILD_DIR}\/include\" )\n  link_directories( \"${LLVM_BUILD_DIR}\/lib\" )\n\n  add_library( ${name} SHARED ${srcs} )\n  \n  if (SYMBOL_FILE)\n    set_target_properties( ${name} PROPERTIES LINK_FlAGS\n      \"-exported_symbols_list ${SYMBOL_FILE}\")\n  endif()\n\n  foreach (clang_lib ${CLANG_LIBS})\n    target_link_libraries( ${name} ${clang_lib} )  \n  endforeach()\n  \n  foreach (llvm_lib ${LLVM_LIBS})\n    target_link_libraries( ${name} ${llvm_lib} )\n  endforeach()\n  \n  foreach (user_lib ${USER_LIBS})\n    target_link_libraries( ${name} ${user_lib} )\n  endforeach()\n\nendmacro(add_clang_plugin)\n\nset(SYMBOL_FILE ToyClangPlugin.exports)\n\nset (CLANG_LIBS\n  clang\n  clangFrontend\n  clangAST\n  clangAnalysis\n  clangBasic\n  clangCodeGen\n  clangDriver\n  clangFrontendTool\n  clangLex\n  clangParse\n  clangSema\n  clangEdit\n  clangSerialization\n  clangStaticAnalyzerCheckers\n  clangStaticAnalyzerCore\n  clangStaticAnalyzerFrontend\n)\n\nset (USER_LIBS\n  pthread\n  curses\n)\n\nadd_clang_plugin(ToyClangPlugin \n  ToyClangPlugin.cpp\n)\n\nset_target_properties(ToyClangPlugin PROPERTIES\n  LINKER_LANGUAGE CXX\n  PREFIX \"\")<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">__ZN4llvm8Registry*<\/pre>\n\n\n\n<p>Now we&#8217;re able to generate Xcode-project, based on <code>CMakeLists.txt<\/code><\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">mkdir build; cd build\ncmake -G Xcode ..\nopen ToyClangPlugin.xcodeproj<\/pre>\n\n\n\n<p>Run <code>ALL_BUILD<\/code> target, and you&#8217;ll see dynamic library at <code>lib\/Debug\/ToyCLangPlugin.dylib<\/code>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">RecursiveASTVisitor<\/h4>\n\n\n\n<p>Clang AST module provides <a title=\"RecursiveASTVisitor\" href=\"http:\/\/clang.llvm.org\/doxygen\/classclang_1_1RecursiveASTVisitor.html\" target=\"_blank\" rel=\"noopener\">RecursiveASTVisitor<\/a>, which allows us to traverse via AST.<br>We just need to create a subclass and implement interesting methods.<br>For test we&#8217;ll print all the found class names.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">    class ToyClassVisitor : public RecursiveASTVisitor&lt;ToyClassVisitor&gt;\n    {\n    public:\n        bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *declaration)\n        {\n            printf(\"ObjClass: %s\\n\", declaration-&gt;getNameAsString().c_str());\n            return true;\n        }\n    };\n\n    class ToyConsumer : public ASTConsumer\n    {\n    public:\n        void HandleTranslationUnit(ASTContext &amp;context) {\n            visitor.TraverseDecl(context.getTranslationUnitDecl());\n        }\n    private:\n        ToyClassVisitor visitor;\n    };<\/pre>\n\n\n\n<p>Let&#8217;s create test source file and check how plugin works.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:objc decode:true\">#import &lt;Foundation\/Foundation.h&gt;\n\n@interface ToyObject : NSObject\n\n@end\n\n@implementation ToyObject\n\n@end<\/pre>\n\n\n\n<p>Rebuild plugin and run<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">\/opt\/llvm\/toy_clang_plugin\/build $ $LLVM_HOME\/llvm_build\/bin\/clang ..\/test.m \\\n  -Xclang -load \\\n  -Xclang lib\/Debug\/ToyClangPlugin.dylib \\\n  -Xclang -plugin \\\n  -Xclang ToyClangPlugin<\/pre>\n\n\n\n<p>We&#8217;ll see a huge list of classes.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Report warnings<\/h4>\n\n\n\n<p>Let&#8217;s report warning in case if class&#8217; name starts with lowercase letter<\/p>\n\n\n\n<p>Add <code>ASTContext<\/code> to <code>ToyClassVisitor<\/code><\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">class ToyClassVisitor : public RecursiveASTVisitor&lt;ToyClassVisitor&gt;\n{\nprivate:\n    ASTContext *context;\npublic:\n    void setContext(ASTContext &amp;context)\n    {\n        this-&gt;context = &amp;context;\n    }\n\/\/ ...\n};\n\n\/\/ ...\nvoid HandleTranslationUnit(ASTContext &amp;context) {\n    visitor.setContext(context);\n    visitor.TraverseDecl(context.getTranslationUnitDecl());\n}\n    \/\/ ...<\/pre>\n\n\n\n<p>Add check<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *declaration)\n{\n    checkForLowercasedName(declaration);\n    return true;\n}\n\/\/  ...\nvoid checkForLowercasedName(ObjCInterfaceDecl *declaration)\n{\n    StringRef name = declaration-&gt;getName();\n    char c = name[0];\n    if (isLowercase(c)) {\n        DiagnosticsEngine &amp;diagEngine = context-&gt;getDiagnostics();\n        unsigned diagID = diagEngine.getCustomDiagID(DiagnosticsEngine::Warning, \"Class name should not start with lowercase letter\");\n        SourceLocation location = declaration-&gt;getLocation();\n        diagEngine.Report(location, diagID);\n    }\n}<\/pre>\n\n\n\n<p>Add some class with &#8220;bad&#8221; name<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:objc decode:true\">@interface bad_ToyObject : NSObject\n\n@end\n\n@implementation bad_ToyObject\n\n@end<\/pre>\n\n\n\n<p>rebuild and run<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">\/opt\/llvm\/toy_clang_plugin\/build $ $LLVM_HOME\/llvm_build\/bin\/clang ..\/test.m \\\n  -Xclang -load \\\n  -Xclang lib\/Debug\/ToyClangPlugin.dylib \\\n  -Xclang -plugin \\\n  -Xclang ToyClangPlugin\n\n..\/test.m:11:12: warning: Class name should not start with lowercase letter\n@interface bad_ToyObject : NSObject\n           ^\n1 warning generated.<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Report error<\/h4>\n\n\n\n<p>Let&#8217;s generate error in case of class&#8217; name contains <code>_<\/code><\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">void checkForUnderscoreInName(ObjCInterfaceDecl *declaration)\n{\n    size_t underscorePos = declaration-&gt;getName().find('_');\n    if (underscorePos != StringRef::npos) {\n        DiagnosticsEngine &amp;diagEngine = context-&gt;getDiagnostics();\n        unsigned diagID = diagEngine.getCustomDiagID(DiagnosticsEngine::Error, \"Class name with `_` forbidden\");\n        SourceLocation location = declaration-&gt;getLocation().getLocWithOffset(underscorePos);\n        diagEngine.Report(location, diagID);\n    }\n}\n\nbool VisitObjCInterfaceDecl(ObjCInterfaceDecl *declaration)\n{\n    \/\/ disable this check temporary\n    \/\/ checkForLowercasedName(declaration);\n    checkForUnderscoreInName(declaration);\n    return true;\n}<\/pre>\n\n\n\n<p>Output after running<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">\/opt\/llvm\/toy_clang_plugin\/build $ $LLVM_HOME\/llvm_build\/bin\/clang ..\/test.m \\\n  -Xclang -load \\\n  -Xclang lib\/Debug\/ToyClangPlugin.dylib \\\n  -Xclang -plugin \\\n  -Xclang ToyClangPlugin\n\n..\/test.m:11:15: error: Class name with `_` forbidden\n@interface bad_ToyObject : NSObject\n              ^\n1 error generated.<\/pre>\n\n\n\n<p>Uncomment first check <code>checkForLowercasedName<\/code> and you&#8217;ll see both error and warning in the output<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">\/opt\/llvm\/toy_clang_plugin\/build $ $LLVM_HOME\/llvm_build\/bin\/clang ..\/test.m \\\n  -Xclang -load \\\n  -Xclang lib\/Debug\/ToyClangPlugin.dylib \\\n  -Xclang -plugin \\\n  -Xclang ToyClangPlugin\n\n..\/test.m:11:12: warning: Class name should not start with lowercase letter\n@interface bad_ToyObject : NSObject\n           ^\n..\/test.m:11:15: error: Class name with `_` forbidden\n@interface bad_ToyObject : NSObject\n              ^\n1 warning and 1 error generated.<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Xcode integration<\/h4>\n\n\n\n<p>Unfortunately, system (under &#8216;system&#8217; I mean Xcode&#8217;s clang) clang doesn&#8217;t support plugins, so we need to hack Xcode a bit, to allow using of custom compiler.<\/p>\n\n\n\n<p>Unzip <a title=\"XcodeHacking\" href=\"https:\/\/github.com\/AlexDenisov\/ToyClangPlugin\/releases\/download\/0.0.1\/XcodeHacking.zip\" target=\"_blank\" rel=\"noopener\">this<\/a> archive and run following commands:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">sudo mv HackedClang.xcplugin `xcode-select -print-path`\/..\/PlugIns\/Xcode3Core.ideplugin\/Contents\/SharedSupport\/Developer\/Library\/Xcode\/Plug-ins\nsudo mv HackedBuildSystem.xcspec `xcode-select -print-path`\/Platforms\/iPhoneSimulator.platform\/Developer\/Library\/Xcode\/Specifications<\/pre>\n\n\n\n<p>This will enable custom compiler for Xcode.<\/p>\n\n\n\n<p>Reopen Xcode and you&#8217;ll see new compiler:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/custom_compiler.png\" alt=\"custom_compiler\"\/><\/figure>\n\n\n\n<p>Create new project and select newly added custom clang in <code>Build settings<\/code><\/p>\n\n\n\n<p>To enable plugin add following parameters to the <code>OTHER_CFLAGS<\/code> section<\/p>\n\n\n\n<pre class=\"wp-block-preformatted lang:sh decode:true\">-Xclang -load -Xclang \/opt\/llvm\/toy_clang_plugin\/build\/lib\/Debug\/ToyClangPlugin.dylib -Xclang -add-plugin -Xclang ToyClangPlugin<\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/other_cflags.png\" alt=\"OTHER_CFLAGS\"\/><\/figure>\n\n\n\n<p>Note, that we use <code>-add-plugin<\/code> here, because we want to add our <code>ASTAction<\/code>, not to replace the existing<\/p>\n\n\n\n<p>Also, you should disable modules for this target\/build<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/disable_modules.png\" alt=\"disable_modules\"\/><\/figure>\n\n\n\n<p>Add <code>test.m<\/code> to this project, or create new one, with class names that match plugin criteria<\/p>\n\n\n\n<p>After build you&#8217;ll see error and warnings in a more familiar form<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/error_warning.png\" alt=\"error_warning\"\/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Interactive hints<\/h4>\n\n\n\n<p>It&#8217;s time to add <code>FixItHints<\/code> for both warning and error<\/p>\n\n\n\n<pre class=\"wp-block-preformatted toolbar:1 lang:c++ decode:true\">void checkForLowercasedName(ObjCInterfaceDecl *declaration)\n{\n    StringRef name = declaration-&gt;getName();\n    char c = name[0];\n    if (isLowercase(c)) {\n        std::string tempName = name;\n        tempName[0] = toUppercase(c);\n        StringRef replacement(tempName);\n        \n        SourceLocation nameStart = declaration-&gt;getLocation();\n        SourceLocation nameEnd = nameStart.getLocWithOffset(name.size());\n        \n        FixItHint fixItHint = FixItHint::CreateReplacement(SourceRange(nameStart, nameEnd), replacement);\n        \n        DiagnosticsEngine &amp;diagEngine = context-&gt;getDiagnostics();\n        unsigned diagID = diagEngine.getCustomDiagID(DiagnosticsEngine::Warning, \"Class name should not start with lowercase letter\");\n        SourceLocation location = declaration-&gt;getLocation();\n        diagEngine.Report(location, diagID).AddFixItHint(fixItHint);\n    }\n}\n\nvoid checkForUnderscoreInName(ObjCInterfaceDecl *declaration)\n{\n    StringRef name = declaration-&gt;getName();\n    size_t underscorePos = name.find('_');\n    if (underscorePos != StringRef::npos) {\n        std::string tempName = name;\n        std::string::iterator end_pos = std::remove(tempName.begin(), tempName.end(), '_');\n        tempName.erase(end_pos, tempName.end());\n        StringRef replacement(tempName);\n        \n        SourceLocation nameStart = declaration-&gt;getLocation();\n        SourceLocation nameEnd = nameStart.getLocWithOffset(name.size());\n        \n        FixItHint fixItHint = FixItHint::CreateReplacement(SourceRange(nameStart, nameEnd), replacement);\n        \n        DiagnosticsEngine &amp;diagEngine = context-&gt;getDiagnostics();\n        unsigned diagID = diagEngine.getCustomDiagID(DiagnosticsEngine::Error, \"Class name with `_` forbidden\");\n        SourceLocation location = declaration-&gt;getLocation().getLocWithOffset(underscorePos);\n        diagEngine.Report(location, diagID).AddFixItHint(fixItHint);\n    }\n}<\/pre>\n\n\n\n<p>Rebuild plugin and try to build test project<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/warning_fixit_hint.png\" alt=\"warning_fixit_hint\"\/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/error_fixit_hint.png\" alt=\"error_fixit_hint\"\/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>As you can see, creating a clang plugin is relatively simple, but it needs some dirty hacks with Xcode, and you should use custom built clang, so I&#8217;d not recommend you to use this clang for building apps for production usage. Apple provides patched version, and we can&#8217;t know the difference between them. Also, it needs a lot of efforts to make it work, which doesn&#8217;t make it widely usable.<br>Another issue you might face is unstable API, cause it uses internal API which changes continuously.<\/p>\n\n\n\n<p>You still can use it on your system for different diagnostic purposes, but please do not force other people to depend on such heavyweight things.<\/p>\n\n\n\n<p>If you have any comments, questions or suggestions feel free to ask me on <a title=\"twitter\" href=\"https:\/\/twitter.com\/1101_debian\" target=\"_blank\" rel=\"noopener\">twitter<\/a>, open issue on <a title=\"GitHub\" href=\"https:\/\/github.com\/AlexDenisov\/ToyClangPlugin\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>, or just leave a comment here.<\/p>\n\n\n\n<p>Happy hacking!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial describes how to create Clang plugin and covers the next things: environment setupbasic plugin setupsetup Xcode project for plugin developmentwarnings reportingerrors reportingXcode integrationinteractive hints for errors\/warnings riddance tl;dr Clang Rocks!!! You can find the plugin here. Intro While working on BloodMagic, I realised that it&#8217;d be nice to have a tool for checking&#8230;<\/p>\n","protected":false},"author":55,"featured_media":9446,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["AlexDenisov"],"class_list":["post-6549","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"\/\/railsware.com\/blog\/wp-content\/uploads\/2014\/02\/custom_compiler.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/6549","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/55"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=6549"}],"version-history":[{"count":25,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/6549\/revisions"}],"predecessor-version":[{"id":13962,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/6549\/revisions\/13962"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/9446"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=6549"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=6549"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=6549"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=6549"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}