Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9182,9 +9182,6 @@ def warn_unused_constructor : Warning<
def warn_unused_constructor_msg : Warning<
"ignoring temporary created by a constructor declared with %0 attribute: %1">,
InGroup<UnusedValue>;
def warn_discarded_class_member_access : Warning<
"left operand of dot in this class member access is discarded and has no effect">,
InGroup<UnusedValue>;
def warn_side_effects_unevaluated_context : Warning<
"expression with side effects has no effect in an unevaluated context">,
InGroup<UnevaluatedExpression>;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8502,6 +8502,11 @@ class Sema final : public SemaBase {
SourceLocation EndLoc);
void ActOnForEachDeclStmt(DeclGroupPtrTy Decl);

/// DiagnoseDiscardedNodiscard - Given an expression that is semantically
/// a discarded-value expression, diagnose if any [[nodiscard]] value
/// has been discarded
void DiagnoseDiscardedNodiscard(const Expr *E);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we find a better name for that function?


/// DiagnoseUnusedExprResult - If the statement passed in is an expression
/// whose result is unused, warn.
void DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
case ExprWithCleanupsClass:
return cast<ExprWithCleanups>(this)->getSubExpr()
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
case OpaqueValueExprClass:
return cast<OpaqueValueExpr>(this)->getSourceExpr()->isUnusedResultAWarning(
WarnE, Loc, R1, R2, Ctx);
}
}

Expand Down
16 changes: 8 additions & 8 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
// an enumerator, the first expression is a discarded-value expression; if
// the id-expression names a non-static data member, the first expression
// shall be a glvalue.
auto MakeDiscardedValue = [&BaseExpr, IsArrow, this] {
auto MakeDiscardedValue = [&] {
assert(getLangOpts().CPlusPlus &&
"Static member / member enumerator outside of C++");
if (IsArrow)
Expand All @@ -1145,11 +1145,10 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
if (Converted.isInvalid())
return true;
BaseExpr = Converted.get();
DiagnoseUnusedExprResult(BaseExpr,
diag::warn_discarded_class_member_access);
DiagnoseDiscardedNodiscard(BaseExpr);
return false;
};
auto MakeGLValue = [&BaseExpr, IsArrow, this] {
auto MakeGLValue = [&] {
if (IsArrow || !BaseExpr->isPRValue())
return false;
ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
Expand All @@ -1171,10 +1170,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
}

if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl)) {
// Properties treated as non-static data members for the purpose of
// temporary materialization
if (MakeGLValue())
return ExprError();
// No temporaries are materialized for property references yet.
// They might be materialized when this is transformed into a member call.
// Note that this is slightly different behaviour from MSVC which doesn't
// implement CWG2813 yet: MSVC might materialize an extra temporary if the
// getter or setter function is an explicit object member function.
return BuildMSPropertyRefExpr(*this, BaseExpr, IsArrow, SS, PD,
MemberNameInfo);
}
Expand Down
114 changes: 63 additions & 51 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,18 +222,13 @@ static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2;
}

void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
const unsigned OrigDiagID = DiagID;
if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S))
return DiagnoseUnusedExprResult(Label->getSubStmt(), DiagID);

const Expr *E = dyn_cast_or_null<Expr>(S);
if (!E)
return;
namespace {
void DiagnoseUnused(Sema &S, const Expr *E, std::optional<unsigned> DiagID) {
bool NoDiscardOnly = !DiagID.has_value();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment explaining the relationship between the DiagID parameter and NoDiscardOnly here would be nice so anyone looking at calls to this knows what’s going on w/o having to go through the entire function.


// If we are in an unevaluated expression context, then there can be no unused
// results because the results aren't expected to be used in the first place.
if (isUnevaluatedContext())
if (S.isUnevaluatedContext())
return;

SourceLocation ExprLoc = E->IgnoreParenImpCasts()->getExprLoc();
Expand All @@ -242,30 +237,31 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
// expression is a call to a function with the warn_unused_result attribute,
// we warn no matter the location. Because of the order in which the various
// checks need to happen, we factor out the macro-related test here.
bool ShouldSuppress =
SourceMgr.isMacroBodyExpansion(ExprLoc) ||
SourceMgr.isInSystemMacro(ExprLoc);
bool ShouldSuppress = S.SourceMgr.isMacroBodyExpansion(ExprLoc) ||
S.SourceMgr.isInSystemMacro(ExprLoc);

const Expr *WarnExpr;
SourceLocation Loc;
SourceRange R1, R2;
if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context))
if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, S.Context))
return;

