<div dir="ltr"><div dir="ltr"><div>I'm trying to write a "use [[nodiscard]]" clang-tidy checker/fix-it to advise when a function might benefit from having [[nodiscard]] added </div><div><br></div><div>Mostly this is working working except I'm finding the wrong location for the place to put the [[nodiscard]] for functions which have const,inline or virtual kewords infront of the return type. </div><div><br></div><div>e.g. </div><div><br></div><div>test.cxx:18:5: warning: function '
empty ' should be made [[nodiscard]] [modernize-use-nodiscard] </div><div> bool empty() const </div><div> ^ </div><div> [[nodiscard]] </div><div>test.cxx:26:5: warning: function '
empty' should be made [[nodiscard]] [modernize-use-nodiscard] </div><div> bool
empty(const A &val) const </div><div> ^ </div><div> [[nodiscard]] </div><div>test.cxx:50:11: warning: function '
empty' should be made [[nodiscard]] [modernize-use-nodiscard] </div><div> const bool
empty() const </div><div> ^ </div><div> [[nodiscard]] </div><div>test.cxx:55:18: warning: function '
empty ' should be made [[nodiscard]] [modernize-use-nodiscard] </div><div> inline const bool
empty() const </div><div> ^ </div><div> [[nodiscard]] </div><div><br></div><div>And some compilers don't like the [[nodiscard] being placed in between those keywords and the return type (preferring them to be at the front of the function) </div><div><br></div><div>I'm currently using (following a series of trail and errors) to find the location just before the return type</div><div>MatchedDecl->getReturnTypeSourceRange().getBegin()</div><div><br></div><div>e.g. </div><div><br></div><div>// This function could be marked [[nodiscard]] </div><div> diag(MatchedDecl->getReturnTypeSourceRange().getBegin(), </div><div> "function %0 should be marked [[nodiscard]]") </div><div> << MatchedDecl </div><div> << FixItHint::CreateInsertion( </div><div> MatchedDecl->getReturnTypeSourceRange().getBegin(), </div><div> "[[nodiscard]] "); </div><div><br></div><div><br></div><div>Does anyone know how I should correctly identify the position in front of all the keywords(virtual,inline,const)? </div><div><br></div><div>Especially in the case where the return type is on a different source code line </div><div><br></div><div>e.g. </div><div>virtual </div><div>const bool full() </div><div><br></div><div>Many thanks in advance, feel free to point out any other "errors of my ways" as this is my first ever checker.</div><div><br></div><div>MyDeveloperDay </div><div><br></div><div>a current copy of the current checker code is presented here for completeness </div><div>------------------------------------------------------------------------------ </div><div>namespace clang { </div><div>namespace tidy { </div><div>namespace modernize { </div><div><br></div><div>void UseNodiscardCheck::registerMatchers(MatchFinder *Finder) { </div><div><br></div><div> Finder->addMatcher( </div><div> cxxMethodDecl(eachOf(unless(returns(voidType())), (isConst()))) </div><div> .bind("noDiscardCandidate"), </div><div> this); </div><div>} </div><div><br></div><div>static bool isNonConstReferenceType(QualType ParamType) { </div><div> return ParamType->isReferenceType() && </div><div> !ParamType.getNonReferenceType().isConstQualified(); </div><div>} </div><div><br></div><div>static bool isOperator(const FunctionDecl *D) { </div><div> return D->getNameAsString().find("operator") != std::string::npos; </div><div>} </div><div><br></div><div>static bool isInternalFunction(const FunctionDecl *D) { </div><div> return D->getNameAsString().find("_") == 0; </div><div>} </div><div><br></div><div>void UseNodiscardCheck::check(const MatchFinder::MatchResult &Result) { </div><div> const auto *MatchedDecl = </div><div> Result.Nodes.getNodeAs<CXXMethodDecl>("noDiscardCandidate"); </div><div><br></div><div> // if the localtion is invalid forget it </div><div> if (!MatchedDecl->getLocation().isValid()) </div><div> return; </div><div><br></div><div> // does it already have [[nodiscard]] </div><div> if (MatchedDecl->hasUnusedResultAttr()) </div><div> return; </div><div><br></div><div> // if the function is const it can't be modifying </div><div> // something locally so more likely the result is important </div><div> if (!MatchedDecl->isConst()) </div><div> return; </div><div><br></div><div> // if the function is a void or is marked no return then </div><div> // its not a canidate </div><div> if (MatchedDecl->isNoReturn() || </div><div> MatchedDecl->getReturnType().getAsString() == "void") </div><div> return; </div><div><br></div><div> // don't add [[nodiscard]] to any operators </div><div> if (isOperator(MatchedDecl)) </div><div> return; </div><div><br></div><div> // don't add [[nodiscard]] to anything marked as _Foo() </div><div> if (isInternalFunction(MatchedDecl)) </div><div> return; </div><div><br></div><div> // if the function has any non constant reference parameters </div><div> // e.g. foo(A &a) </div><div> // the may not care about the return because we may be passing data </div><div> // via the non const reference </div><div> // functions with no arguments will fall through foo() </div><div> for (const auto *Par : MatchedDecl->parameters()) { </div><div> const Type *ParType = Par->getType().getTypePtr(); </div><div><br></div><div> if (isNonConstReferenceType(Par->getType())) { </div><div> return; </div><div> } </div><div> } </div><div><br></div><div> // This function could be marked [[nodiscard]] </div><div> diag(MatchedDecl->getReturnTypeSourceRange().getBegin(), </div><div> "function %0 should be marked [[nodiscard]]") </div><div> << MatchedDecl </div><div> << FixItHint::CreateInsertion( </div><div> MatchedDecl->getReturnTypeSourceRange().getBegin(), </div><div> "[[nodiscard]] "); </div><div> return; </div><div>} </div><div><br></div><div>} // namespace modernize </div><div>} // namespace tidy </div><div>} // namespace clang </div></div></div>