diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 4be1a8f831339..01a86a23fd572 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC UniqueptrDeleteReleaseCheck.cpp UppercaseLiteralSuffixCheck.cpp UseAnyOfAllOfCheck.cpp + UseCppStyleCommentsCheck.cpp UseStdMinMaxCheck.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index 4c0812f0e6793..1add5695b178d 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -61,6 +61,7 @@ #include "UniqueptrDeleteReleaseCheck.h" #include "UppercaseLiteralSuffixCheck.h" #include "UseAnyOfAllOfCheck.h" +#include "UseCppStyleCommentsCheck.h" #include "UseStdMinMaxCheck.h" namespace clang::tidy { @@ -173,6 +174,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-uppercase-literal-suffix"); CheckFactories.registerCheck( "readability-use-anyofallof"); + CheckFactories.registerCheck( + "readability-use-cpp-style-comments"); CheckFactories.registerCheck( "readability-use-std-min-max"); } diff --git a/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.cpp new file mode 100644 index 0000000000000..0638deef5f307 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.cpp @@ -0,0 +1,162 @@ +//===--- UseCppStyleCommentsCheck.cpp - clang-tidy-------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UseCppStyleCommentsCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { +class UseCppStyleCommentsCheck::CStyleCommentHandler : public CommentHandler { +public: + CStyleCommentHandler(UseCppStyleCommentsCheck &Check, bool ExcludeDoxygen, + StringRef ExcludedComments) + : Check(Check), ExcludeDoxygen(ExcludeDoxygen), + ExcludedComments(ExcludedComments), + ExcludedCommentMatch(ExcludedComments), + CStyleCommentMatch( + "^[ \t]*/\\*+[ \t\r\n]*(.*[ \t\r\n]*)*[ \t\r\n]*\\*+/[ \t\r\n]*$") { + } + + StringRef getExcludedCommentsRegex() const { return ExcludedComments; } + + bool isExcludeDoxygen() const { return ExcludeDoxygen; } + + void convertToCppStyleCommentFixes(const SourceManager &SM, + const SourceRange &Range, + SmallVectorImpl &FixIts) { + + StringRef Raw = Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + SM, LangOptions()); + + size_t FirstStar = Raw.find('*'); + size_t LastStar = Raw.rfind('*'); + + StringRef CommentText; + if (FirstStar != StringRef::npos && LastStar != StringRef::npos && + LastStar > FirstStar) { + CommentText = Raw.substr(FirstStar + 1, LastStar - FirstStar - 1); + } + + CommentText = CommentText.trim(" \t\r\n*/"); + + SmallVector Lines; + CommentText.split(Lines, '\n'); + + const FileID FID = SM.getFileID(Range.getBegin()); + unsigned LineNo = SM.getSpellingLineNumber(Range.getBegin()); + + for (auto &Line : Lines) { + Line = Line.ltrim(" \t"); + SourceLocation LineStart = SM.translateLineCol(FID, LineNo, 1); + SourceLocation LineEnd = + Lexer::getLocForEndOfToken(Range.getEnd(), 0, SM, LangOptions()); + + FixIts.push_back(FixItHint::CreateReplacement( + CharSourceRange::getCharRange(LineStart, LineEnd), + "// " + Line.str())); + + ++LineNo; + } + } + + bool isDoxygenStyleComment(const StringRef &Text) { + StringRef Trimmed = Text.ltrim(); + return Trimmed.starts_with("/**") || Trimmed.starts_with("/*!") || + Trimmed.starts_with("///") || Trimmed.starts_with("//!") || + (Trimmed.starts_with("/*") && + Trimmed.drop_front(2).starts_with("*")); + } + + bool CheckForCodeAfterComment(Preprocessor &PP, SourceRange Range) { + const SourceManager &SM = PP.getSourceManager(); + const SourceLocation CommentStart = Range.getBegin(), + CommentEnd = Range.getEnd(); + const std::optional NextTok = + Lexer::findNextToken(CommentStart, SM, PP.getLangOpts()); + if (!NextTok.has_value()) + return false; + + const std::string tokenSpelling = + Lexer::getSpelling(*NextTok, SM, PP.getLangOpts()); + const unsigned lineNo = SM.getSpellingLineNumber(CommentEnd); + const SourceLocation loc = NextTok->getLocation(); + const unsigned tokenLine = SM.getSpellingLineNumber(loc); + return lineNo == tokenLine; + } + + bool HandleComment(Preprocessor &PP, SourceRange Range) override { + const SourceManager &SM = PP.getSourceManager(); + + if (Range.getBegin().isMacroID() || SM.isInSystemHeader(Range.getBegin())) + return false; + + const StringRef Text = Lexer::getSourceText( + CharSourceRange::getCharRange(Range), SM, PP.getLangOpts()); + + if (!ExcludedCommentMatch.isValid()) { + llvm::errs() << "Warning: Invalid regex pattern:" << ExcludedComments + << ":for ExcludedComments\n"; + } else if (ExcludedCommentMatch.match(Text)) { + return false; + } + if (ExcludeDoxygen && isDoxygenStyleComment(Text)) + return false; + + SmallVector Matches; + if (!CStyleCommentMatch.match(Text, &Matches)) + return false; + + if (CheckForCodeAfterComment(PP, Range)) + return false; + + SmallVector FixIts; + convertToCppStyleCommentFixes(SM, Range, FixIts); + + auto D = Check.diag( + Range.getBegin(), + "use C++ style comments '//' instead of C style comments '/*...*/'"); + + for (const auto &Fix : FixIts) + D << Fix; + + return false; + } + +private: + UseCppStyleCommentsCheck &Check; + bool ExcludeDoxygen; + StringRef ExcludedComments; + llvm::Regex ExcludedCommentMatch; + llvm::Regex CStyleCommentMatch; +}; + +UseCppStyleCommentsCheck::UseCppStyleCommentsCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Handler(std::make_unique( + *this, Options.get("ExcludeDoxygenStyleComments", false), + Options.get("ExcludedComments", "^$"))) {} + +void UseCppStyleCommentsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ExcludeDoxygenStyleComments", + Handler->isExcludeDoxygen()); + Options.store(Opts, "ExcludedComments", Handler->getExcludedCommentsRegex()); +} +void UseCppStyleCommentsCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + PP->addCommentHandler(Handler.get()); +} + +void UseCppStyleCommentsCheck::check(const MatchFinder::MatchResult &Result) {} + +UseCppStyleCommentsCheck::~UseCppStyleCommentsCheck() = default; +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.h b/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.h new file mode 100644 index 0000000000000..d672106739feb --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/UseCppStyleCommentsCheck.h @@ -0,0 +1,45 @@ +//===--- UseCppStyleCommentsCheck.h - clang-tidy-----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECPPSTYLECOMMENTSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECPPSTYLECOMMENTSCHECK_H + +#include "../ClangTidyCheck.h" +#include + +namespace clang::tidy::readability { +/// Detects C Style comments and suggests to use C++ style comments instead. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/use-cpp-style-comments.html +class UseCppStyleCommentsCheck : public ClangTidyCheck { +public: + UseCppStyleCommentsCheck(StringRef Name, ClangTidyContext *Context); + ~UseCppStyleCommentsCheck() override; + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + class CStyleCommentHandler; + std::unique_ptr Handler; + const bool ExcludeDoxygenStyleComments = + Options.get("ExcludeDoxygenStyleComments", false); + const StringRef ExcludedComments = Options.get("ExcludedComments", "^$"); +}; +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECPPSTYLECOMMENTSCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 0ad52f83fad85..ae92a70b3e450 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -112,6 +112,11 @@ New checks Finds potentially erroneous calls to ``reset`` method on smart pointers when the pointee type also has a ``reset`` method. +- New :doc:`readability-use-cpp-style-comments + ` check. + + Replaces C-style comments with C++-style comments. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index c73bc8bff3539..e9f92bd5f47f9 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -405,6 +405,7 @@ Clang-Tidy Checks :doc:`readability-uniqueptr-delete-release `, "Yes" :doc:`readability-uppercase-literal-suffix `, "Yes" :doc:`readability-use-anyofallof `, + :doc:`readability-use-cpp-style-comments `, "Yes" :doc:`readability-use-std-min-max `, "Yes" :doc:`zircon-temporary-objects `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-cpp-style-comments.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-cpp-style-comments.rst new file mode 100644 index 0000000000000..5ed8079053bcf --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-cpp-style-comments.rst @@ -0,0 +1,59 @@ +.. title:: clang-tidy - use-cpp-style-comments + +readability-use-cpp-style-comments +================================== + +Replaces C-style comments with C++-style comments. + +C-style comments (``/* ... */``) are inherited from C, while C++ introduces +``//`` as a more concise, readable, and less error-prone alternative. Modern +C++ guidelines recommend using C++-style comments for consistency and +maintainability. This check identifies and replaces C-style comments with +equivalent C++-style comments. + +Examples: + +Input: + +.. code-block::c++ + + /* This is a single-line comment */ + int x = 42; /* Inline comment */ + + /* This is a + multi-line comment */ + +Output: + +.. code-block::c++ + + // This is a single-line comment + int x = 42; // Inline comment + + // This is a + // multi-line comment + +.. note:: + + Inline Comments are neither fixed nor warned. + + Example: + + .. code-block:: c++ + + int a = /* this is a comment */ 5; + +Options +------- + +.. option:: ExcludeDoxygenStyleComments + + A boolean option that determines whether Doxygen-style comments should be + excluded. Default is `false`. + +.. option:: ExcludedComments + + A regex pattern that allows specifying certain comments to exclude from + transformation. By default, this option is set to `^$`, which means no + comments are excluded. You can provide a custom regex to exclude specific + comments based on your needs. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments-doxygen.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments-doxygen.cpp new file mode 100644 index 0000000000000..22f7ba4bb776a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments-doxygen.cpp @@ -0,0 +1,34 @@ +// RUN: %check_clang_tidy -std=c++11 %s readability-use-cpp-style-comments %t -config="{CheckOptions: [{key: readability-use-cpp-style-comments.ExcludeDoxygenStyleComments, value: true}]}" + +// Tests for Doxygen comments with ExcludeDoxygenStyleComments enabled +/** + * This is a Doxygen comment for a function. + * It should NOT be transformed. + */ +void doxygenFunction1(); + +/*! + * This is another Doxygen-style comment. + * It should also NOT be transformed. + */ +void doxygenFunction2(); + +/** + * Multiline Doxygen comment describing parameters. + * + * @param x The first parameter. + * @param y The second parameter. + * @return A result value. + */ +int doxygenFunctionWithParams(int x, int y); + +/******************************* + * Non-Doxygen block comments without markers + *******************************/ +void DoxygenBlock(); + +/*! + * This is a single-line Doxygen comment. + * Should NOT be transformed. + */ +void singleLineDoxygen(); diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments.cpp new file mode 100644 index 0000000000000..c1908e4691e9d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-cpp-style-comments.cpp @@ -0,0 +1,80 @@ +// RUN: %check_clang_tidy -std=c++98-or-later %s readability-use-cpp-style-comments %t + +// Single-line full C-style comment +static const int CONSTANT = 42; /* Important constant value */ +// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: static const int CONSTANT = 42; // Important constant value + +// Inline comment that should NOT be transformed +int a = /* inline comment */ 5; + +// Multiline full-line comment +/* This is a multiline comment + that spans several lines + and should be converted to C++ style */ +// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: // This is a multiline comment +// CHECK-FIXES: // that spans several lines +// CHECK-FIXES: // and should be converted to C++ style +void fnWithSomeBools(bool A,bool B) {} +// Function with parameter inline comments +void processData(int data /* input data */, + bool validate /* perform validation */) { + // These inline comments should NOT be transformed + fnWithSomeBools(/*ControlsA=*/ true, /*ControlsB=*/ false); +} + +int calculateSomething() { return 1;} +// Comment at end of complex line +int complexCalculation = calculateSomething(); /* Result of complex calculation */ +// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: int complexCalculation = calculateSomething(); // Result of complex calculation + +// Nested comments and edge cases +void edgeCaseFunction() { + int x = 10 /* First value */ + 20 /* Second value */; // Inline comments should not transform + + /* Comment with special characters !@#$%^&*()_+ */ + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] + // CHECK-FIXES: // Comment with special characters !@#$%^&*()_+ +} + +// Multiline comment with various indentations + /* This comment is indented + and should preserve indentation when converted */ +// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: // This comment is indented +// CHECK-FIXES: // and should preserve indentation when converted + +// Complex function with mixed comment types +void complexFunction() { + /* Full line comment at start of block */ + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] + // CHECK-FIXES: // Full line comment at start of block + + int x = 10; /* Inline comment not to be transformed */ + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] + // CHECK-FIXES: int x = 10; // Inline comment not to be transformed +} + +/* aaa + bbbb + ccc */ int z = 1; +// There is a token after the comment ends so it should be ignored. + +int y = 10;/* aaa + bbbb + ccc */ int z1 = 1; +// There is a token after the comment ends so it should be ignored. + +/* aaa +a // abc + bbb */ +// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: // aaa +// CHECK-FIXES: //a // abc +// CHECK-FIXES: // bbb + +int k1 = 49; /* aa //bbbb aa */// +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use C++ style comments '//' instead of C style comments '/*...*/' [readability-use-cpp-style-comments] +// CHECK-FIXES: // aa //bbbb aa