forked from OSchip/llvm-project
152 lines
5.7 KiB
C++
152 lines
5.7 KiB
C++
//===--- UseNodiscardCheck.cpp - clang-tidy -------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "UseNodiscardCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace modernize {
|
|
|
|
static bool doesNoDiscardMacroExist(ASTContext &Context,
|
|
const llvm::StringRef &MacroId) {
|
|
// Don't check for the Macro existence if we are using an attribute
|
|
// either a C++17 standard attribute or pre C++17 syntax
|
|
if (MacroId.startswith("[[") || MacroId.startswith("__attribute__"))
|
|
return true;
|
|
|
|
// Otherwise look up the macro name in the context to see if its defined.
|
|
return Context.Idents.get(MacroId).hasMacroDefinition();
|
|
}
|
|
|
|
namespace {
|
|
AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
|
|
// Don't put ``[[nodiscard]]`` in front of operators.
|
|
return Node.isOverloadedOperator();
|
|
}
|
|
AST_MATCHER(CXXMethodDecl, isConversionOperator) {
|
|
// Don't put ``[[nodiscard]]`` in front of a conversion decl
|
|
// like operator bool().
|
|
return isa<CXXConversionDecl>(Node);
|
|
}
|
|
AST_MATCHER(CXXMethodDecl, hasClassMutableFields) {
|
|
// Don't put ``[[nodiscard]]`` on functions on classes with
|
|
// mutable member variables.
|
|
return Node.getParent()->hasMutableFields();
|
|
}
|
|
AST_MATCHER(ParmVarDecl, hasParameterPack) {
|
|
// Don't put ``[[nodiscard]]`` on functions with parameter pack arguments.
|
|
return Node.isParameterPack();
|
|
}
|
|
AST_MATCHER(CXXMethodDecl, hasTemplateReturnType) {
|
|
// Don't put ``[[nodiscard]]`` in front of functions returning a template
|
|
// type.
|
|
return Node.getReturnType()->isTemplateTypeParmType() ||
|
|
Node.getReturnType()->isInstantiationDependentType();
|
|
}
|
|
AST_MATCHER(CXXMethodDecl, isDefinitionOrInline) {
|
|
// A function definition, with optional inline but not the declaration.
|
|
return !(Node.isThisDeclarationADefinition() && Node.isOutOfLine());
|
|
}
|
|
AST_MATCHER(QualType, isInstantiationDependentType) {
|
|
return Node->isInstantiationDependentType();
|
|
}
|
|
AST_MATCHER(QualType, isNonConstReferenceOrPointer) {
|
|
// If the function has any non-const-reference arguments
|
|
// bool foo(A &a)
|
|
// or pointer arguments
|
|
// bool foo(A*)
|
|
// then they may not care about the return value because of passing data
|
|
// via the arguments.
|
|
return (Node->isTemplateTypeParmType() || Node->isPointerType() ||
|
|
(Node->isReferenceType() &&
|
|
!Node.getNonReferenceType().isConstQualified()) ||
|
|
Node->isInstantiationDependentType());
|
|
}
|
|
} // namespace
|
|
|
|
UseNodiscardCheck::UseNodiscardCheck(StringRef Name, ClangTidyContext *Context)
|
|
: ClangTidyCheck(Name, Context),
|
|
NoDiscardMacro(Options.get("ReplacementString", "[[nodiscard]]")) {}
|
|
|
|
void UseNodiscardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|
Options.store(Opts, "ReplacementString", NoDiscardMacro);
|
|
}
|
|
|
|
void UseNodiscardCheck::registerMatchers(MatchFinder *Finder) {
|
|
auto FunctionObj =
|
|
cxxRecordDecl(hasAnyName("::std::function", "::boost::function"));
|
|
|
|
// Find all non-void const methods which have not already been marked to
|
|
// warn on unused result.
|
|
Finder->addMatcher(
|
|
cxxMethodDecl(
|
|
allOf(isConst(), isDefinitionOrInline(),
|
|
unless(anyOf(
|
|
returns(voidType()),
|
|
returns(hasDeclaration(decl(hasAttr(clang::attr::WarnUnusedResult)))),
|
|
isNoReturn(), isOverloadedOperator(),
|
|
isVariadic(), hasTemplateReturnType(),
|
|
hasClassMutableFields(), isConversionOperator(),
|
|
hasAttr(clang::attr::WarnUnusedResult),
|
|
hasType(isInstantiationDependentType()),
|
|
hasAnyParameter(anyOf(
|
|
parmVarDecl(anyOf(hasType(FunctionObj),
|
|
hasType(references(FunctionObj)))),
|
|
hasType(isNonConstReferenceOrPointer()),
|
|
hasParameterPack()))))))
|
|
.bind("no_discard"),
|
|
this);
|
|
}
|
|
|
|
void UseNodiscardCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("no_discard");
|
|
// Don't make replacements if the location is invalid or in a macro.
|
|
SourceLocation Loc = MatchedDecl->getLocation();
|
|
if (Loc.isInvalid() || Loc.isMacroID())
|
|
return;
|
|
|
|
SourceLocation RetLoc = MatchedDecl->getInnerLocStart();
|
|
|
|
ASTContext &Context = *Result.Context;
|
|
|
|
auto Diag = diag(RetLoc, "function %0 should be marked %1")
|
|
<< MatchedDecl << NoDiscardMacro;
|
|
|
|
// Check for the existence of the keyword being used as the ``[[nodiscard]]``.
|
|
if (!doesNoDiscardMacroExist(Context, NoDiscardMacro))
|
|
return;
|
|
|
|
// Possible false positives include:
|
|
// 1. A const member function which returns a variable which is ignored
|
|
// but performs some external I/O operation and the return value could be
|
|
// ignored.
|
|
Diag << FixItHint::CreateInsertion(RetLoc, NoDiscardMacro + " ");
|
|
}
|
|
|
|
bool UseNodiscardCheck::isLanguageVersionSupported(
|
|
const LangOptions &LangOpts) const {
|
|
// If we use ``[[nodiscard]]`` attribute, we require at least C++17. Use a
|
|
// macro or ``__attribute__`` with pre c++17 compilers by using
|
|
// ReplacementString option.
|
|
|
|
if (NoDiscardMacro == "[[nodiscard]]")
|
|
return LangOpts.CPlusPlus17;
|
|
|
|
return LangOpts.CPlusPlus;
|
|
}
|
|
|
|
} // namespace modernize
|
|
} // namespace tidy
|
|
} // namespace clang
|