10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchers.h" 12 #include "clang/Basic/SourceLocation.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->getBeginLoc());
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);
108 Lexer::getAsCharRange(SourceRange(NestedNamespaceBegin, LBracketLocation),
110 StringRef NestedNamespaceName =
111 Lexer::getSourceText(TextRange, Sources,
getLangOpts())
114 bool IsNested = NestedNamespaceName.contains(
':');
117 Ends.push_back(LBracketLocation);
119 NestedNamespaceName = ND->getName();
122 while (Lexer::getRawToken(Loc, Tok, Sources,
getLangOpts()) ||
124 Loc = Loc.getLocWithOffset(1);
130 bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
133 bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
135 SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
136 std::string
Message =
"%0 not terminated with a closing comment";
139 if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
140 StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
141 SmallVector<StringRef, 7> Groups;
142 if (NamespaceCommentPattern.match(Comment, &Groups)) {
143 StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] :
"";
144 StringRef Anonymous = Groups.size() > 3 ? Groups[3] :
"";
146 if (IsNested && NestedNamespaceName == NamespaceNameInComment) {
149 }
else if ((ND->isAnonymousNamespace() &&
150 NamespaceNameInComment.empty()) ||
151 (ND->getNameAsString() == NamespaceNameInComment &&
152 Anonymous.empty())) {
160 NeedLineBreak = Comment.startswith(
"/*");
162 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
165 "%0 ends with a comment that refers to a wrong namespace '") +
166 NamespaceNameInComment +
"'")
168 }
else if (Comment.startswith(
"//")) {
171 NeedLineBreak =
false;
173 SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
174 Message =
"%0 ends with an unrecognized comment";
180 std::string NamespaceName =
181 ND->isAnonymousNamespace()
182 ?
"anonymous namespace" 183 : (
"namespace '" + NestedNamespaceName.str() +
"'");
186 SourceLocation DiagLoc =
187 OldCommentRange.getBegin() != OldCommentRange.getEnd()
188 ? OldCommentRange.getBegin()
189 : ND->getRBraceLoc();
191 diag(DiagLoc, Message)
193 << FixItHint::CreateReplacement(
194 CharSourceRange::getCharRange(OldCommentRange),
195 std::string(SpacesBeforeComments,
' ') +
199 diag(ND->getLocation(),
"%0 starts here", DiagnosticIDs::Note)
SourceLocation Loc
'#' location in the include directive
Some operations such as code completion produce a set of candidates.
constexpr llvm::StringLiteral Message
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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.
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)
static constexpr llvm::StringLiteral Name
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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
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.