// If this is a GNU statement expression expanded from a macro, it is probably
// unused because it is a function-like macro that can be used as either an
// expression or statement. Don't warn, because it is almost certainly a
// false positive.
if (isa<StmtExpr>(E) && Loc.isMacroID())
return;

// Check if this is the UNREFERENCED_PARAMETER from the Microsoft headers.
// That macro is frequently used to suppress "unused parameter" warnings,
// but its implementation makes clang's -Wunused-value fire. Prevent this.
if (isa<ParenExpr>(E->IgnoreImpCasts()) && Loc.isMacroID()) {
SourceLocation SpellLoc = Loc;
if (findMacroSpelling(SpellLoc, "UNREFERENCED_PARAMETER"))
if (!NoDiscardOnly) {
// If this is a GNU statement expression expanded from a macro, it is
// probably unused because it is a function-like macro that can be used as
// either an expression or statement. Don't warn, because it is almost
// certainly a false positive.
if (isa<StmtExpr>(E) && Loc.isMacroID())
return;

// Check if this is the UNREFERENCED_PARAMETER from the Microsoft headers.
// That macro is frequently used to suppress "unused parameter" warnings,
// but its implementation makes clang's -Wunused-value fire. Prevent this.
if (isa<ParenExpr>(E->IgnoreImpCasts()) && Loc.isMacroID()) {
SourceLocation SpellLoc = Loc;
if (S.findMacroSpelling(SpellLoc, "UNREFERENCED_PARAMETER"))
return;
}
}

// Okay, we have an unused result. Depending on what the base expression is,
Expand All @@ -276,7 +272,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (const CXXBindTemporaryExpr *TempExpr = dyn_cast<CXXBindTemporaryExpr>(E))
E = TempExpr->getSubExpr();

if (DiagnoseUnusedComparison(*this, E))
if (DiagnoseUnusedComparison(S, E))
return;

E = WarnExpr;
Expand All @@ -289,8 +285,9 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (E->getType()->isVoidType())
return;

if (DiagnoseNoDiscard(*this, cast_or_null<WarnUnusedResultAttr>(
CE->getUnusedResultAttr(Context)),
if (DiagnoseNoDiscard(S,
cast_if_present<WarnUnusedResultAttr>(
CE->getUnusedResultAttr(S.Context)),
Loc, R1, R2, /*isCtor=*/false))
return;

Expand All @@ -302,50 +299,50 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (ShouldSuppress)
return;
if (FD->hasAttr<PureAttr>()) {
Diag(Loc, diag::warn_unused_call) << R1 << R2 << "pure";
S.Diag(Loc, diag::warn_unused_call) << R1 << R2 << "pure";
return;
}
if (FD->hasAttr<ConstAttr>()) {
Diag(Loc, diag::warn_unused_call) << R1 << R2 << "const";
S.Diag(Loc, diag::warn_unused_call) << R1 << R2 << "const";
return;
}
}
} else if (const auto *CE = dyn_cast<CXXConstructExpr>(E)) {
if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
const auto *A = Ctor->getAttr<WarnUnusedResultAttr>();
A = A ? A : Ctor->getParent()->getAttr<WarnUnusedResultAttr>();
if (DiagnoseNoDiscard(*this, A, Loc, R1, R2, /*isCtor=*/true))
if (DiagnoseNoDiscard(S, A, Loc, R1, R2, /*isCtor=*/true))
return;
}
} else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) {

if (DiagnoseNoDiscard(*this, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
R2, /*isCtor=*/false))
if (DiagnoseNoDiscard(S, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1, R2,
/*isCtor=*/false))
return;
}
} else if (ShouldSuppress)
return;

