130 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "TodoCommentCheck.h"10#include "clang/Frontend/CompilerInstance.h"11#include "clang/Lex/Preprocessor.h"12#include <optional>13 14namespace clang::tidy {15 16namespace google::readability {17 18enum class StyleKind { Parentheses, Hyphen };19 20} // namespace google::readability21 22template <> struct OptionEnumMapping<google::readability::StyleKind> {23 static ArrayRef<std::pair<google::readability::StyleKind, StringRef>>24 getEnumMapping() {25 static constexpr std::pair<google::readability::StyleKind, StringRef>26 Mapping[] = {27 {google::readability::StyleKind::Hyphen, "Hyphen"},28 {google::readability::StyleKind::Parentheses, "Parentheses"},29 };30 return {Mapping};31 }32};33 34} // namespace clang::tidy35 36namespace clang::tidy::google::readability {37class TodoCommentCheck::TodoCommentHandler : public CommentHandler {38public:39 TodoCommentHandler(TodoCommentCheck &Check, std::optional<std::string> User)40 : Check(Check), User(User ? *User : "unknown"),41 TodoMatch(R"(^// *TODO *((\((.*)\))?:?( )?|: *(.*) *- *)?(.*)$)") {42 const llvm::StringRef TodoStyleString =43 Check.Options.get("Style", "Hyphen");44 for (const auto &[Value, Name] :45 OptionEnumMapping<StyleKind>::getEnumMapping()) {46 if (Name == TodoStyleString) {47 TodoStyle = Value;48 return;49 }50 }51 Check.configurationDiag(52 "invalid value '%0' for "53 "google-readability-todo.Style; valid values are "54 "'Parentheses' and 'Hyphen'. Defaulting to 'Hyphen'")55 << TodoStyleString;56 }57 58 bool HandleComment(Preprocessor &PP, SourceRange Range) override {59 const StringRef Text =60 Lexer::getSourceText(CharSourceRange::getCharRange(Range),61 PP.getSourceManager(), PP.getLangOpts());62 63 SmallVector<StringRef, 7> Matches;64 if (!TodoMatch.match(Text, &Matches))65 return false;66 67 const StyleKind ParsedStyle =68 !Matches[3].empty() ? StyleKind::Parentheses : StyleKind::Hyphen;69 const StringRef Username =70 ParsedStyle == StyleKind::Parentheses ? Matches[3] : Matches[5];71 const StringRef Comment = Matches[6];72 73 if (!Username.empty() &&74 (ParsedStyle == StyleKind::Parentheses || !Comment.empty())) {75 return false;76 }77 78 if (Username.empty()) {79 Check.diag(Range.getBegin(), "missing username/bug in TODO")80 << FixItHint::CreateReplacement(81 CharSourceRange::getCharRange(Range),82 createReplacementString(Username, Comment));83 }84 85 if (Comment.empty())86 Check.diag(Range.getBegin(), "missing details in TODO");87 88 return false;89 }90 91 std::string createReplacementString(const StringRef Username,92 const StringRef Comment) const {93 if (TodoStyle == StyleKind::Parentheses) {94 return ("// TODO(" + Twine(User) +95 "): " + (Comment.empty() ? "some details" : Comment))96 .str();97 }98 return ("// TODO: " + Twine(User) + " - " +99 (Comment.empty() ? "some details" : Comment))100 .str();101 }102 103 StyleKind getTodoStyle() const { return TodoStyle; }104 105private:106 TodoCommentCheck &Check;107 std::string User;108 llvm::Regex TodoMatch;109 StyleKind TodoStyle = StyleKind::Hyphen;110};111 112TodoCommentCheck::TodoCommentCheck(StringRef Name, ClangTidyContext *Context)113 : ClangTidyCheck(Name, Context),114 Handler(std::make_unique<TodoCommentHandler>(115 *this, Context->getOptions().User)) {}116 117TodoCommentCheck::~TodoCommentCheck() = default;118 119void TodoCommentCheck::registerPPCallbacks(const SourceManager &SM,120 Preprocessor *PP,121 Preprocessor *ModuleExpanderPP) {122 PP->addCommentHandler(Handler.get());123}124 125void TodoCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {126 Options.store(Opts, "Style", Handler->getTodoStyle());127}128 129} // namespace clang::tidy::google::readability130