11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/StringExtras.h"
16 using namespace clang::ast_matchers;
20 namespace readability {
22 NamespaceCommentCheck::NamespaceCommentCheck(StringRef
Name,
25 NamespaceCommentPattern(
"^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
26 "namespace( +([a-zA-Z0-9_]+))?\\.? *(\\*/)?$",
27 llvm::Regex::IgnoreCase),
28 ShortNamespaceLines(Options.get(
"ShortNamespaceLines", 1u)),
29 SpacesBeforeComments(Options.get(
"SpacesBeforeComments", 1u)) {}
32 Options.
store(Opts,
"ShortNamespaceLines", ShortNamespaceLines);
33 Options.
store(Opts,
"SpacesBeforeComments", SpacesBeforeComments);
40 Finder->addMatcher(namespaceDecl().bind(
"namespace"),
this);
44 SourceLocation Loc1, SourceLocation Loc2) {
45 return Loc1.isFileID() && Loc2.isFileID() &&
46 Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
50 bool InsertLineBreak) {
51 std::string
Fix =
"// namespace";
52 if (!ND->isAnonymousNamespace())
53 Fix.append(
" ").append(ND->getNameAsString());
60 const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>(
"namespace");
61 const SourceManager &Sources = *Result.SourceManager;
68 unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart());
69 unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
70 if (EndLine - StartLine + 1 <= ShortNamespaceLines)
74 SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
75 SourceLocation
Loc = AfterRBrace;
78 while (Lexer::getRawToken(Loc, Tok, Sources,
getLangOpts()) ||
80 Loc = Loc.getLocWithOffset(1);
85 bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
88 bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
90 SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
91 std::string
Message =
"%0 not terminated with a closing comment";
94 if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
95 StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
96 SmallVector<StringRef, 7> Groups;
97 if (NamespaceCommentPattern.match(Comment, &Groups)) {
98 StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] :
"";
99 StringRef Anonymous = Groups.size() > 3 ? Groups[3] :
"";
102 if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) ||
103 (ND->getNameAsString() == NamespaceNameInComment &&
104 Anonymous.empty())) {
111 NeedLineBreak = Comment.startswith(
"/*");
113 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
116 "%0 ends with a comment that refers to a wrong namespace '") +
117 NamespaceNameInComment +
"'")
119 }
else if (Comment.startswith(
"//")) {
122 NeedLineBreak =
false;
124 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
125 Message =
"%0 ends with an unrecognized comment";
131 std::string NamespaceName =
132 ND->isAnonymousNamespace()
133 ?
"anonymous namespace"
134 : (
"namespace '" + ND->getNameAsString() +
"'");
136 diag(AfterRBrace, Message)
137 << NamespaceName << FixItHint::CreateReplacement(
138 CharSourceRange::getCharRange(OldCommentRange),
139 std::string(SpacesBeforeComments,
' ') +
141 diag(ND->getLocation(),
"%0 starts here", DiagnosticIDs::Note)
SourceLocation Loc
'#' location in the include directive
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
static const StringRef Message
Base class for all clang-tidy checks.
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
static std::string getNamespaceComment(const NamespaceDecl *ND, bool InsertLineBreak)
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static cl::opt< bool > Fix("fix", cl::desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.