E = WarnExpr;
if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
if (getLangOpts().ObjCAutoRefCount && ME->isDelegateInitCall()) {
Diag(Loc, diag::err_arc_unused_init_message) << R1;
if (S.getLangOpts().ObjCAutoRefCount && ME->isDelegateInitCall()) {
S.Diag(Loc, diag::err_arc_unused_init_message) << R1;
return;
}
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD) {
if (DiagnoseNoDiscard(*this, MD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
R2, /*isCtor=*/false))
if (DiagnoseNoDiscard(S, MD->getAttr<WarnUnusedResultAttr>(), Loc, R1, R2,
/*isCtor=*/false))
return;
}
} else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
const Expr *Source = POE->getSyntacticForm();
// Handle the actually selected call of an OpenMP specialized call.
if (LangOpts.OpenMP && isa<CallExpr>(Source) &&
if (S.LangOpts.OpenMP && isa<CallExpr>(Source) &&
POE->getNumSemanticExprs() == 1 &&
isa<CallExpr>(POE->getSemanticExpr(0)))
return DiagnoseUnusedExprResult(POE->getSemanticExpr(0), DiagID);
return DiagnoseUnused(S, POE->getSemanticExpr(0), DiagID);
if (isa<ObjCSubscriptRefExpr>(Source))
DiagID = diag::warn_unused_container_subscript_expr;
else if (isa<ObjCPropertyRefExpr>(Source))
Expand All @@ -362,17 +359,21 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (!RD->getAttr<WarnUnusedAttr>())
return;
}

if (NoDiscardOnly)
return;

// Diagnose "(void*) blah" as a typo for "(void) blah".
else if (const CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(E)) {
if (const CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(E)) {
TypeSourceInfo *TI = CE->getTypeInfoAsWritten();
QualType T = TI->getType();

// We really do want to use the non-canonical type here.
if (T == Context.VoidPtrTy) {
if (T == S.Context.VoidPtrTy) {
PointerTypeLoc TL = TI->getTypeLoc().castAs<PointerTypeLoc>();

Diag(Loc, diag::warn_unused_voidptr)
<< FixItHint::CreateRemoval(TL.getStarLoc());
S.Diag(Loc, diag::warn_unused_voidptr)
<< FixItHint::CreateRemoval(TL.getStarLoc());
return;
}
}
Expand All @@ -381,23 +382,34 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
// isn't an array.
if (E->isGLValue() && E->getType().isVolatileQualified() &&
!E->getType()->isArrayType()) {
Diag(Loc, diag::warn_unused_volatile) << R1 << R2;
S.Diag(Loc, diag::warn_unused_volatile) << R1 << R2;
return;
}

// Do not diagnose use of a comma operator in a SFINAE context because the
// type of the left operand could be used for SFINAE, so technically it is
// *used*.
if (DiagID == diag::warn_unused_comma_left_operand && isSFINAEContext())
if (DiagID == diag::warn_unused_comma_left_operand && S.isSFINAEContext())
return;

// Don't diagnose discarded left of dot in static class member access
// because its type is "used" to determine the class to access
if (OrigDiagID == diag::warn_discarded_class_member_access)
S.DiagIfReachable(Loc, llvm::ArrayRef<const Stmt *>(E),
S.PDiag(*DiagID) << R1 << R2);
}
} // namespace

void Sema::DiagnoseDiscardedNodiscard(const Expr *E) {
DiagnoseUnused(*this, E, std::nullopt);
}

void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (const LabelStmt *Label = dyn_cast_if_present<LabelStmt>(S))
S = Label->getSubStmt();

const Expr *E = dyn_cast_if_present<Expr>(S);
if (!E)
return;

DiagIfReachable(Loc, S ? llvm::ArrayRef(S) : std::nullopt,
PDiag(DiagID) << R1 << R2);
DiagnoseUnused(*this, E, DiagID);
}

void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {
Expand Down
Loading