11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/RecursiveASTVisitor.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/Tooling/FixIt.h" 24 struct UnqualNameVisitor :
public RecursiveASTVisitor<UnqualNameVisitor> {
26 UnqualNameVisitor(
const FunctionDecl &F) : F(F) {}
30 bool shouldWalkTypesOfTypeLocs()
const {
return false; }
32 bool VisitUnqualName(StringRef UnqualName) {
34 for (ParmVarDecl *Param : F.parameters())
35 if (
const IdentifierInfo *Ident = Param->getIdentifier())
36 if (Ident->getName() == UnqualName) {
43 bool TraverseTypeLoc(TypeLoc TL,
bool Elaborated =
false) {
48 switch (TL.getTypeLocClass()) {
51 TL.getAs<RecordTypeLoc>().getTypePtr()->getDecl()->getName()))
56 TL.getAs<EnumTypeLoc>().getTypePtr()->getDecl()->getName()))
59 case TypeLoc::TemplateSpecialization:
60 if (VisitUnqualName(TL.getAs<TemplateSpecializationTypeLoc>()
72 return RecursiveASTVisitor<UnqualNameVisitor>::TraverseTypeLoc(TL);
77 bool TraverseQualifiedTypeLoc(QualifiedTypeLoc TL) {
78 return TraverseTypeLoc(TL.getUnqualifiedLoc());
83 bool TraverseElaboratedTypeLoc(ElaboratedTypeLoc TL) {
84 if (TL.getQualifierLoc() &&
85 !TraverseNestedNameSpecifierLoc(TL.getQualifierLoc()))
87 return TraverseTypeLoc(TL.getNamedTypeLoc(),
true);
90 bool VisitDeclRefExpr(DeclRefExpr *S) {
91 DeclarationName
Name = S->getNameInfo().getName();
92 return S->getQualifierLoc() || !Name.isIdentifier() ||
93 !VisitUnqualName(Name.getAsIdentifierInfo()->getName());
97 const FunctionDecl &F;
102 "use a trailing return type for this function";
105 const SourceManager &SM) {
107 Loc =
expandIfMacroId(SM.getImmediateExpansionRange(Loc).getBegin(), SM);
108 assert(!Loc.isMacroID() &&
109 "SourceLocation must not be a macro ID after recursive expansion");
113 SourceLocation UseTrailingReturnTypeCheck::findTrailingReturnTypeSourceLocation(
114 const FunctionDecl &F,
const FunctionTypeLoc &FTL,
const ASTContext &
Ctx,
115 const SourceManager &SM,
const LangOptions &LangOpts) {
117 SourceRange ExceptionSpecRange = F.getExceptionSpecSourceRange();
118 if (ExceptionSpecRange.isValid())
119 return Lexer::getLocForEndOfToken(ExceptionSpecRange.getEnd(), 0, SM,
124 SourceLocation ClosingParen = FTL.getRParenLoc();
125 if (ClosingParen.isMacroID())
129 Lexer::getLocForEndOfToken(ClosingParen, 0, SM, LangOpts);
132 std::pair<FileID, unsigned>
Loc = SM.getDecomposedLoc(Result);
133 StringRef File = SM.getBufferData(Loc.first);
134 const char *TokenBegin = File.data() + Loc.second;
135 Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(),
136 TokenBegin, File.end());
138 while (!Lexer.LexFromRawLexer(T)) {
139 if (T.is(tok::raw_identifier)) {
140 IdentifierInfo &
Info = Ctx.Idents.get(
141 StringRef(SM.getCharacterData(T.getLocation()), T.getLength()));
142 T.setIdentifierInfo(&Info);
143 T.setKind(Info.getTokenID());
146 if (T.isOneOf(tok::amp, tok::ampamp, tok::kw_const, tok::kw_volatile,
148 Result = T.getEndLoc();
157 return T.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict);
161 return T.isOneOf(tok::kw_constexpr, tok::kw_inline, tok::kw_extern,
162 tok::kw_static, tok::kw_friend, tok::kw_virtual);
165 static llvm::Optional<ClassifiedToken>
171 bool ContainsQualifiers =
false;
172 bool ContainsSpecifiers =
false;
173 bool ContainsSomethingElse =
false;
176 End.setKind(tok::eof);
177 SmallVector<Token, 2> Stream{Tok, End};
180 PP.EnterTokenStream(Stream,
false,
false);
187 bool Qual =
IsCVR(T);
191 ContainsQualifiers |= Qual;
192 ContainsSpecifiers |= Spec;
193 ContainsSomethingElse |= !Qual && !Spec;
198 if (ContainsQualifiers + ContainsSpecifiers + ContainsSomethingElse > 1)
204 llvm::Optional<SmallVector<ClassifiedToken, 8>>
205 UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
206 const FunctionDecl &F,
const ASTContext &Ctx,
const SourceManager &SM,
207 const LangOptions &LangOpts) {
212 std::pair<FileID, unsigned>
Loc = SM.getDecomposedLoc(BeginF);
213 StringRef File = SM.getBufferData(Loc.first);
214 const char *TokenBegin = File.data() + Loc.second;
215 Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(),
216 TokenBegin, File.end());
218 SmallVector<ClassifiedToken, 8> ClassifiedTokens;
219 while (!Lexer.LexFromRawLexer(T) &&
220 SM.isBeforeInTranslationUnit(T.getLocation(), BeginNameF)) {
221 if (T.is(tok::raw_identifier)) {
222 IdentifierInfo &
Info = Ctx.Idents.get(
223 StringRef(SM.getCharacterData(T.getLocation()), T.getLength()));
225 if (Info.hasMacroDefinition()) {
226 const MacroInfo *MI = PP->getMacroInfo(&Info);
227 if (!MI || MI->isFunctionLike()) {
229 diag(F.getLocation(),
Message);
234 T.setIdentifierInfo(&Info);
235 T.setKind(Info.getTokenID());
238 if (llvm::Optional<ClassifiedToken> CT =
classifyToken(F, *PP, T))
239 ClassifiedTokens.push_back(*CT);
241 diag(F.getLocation(),
Message);
246 return ClassifiedTokens;
250 bool Result = Type.hasLocalQualifiers();
251 if (Type->isPointerType())
253 Type->castAs<PointerType>()->getPointeeType());
254 if (Type->isReferenceType())
256 Type->castAs<ReferenceType>()->getPointeeType());
260 SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
261 const FunctionDecl &F,
const ASTContext &Ctx,
const SourceManager &SM,
262 const LangOptions &LangOpts) {
266 SourceRange ReturnTypeRange = F.getReturnTypeSourceRange();
267 if (ReturnTypeRange.isInvalid()) {
270 diag(F.getLocation(),
Message);
276 return ReturnTypeRange;
279 llvm::Optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
280 classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
283 const SmallVector<ClassifiedToken, 8> &
Tokens = *MaybeTokens;
285 ReturnTypeRange.setBegin(
expandIfMacroId(ReturnTypeRange.getBegin(), SM));
288 bool ExtendedLeft =
false;
289 for (
size_t I = 0; I < Tokens.size(); I++) {
291 if (!SM.isBeforeInTranslationUnit(Tokens[I].T.getLocation(),
292 ReturnTypeRange.getBegin()) &&
294 assert(I <=
size_t(std::numeric_limits<int>::max()) &&
295 "Integer overflow detected");
296 for (
int J = static_cast<int>(I) - 1; J >= 0 && Tokens[J].isQualifier;
298 ReturnTypeRange.setBegin(Tokens[J].T.getLocation());
302 if (SM.isBeforeInTranslationUnit(ReturnTypeRange.getEnd(),
303 Tokens[I].T.getLocation())) {
304 for (
size_t J = I; J < Tokens.size() && Tokens[J].isQualifier; J++)
305 ReturnTypeRange.setEnd(Tokens[J].T.getLocation());
310 assert(!ReturnTypeRange.getBegin().isMacroID() &&
311 "Return type source range begin must not be a macro");
312 assert(!ReturnTypeRange.getEnd().isMacroID() &&
313 "Return type source range end must not be a macro");
314 return ReturnTypeRange;
317 bool UseTrailingReturnTypeCheck::keepSpecifiers(
318 std::string &
ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange,
319 const FunctionDecl &F,
const FriendDecl *Fr,
const ASTContext &Ctx,
320 const SourceManager &SM,
const LangOptions &LangOpts) {
323 const auto *M = dyn_cast<CXXMethodDecl>(&F);
324 if (!F.isConstexpr() && !F.isInlineSpecified() &&
325 F.getStorageClass() != SC_Extern && F.getStorageClass() != SC_Static &&
326 !Fr && !(M && M->isVirtualAsWritten()))
331 llvm::Optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
332 classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
337 unsigned int ReturnTypeBeginOffset =
338 SM.getDecomposedLoc(ReturnTypeCVRange.getBegin()).second;
339 size_t InitialAutoLength = Auto.size();
340 unsigned int DeletedChars = 0;
342 if (SM.isBeforeInTranslationUnit(CT.T.getLocation(),
343 ReturnTypeCVRange.getBegin()) ||
344 SM.isBeforeInTranslationUnit(ReturnTypeCVRange.getEnd(),
352 unsigned int TOffset = SM.getDecomposedLoc(CT.T.getLocation()).second;
353 assert(TOffset >= ReturnTypeBeginOffset &&
354 "Token location must be after the beginning of the return type");
355 unsigned int TOffsetInRT = TOffset - ReturnTypeBeginOffset - DeletedChars;
356 unsigned int TLengthWithWS = CT.T.getLength();
357 while (TOffsetInRT + TLengthWithWS < ReturnType.size() &&
358 std::isspace(ReturnType[TOffsetInRT + TLengthWithWS]))
360 std::string Specifier = ReturnType.substr(TOffsetInRT, TLengthWithWS);
361 if (!std::isspace(Specifier.back()))
362 Specifier.push_back(
' ');
363 Auto.insert(Auto.size() - InitialAutoLength, Specifier);
364 ReturnType.erase(TOffsetInRT, TLengthWithWS);
365 DeletedChars += TLengthWithWS;
371 void UseTrailingReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
372 if (!getLangOpts().CPlusPlus11)
375 auto F = functionDecl(unless(anyOf(hasTrailingReturn(), returns(voidType()),
376 returns(autoType()), cxxConversionDecl(),
377 cxxMethodDecl(isImplicit()))))
380 Finder->addMatcher(F,
this);
381 Finder->addMatcher(friendDecl(hasDescendant(F)).bind(
"Friend"),
this);
384 void UseTrailingReturnTypeCheck::registerPPCallbacks(
385 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
389 void UseTrailingReturnTypeCheck::check(
const MatchFinder::MatchResult &
Result) {
390 assert(PP &&
"Expected registerPPCallbacks() to have been called before so " 391 "preprocessor is available");
393 const auto *F = Result.Nodes.getNodeAs<FunctionDecl>(
"Func");
394 const auto *Fr = Result.Nodes.getNodeAs<FriendDecl>(
"Friend");
395 assert(F &&
"Matcher is expected to find only FunctionDecls");
397 if (F->getLocation().isInvalid())
401 if (F->getDeclaredReturnType()->isFunctionPointerType() ||
402 F->getDeclaredReturnType()->isMemberFunctionPointerType() ||
403 F->getDeclaredReturnType()->isMemberPointerType() ||
404 F->getDeclaredReturnType()->getAs<DecltypeType>() !=
nullptr) {
405 diag(F->getLocation(),
Message);
409 const ASTContext &Ctx = *Result.Context;
410 const SourceManager &SM = *Result.SourceManager;
411 const LangOptions &LangOpts = getLangOpts();
413 const TypeSourceInfo *TSI = F->getTypeSourceInfo();
417 FunctionTypeLoc FTL =
418 TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
423 diag(F->getLocation(),
Message);
427 SourceLocation InsertionLoc =
428 findTrailingReturnTypeSourceLocation(*F, FTL, Ctx, SM, LangOpts);
429 if (InsertionLoc.isInvalid()) {
430 diag(F->getLocation(),
Message);
437 SourceRange ReturnTypeCVRange =
438 findReturnTypeAndCVSourceRange(*F, Ctx, SM, LangOpts);
439 if (ReturnTypeCVRange.isInvalid())
450 UnqualNameVisitor UNV{*F};
451 UNV.TraverseTypeLoc(FTL.getReturnLoc());
453 diag(F->getLocation(),
Message);
457 SourceLocation ReturnTypeEnd =
458 Lexer::getLocForEndOfToken(ReturnTypeCVRange.getEnd(), 0, SM, LangOpts);
459 StringRef CharAfterReturnType = Lexer::getSourceText(
460 CharSourceRange::getCharRange(ReturnTypeEnd,
461 ReturnTypeEnd.getLocWithOffset(1)),
463 bool NeedSpaceAfterAuto =
464 CharAfterReturnType.empty() || !std::isspace(CharAfterReturnType[0]);
466 std::string Auto = NeedSpaceAfterAuto ?
"auto " :
"auto";
467 std::string ReturnType = tooling::fixit::getText(ReturnTypeCVRange, Ctx);
468 keepSpecifiers(ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM,
471 diag(F->getLocation(),
Message)
472 << FixItHint::CreateReplacement(ReturnTypeCVRange, Auto)
473 << FixItHint::CreateInsertion(InsertionLoc,
" -> " + ReturnType);
SourceLocation Loc
'#' location in the include directive
static SourceLocation expandIfMacroId(SourceLocation Loc, const SourceManager &SM)
llvm::SmallVector< uint64_t, 1024 > Record
static bool IsSpecifier(Token T)
constexpr llvm::StringLiteral Message
static bool IsCVR(Token T)
static bool hasAnyNestedLocalQualifiers(QualType Type)
static llvm::Optional< ClassifiedToken > classifyToken(const FunctionDecl &F, Preprocessor &PP, Token Tok)
static constexpr llvm::StringLiteral Name
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result