1010#include " clang/ASTMatchers/ASTMatchFinder.h"
1111#include " clang/Lex/Lexer.h"
1212#include " clang/Lex/Preprocessor.h"
13- #include < sstream >
13+ #include < optional >
1414
1515using namespace clang ::ast_matchers;
1616
1717namespace clang ::tidy::readability {
1818class UseCppStyleCommentsCheck ::CStyleCommentHandler : public CommentHandler {
1919public:
20- CStyleCommentHandler (UseCppStyleCommentsCheck &Check, bool ExcludeDoxygen)
20+ CStyleCommentHandler (UseCppStyleCommentsCheck &Check, bool ExcludeDoxygen,
21+ StringRef ExcludedComments)
2122 : Check(Check), ExcludeDoxygen(ExcludeDoxygen),
23+ ExcludedComments (ExcludedComments),
24+ ExcludedCommentMatch(ExcludedComments),
2225 CStyleCommentMatch(
2326 " ^[ \t ]*/\\ *+[ \t\r\n ]*(.*[ \t\r\n ]*)*[ \t\r\n ]*\\ *+/[ \t\r\n ]*$" ) {
2427 }
2528
26- void setExcludeDoxygen ( bool Exclude) { ExcludeDoxygen = Exclude ; }
29+ StringRef getExcludedCommentsRegex () const { return ExcludedComments ; }
2730
2831 bool isExcludeDoxygen () const { return ExcludeDoxygen; }
2932
30- std::string convertToCppStyleComment (const SourceManager &SM,
31- const SourceRange &Range) {
32- const StringRef CommentText = Lexer::getSourceText (
33+ void convertToCppStyleCommentFixes (const SourceManager &SM,
34+ const SourceRange &Range,
35+ SmallVectorImpl<FixItHint> &FixIts) {
36+ StringRef CommentText = Lexer::getSourceText (
3337 CharSourceRange::getTokenRange (Range), SM, LangOptions ());
3438
35- std::string InnerText = CommentText.str ();
36- InnerText.erase (0 , 2 );
37- InnerText.erase (InnerText.size () - 2 , 2 );
38-
39- std::string Result;
40- std::istringstream Stream (InnerText);
41- std::string Line;
42-
43- if (std::getline (Stream, Line)) {
44- const size_t StartPos = Line.find_first_not_of (" \t " );
45- if (StartPos != std::string::npos) {
46- Line = Line.substr (StartPos);
39+ CommentText = CommentText.drop_front (2 ).drop_back (2 );
40+ const SourceLocation StartLoc = Range.getBegin ();
41+ const FileID FID = SM.getFileID (StartLoc);
42+ unsigned LineNo = SM.getSpellingLineNumber (StartLoc);
43+ const unsigned StartCol = SM.getSpellingColumnNumber (StartLoc);
44+ if (!CommentText.empty ()) {
45+ const size_t NewlinePos = CommentText.find (' \n ' );
46+
47+ const StringRef Line = (NewlinePos == StringRef::npos)
48+ ? CommentText
49+ : CommentText.substr (0 , NewlinePos);
50+
51+ const SourceLocation LineStart =
52+ SM.translateLineCol (FID, LineNo, StartCol);
53+ const SourceLocation LineEnd = SM.translateLineCol (
54+ FID, LineNo, std::numeric_limits<unsigned >::max ());
55+
56+ FixIts.push_back (FixItHint::CreateReplacement (
57+ CharSourceRange::getCharRange (LineStart, LineEnd),
58+ " //" + Line.str ()));
59+
60+ if (NewlinePos != StringRef::npos) {
61+ CommentText = CommentText.substr (NewlinePos + 1 );
62+ LineNo++;
4763 } else {
48- Line. clear () ;
64+ CommentText = " " ;
4965 }
50- Result += " // " + Line;
5166 }
52-
53- while (std::getline (Stream, Line)) {
54- const size_t StartPos = Line.find_first_not_of (" \t " );
55- if (StartPos != std::string::npos) {
56- Line = Line.substr (StartPos);
67+ while (!CommentText.empty ()) {
68+ const size_t NewlinePos = CommentText.find (' \n ' );
69+
70+ StringRef Line = (NewlinePos == StringRef::npos)
71+ ? CommentText
72+ : CommentText.substr (0 , NewlinePos);
73+
74+ const SourceLocation LineStart = SM.translateLineCol (FID, LineNo, 1 );
75+ const SourceLocation LineEnd = SM.translateLineCol (
76+ FID, LineNo, std::numeric_limits<unsigned >::max ());
77+
78+ if (Line.substr (0 , 2 ) == " " ) {
79+ Line = Line.drop_front (2 );
80+ FixIts.push_back (FixItHint::CreateReplacement (
81+ CharSourceRange::getCharRange (LineStart, LineEnd),
82+ " //" + Line.str ()));
5783 } else {
58- Line.clear ();
84+ FixIts.push_back (FixItHint::CreateReplacement (
85+ CharSourceRange::getCharRange (LineStart, LineEnd),
86+ " //" + Line.str ()));
87+ }
88+
89+ if (NewlinePos == StringRef::npos) {
90+ break ;
5991 }
60- Result += " \n // " + Line;
92+ CommentText = CommentText.substr (NewlinePos + 1 );
93+ LineNo++;
6194 }
62- return Result;
6395 }
6496
6597 bool isDoxygenStyleComment (const StringRef &Text) {
@@ -70,23 +102,21 @@ class UseCppStyleCommentsCheck::CStyleCommentHandler : public CommentHandler {
70102 Trimmed.drop_front (2 ).starts_with (" *" ));
71103 }
72104
73- bool CheckForTextAfterComment (Preprocessor &PP, SourceRange Range) {
105+ bool CheckForCodeAfterComment (Preprocessor &PP, SourceRange Range) {
74106 const SourceManager &SM = PP.getSourceManager ();
75- const SourceLocation CommentEnd = Range.getEnd ();
76-
77- unsigned EndLine = SM.getSpellingLineNumber (CommentEnd);
78- unsigned EndCol = SM.getSpellingColumnNumber (CommentEnd);
79-
80- const SourceLocation LineBegin =
81- SM.translateLineCol (SM.getFileID (CommentEnd), EndLine, EndCol);
82- const SourceLocation LineEnd =
83- SM.translateLineCol (SM.getFileID (CommentEnd), EndLine,
84- std::numeric_limits<unsigned >::max ());
85- const StringRef AfterComment =
86- Lexer::getSourceText (CharSourceRange::getCharRange (LineBegin, LineEnd),
87- SM, PP.getLangOpts ());
88-
89- return !AfterComment.trim ().empty ();
107+ const SourceLocation CommentStart = Range.getBegin (),
108+ CommentEnd = Range.getEnd ();
109+ const std::optional<Token> NextTok =
110+ Lexer::findNextToken (CommentStart, SM, PP.getLangOpts ());
111+ if (!NextTok.has_value ()) {
112+ return false ;
113+ }
114+ const std::string tokenSpelling =
115+ Lexer::getSpelling (*NextTok, SM, PP.getLangOpts ());
116+ const unsigned lineNo = SM.getSpellingLineNumber (CommentEnd);
117+ const SourceLocation loc = NextTok->getLocation ();
118+ const unsigned tokenLine = SM.getSpellingLineNumber (loc);
119+ return lineNo == tokenLine;
90120 }
91121
92122 bool HandleComment (Preprocessor &PP, SourceRange Range) override {
@@ -99,6 +129,12 @@ class UseCppStyleCommentsCheck::CStyleCommentHandler : public CommentHandler {
99129 const StringRef Text = Lexer::getSourceText (
100130 CharSourceRange::getCharRange (Range), SM, PP.getLangOpts ());
101131
132+ if (!ExcludedCommentMatch.isValid ()) {
133+ llvm::errs () << " Warning: Invalid regex pattern:" << ExcludedComments
134+ << " :for ExcludedComments\n " ;
135+ } else if (ExcludedCommentMatch.match (Text)) {
136+ return false ;
137+ }
102138 if (ExcludeDoxygen && isDoxygenStyleComment (Text)) {
103139 return false ;
104140 }
@@ -108,30 +144,38 @@ class UseCppStyleCommentsCheck::CStyleCommentHandler : public CommentHandler {
108144 return false ;
109145 }
110146
111- if (CheckForTextAfterComment (PP, Range)) {
147+ if (CheckForCodeAfterComment (PP, Range)) {
112148 return false ;
113149 }
114150
115- Check.diag (
151+ SmallVector<FixItHint, 4 > FixIts;
152+ convertToCppStyleCommentFixes (SM, Range, FixIts);
153+
154+ auto D = Check.diag (
116155 Range.getBegin (),
117- " use C++ style comments '//' instead of C style comments '/*...*/'" )
118- << clang::FixItHint::CreateReplacement (
119- Range, convertToCppStyleComment (SM, Range));
156+ " use C++ style comments '//' instead of C style comments '/*...*/'" );
157+
158+ for (const auto &Fix : FixIts) {
159+ D << Fix;
160+ }
120161
121162 return false ;
122163 }
123164
124165private:
125166 UseCppStyleCommentsCheck &Check;
126167 bool ExcludeDoxygen;
168+ StringRef ExcludedComments;
169+ llvm::Regex ExcludedCommentMatch;
127170 llvm::Regex CStyleCommentMatch;
128171};
129172
130173UseCppStyleCommentsCheck::UseCppStyleCommentsCheck (StringRef Name,
131174 ClangTidyContext *Context)
132175 : ClangTidyCheck(Name, Context),
133176 Handler(std::make_unique<CStyleCommentHandler>(
134- *this , Options.get(" ExcludeDoxygenStyleComments" , false ))) {}
177+ *this , Options.get(" ExcludeDoxygenStyleComments" , false ),
178+ Options.get(" ExcludedComments" , " ^$" ))) {}
135179
136180void UseCppStyleCommentsCheck::registerPPCallbacks (
137181 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
@@ -143,6 +187,7 @@ void UseCppStyleCommentsCheck::check(const MatchFinder::MatchResult &Result) {}
143187void UseCppStyleCommentsCheck::storeOptions (ClangTidyOptions::OptionMap &Opts) {
144188 Options.store (Opts, " ExcludeDoxygenStyleComments" ,
145189 Handler->isExcludeDoxygen ());
190+ Options.store (Opts, " ExcludedComments" , Handler->getExcludedCommentsRegex ());
146191}
147192
148193UseCppStyleCommentsCheck::~UseCppStyleCommentsCheck () = default ;
0 commit comments