11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Lex/Lexer.h" 14 #include "llvm/ADT/StringExtras.h" 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 bool InsertLineBreak) {
61 std::string
Fix =
"// namespace ";
62 Fix.append(NameSpaceName);
69 const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>(
"namespace");
70 const SourceManager &Sources = *Result.SourceManager;
77 unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart());
78 unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
79 if (EndLine - StartLine + 1 <= ShortNamespaceLines)
83 SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
84 SourceLocation
Loc = AfterRBrace;
86 SourceLocation LBracketLocation = ND->getLocation();
87 SourceLocation NestedNamespaceBegin = LBracketLocation;
92 for (
const auto &EndOfNameLocation : Ends) {
93 if (Sources.isBeforeInTranslationUnit(NestedNamespaceBegin,
99 if (!ND->getLocation().isMacroID()) {
100 while (Lexer::getRawToken(LBracketLocation, Tok, Sources,
getLangOpts()) ||
101 !Tok.is(tok::l_brace)) {
102 LBracketLocation = LBracketLocation.getLocWithOffset(1);
107 Lexer::getAsCharRange(SourceRange(NestedNamespaceBegin, LBracketLocation),
109 StringRef NestedNamespaceName =
110 Lexer::getSourceText(TextRange, Sources,
getLangOpts()).rtrim();
111 bool IsNested = NestedNamespaceName.contains(
':');
114 Ends.push_back(LBracketLocation);
116 NestedNamespaceName = ND->getName();
119 while (Lexer::getRawToken(Loc, Tok, Sources,
getLangOpts()) ||
121 Loc = Loc.getLocWithOffset(1);
127 bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
130 bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
132 SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
133 std::string
Message =
"%0 not terminated with a closing comment";
136 if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
137 StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
138 SmallVector<StringRef, 7> Groups;
139 if (NamespaceCommentPattern.match(Comment, &Groups)) {
140 StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] :
"";
141 StringRef Anonymous = Groups.size() > 3 ? Groups[3] :
"";
143 if (IsNested && NestedNamespaceName == NamespaceNameInComment) {
146 }
else if ((ND->isAnonymousNamespace() &&
147 NamespaceNameInComment.empty()) ||
148 (ND->getNameAsString() == NamespaceNameInComment &&
149 Anonymous.empty())) {
157 NeedLineBreak = Comment.startswith(
"/*");
159 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
162 "%0 ends with a comment that refers to a wrong namespace '") +
163 NamespaceNameInComment +
"'")
165 }
else if (Comment.startswith(
"//")) {
168 NeedLineBreak =
false;
170 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
171 Message =
"%0 ends with an unrecognized comment";
177 std::string NamespaceName =
178 ND->isAnonymousNamespace()
179 ?
"anonymous namespace" 180 : (
"namespace '" + NestedNamespaceName.str() +
"'");
182 diag(AfterRBrace, Message)
184 << FixItHint::CreateReplacement(
185 CharSourceRange::getCharRange(OldCommentRange),
186 std::string(SpacesBeforeComments,
' ') +
190 diag(ND->getLocation(),
"%0 starts here", DiagnosticIDs::Note)
SourceLocation Loc
'#' location in the include directive
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.
Some operations such as code completion produce a set of candidates.
LangOptions getLangOpts() const
Returns the language options from the context.
static const StringRef Message
Base class for all clang-tidy checks.
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)
std::map< std::string, std::string > OptionMap
static std::string getNamespaceComment(const NamespaceDecl *ND, bool InsertLineBreak)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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.