Files
swift-mirror/lib/Sema/MiscDiagnostics.cpp
Jordan Rose 8f520414b7 [Accessibility] A private class cannot contain public methods.
There's no meaningful way in which these methods are public, since they
can't be accessed through any value of the type

<rdar://problem/17647878>

Swift SVN r20224
2014-07-20 17:26:24 +00:00

624 lines
22 KiB
C++

//===--- MiscDiagnostics.cpp - AST-Level Diagnostics ----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements AST-level diagnostics.
//
//===----------------------------------------------------------------------===//
#include "MiscDiagnostics.h"
#include "TypeChecker.h"
#include "swift/Basic/SourceManager.h"
#include "swift/AST/ASTWalker.h"
#include "swift/Parse/Lexer.h"
using namespace swift;
//===--------------------------------------------------------------------===//
// Diagnose assigning variable to itself.
//===--------------------------------------------------------------------===//
static Decl *findSimpleReferencedDecl(const Expr *E) {
if (auto *LE = dyn_cast<LoadExpr>(E))
E = LE->getSubExpr();
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl();
return nullptr;
}
static std::pair<Decl *, Decl *> findReferencedDecl(const Expr *E) {
if (auto *LE = dyn_cast<LoadExpr>(E))
E = LE->getSubExpr();
if (auto *D = findSimpleReferencedDecl(E))
return std::make_pair(nullptr, D);
if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
if (auto *BaseDecl = findSimpleReferencedDecl(MRE->getBase()))
return std::make_pair(BaseDecl, MRE->getMember().getDecl());
}
return std::make_pair(nullptr, nullptr);
}
/// Diagnose assigning variable to itself.
static void diagSelfAssignment(TypeChecker &TC, const Expr *E) {
auto *AE = dyn_cast<AssignExpr>(E);
if (!AE)
return;
auto LHSDecl = findReferencedDecl(AE->getDest());
auto RHSDecl = findReferencedDecl(AE->getSrc());
if (LHSDecl.second && LHSDecl == RHSDecl) {
TC.diagnose(AE->getLoc(), LHSDecl.first ? diag::self_assignment_prop
: diag::self_assignment_var)
.highlight(AE->getDest()->getSourceRange())
.highlight(AE->getSrc()->getSourceRange());
}
}
/// Issue a warning on code where a returned expression is on a different line
/// than the return keyword, but both have the same indentation.
///
/// \code
/// ...
/// return
/// foo()
/// \endcode
static void diagUnreachableCode(TypeChecker &TC, const Stmt *S) {
auto *RS = dyn_cast<ReturnStmt>(S);
if (!RS)
return;
if (!RS->hasResult())
return;
auto RetExpr = RS->getResult();
auto RSLoc = RS->getStartLoc();
auto RetExprLoc = RetExpr->getStartLoc();
// FIXME: Expose getColumnNumber() in LLVM SourceMgr to make this check
// cheaper.
if (RSLoc.isInvalid() || RetExprLoc.isInvalid() || (RSLoc == RetExprLoc))
return;
SourceManager &SM = TC.Context.SourceMgr;
if (SM.getLineAndColumn(RSLoc).second ==
SM.getLineAndColumn(RetExprLoc).second) {
TC.diagnose(RetExpr->getStartLoc(), diag::unindented_code_after_return);
TC.diagnose(RetExpr->getStartLoc(), diag::indent_expression_to_silence);
return;
}
return;
}
/// Diagnose use of module or metatype values outside of dot expressions.
static void diagModuleOrMetatypeValue(TypeChecker &TC, const Expr *E) {
class DiagnoseWalker : public ASTWalker {
public:
TypeChecker &TC;
DiagnoseWalker(TypeChecker &TC) : TC(TC) {}
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
// Diagnose module values that don't appear as part of a qualification.
if (auto *ME = dyn_cast<ModuleExpr>(E)) {
bool Diagnose = true;
if (auto *ParentExpr = Parent.getAsExpr()) {
// Allow module values as a part of:
// - ignored base expressions;
// - expressions that failed to type check.
if (isa<DotSyntaxBaseIgnoredExpr>(ParentExpr) ||
isa<UnresolvedDotExpr>(ParentExpr))
Diagnose = false;
}
if (Diagnose)
TC.diagnose(ME->getStartLoc(), diag::value_of_module_type);
return { true, E };
}
// Diagnose metatype values that don't appear as part of a property,
// method, or constructor reference.
// See through implicit conversions.
auto Base = E;
while (auto Conv = dyn_cast<ImplicitConversionExpr>(Base))
Base = Conv->getSubExpr();
auto *DRE = dyn_cast<DeclRefExpr>(Base);
auto *MRE = dyn_cast<MemberRefExpr>(Base);
if ((DRE && isa<TypeDecl>(DRE->getDecl())) ||
(MRE && isa<TypeDecl>(MRE->getMember().getDecl())) ||
isa<TypeExpr>(Base)) {
// Allow references to types as a part of:
// - member references T.foo, T.Type, T.self, etc. (but *not* T.type)
// - constructor calls T()
enum class Diagnostic {
None, // OK
UnqualifiedMetatypeValue, // type named without being accessed
TypeOfMetatypeValue, // .type applied to a type
} Diagnose;
if (auto *ParentExpr = Parent.getAsExpr()) {
switch (ParentExpr->getKind()) {
case ExprKind::Error:
case ExprKind::Call:
case ExprKind::MemberRef:
case ExprKind::DotSelf:
case ExprKind::DotSyntaxCall:
case ExprKind::ConstructorRefCall:
case ExprKind::UnresolvedMember:
case ExprKind::UnresolvedDot:
case ExprKind::UnresolvedSelector:
case ExprKind::UnresolvedSpecialize:
case ExprKind::DotSyntaxBaseIgnored:
Diagnose = Diagnostic::None;
break;
case ExprKind::DynamicType:
Diagnose = Diagnostic::TypeOfMetatypeValue;
break;
case ExprKind::NilLiteral:
case ExprKind::IntegerLiteral:
case ExprKind::FloatLiteral:
case ExprKind::BooleanLiteral:
case ExprKind::CharacterLiteral:
case ExprKind::StringLiteral:
case ExprKind::InterpolatedStringLiteral:
case ExprKind::MagicIdentifierLiteral:
case ExprKind::DiscardAssignment:
case ExprKind::DeclRef:
case ExprKind::SuperRef:
case ExprKind::Type:
case ExprKind::OtherConstructorDeclRef:
case ExprKind::UnresolvedConstructor:
case ExprKind::OverloadedDeclRef:
case ExprKind::OverloadedMemberRef:
case ExprKind::UnresolvedDeclRef:
case ExprKind::DynamicMemberRef:
case ExprKind::DynamicSubscript:
case ExprKind::Sequence:
case ExprKind::Paren:
case ExprKind::Tuple:
case ExprKind::Array:
case ExprKind::Dictionary:
case ExprKind::Subscript:
case ExprKind::TupleElement:
case ExprKind::Closure:
case ExprKind::AutoClosure:
case ExprKind::Module:
case ExprKind::InOut:
case ExprKind::RebindSelfInConstructor:
case ExprKind::OpaqueValue:
case ExprKind::BindOptional:
case ExprKind::OptionalEvaluation:
case ExprKind::ClassMetatypeToObject:
case ExprKind::ProtocolMetatypeToObject:
case ExprKind::ExistentialMetatypeToObject:
case ExprKind::ForceValue:
case ExprKind::OpenExistential:
case ExprKind::PrefixUnary:
case ExprKind::PostfixUnary:
case ExprKind::Binary:
case ExprKind::Load:
case ExprKind::TupleShuffle:
case ExprKind::FunctionConversion:
case ExprKind::CovariantFunctionConversion:
case ExprKind::CovariantReturnConversion:
case ExprKind::MetatypeConversion:
case ExprKind::CollectionUpcastConversion:
case ExprKind::Erasure:
case ExprKind::MetatypeErasure:
case ExprKind::DerivedToBase:
case ExprKind::ArchetypeToSuper:
case ExprKind::ScalarToTuple:
case ExprKind::InjectIntoOptional:
case ExprKind::LValueToPointer:
case ExprKind::UnresolvedCheckedCast:
case ExprKind::ForcedCheckedCast:
case ExprKind::ConditionalCheckedCast:
case ExprKind::Isa:
case ExprKind::Coerce:
case ExprKind::If:
case ExprKind::Assign:
case ExprKind::DefaultValue:
case ExprKind::UnresolvedPattern:
case ExprKind::InOutToPointer:
case ExprKind::StringToPointer:
case ExprKind::ArrayToPointer:
case ExprKind::PointerToPointer:
Diagnose = Diagnostic::UnqualifiedMetatypeValue;
break;
}
} else {
Diagnose = Diagnostic::UnqualifiedMetatypeValue;
}
switch (Diagnose) {
case Diagnostic::None:
break;
case Diagnostic::UnqualifiedMetatypeValue: {
TC.diagnose(E->getStartLoc(), diag::value_of_metatype_type);
// Add fixits to insert '()' or '.self'.
auto endLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr,
E->getEndLoc());
TC.diagnose(endLoc, diag::add_parens_to_type)
.fixItInsert(endLoc, "()");
TC.diagnose(endLoc, diag::add_self_to_type)
.fixItInsert(endLoc, ".self");
break;
}
case Diagnostic::TypeOfMetatypeValue: {
TC.diagnose(E->getStartLoc(), diag::type_of_metatype);
// Add a fixit to replace '.type' with '.self'.
auto metaExpr = cast<DynamicTypeExpr>(Parent.getAsExpr());
auto endLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr,
metaExpr->getMetatypeLoc());
TC.diagnose(metaExpr->getMetatypeLoc(),
diag::add_self_to_type)
.fixItReplaceChars(metaExpr->getMetatypeLoc(),
endLoc, "self");
break;
}
}
// We don't need to visit the children of a type member reference.
return { false, E };
}
return { true, E };
}
};
DiagnoseWalker Walker(TC);
const_cast<Expr *>(E)->walk(Walker);
}
/// Diagnose recursive use of properties within their own accessors
static void diagRecursivePropertyAccess(TypeChecker &TC, const Expr *E,
const DeclContext *DC) {
auto fn = dyn_cast<FuncDecl>(DC);
if (!fn || !fn->isAccessor())
return;
auto var = dyn_cast<VarDecl>(fn->getAccessorStorageDecl());
if (!var) // Ignore subscripts
return;
class DiagnoseWalker : public ASTWalker {
TypeChecker &TC;
VarDecl *Var;
const FuncDecl *Accessor;
public:
explicit DiagnoseWalker(TypeChecker &TC, VarDecl *var,
const FuncDecl *Accessor)
: TC(TC), Var(var), Accessor(Accessor) {}
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
// Handle local and top-level computed variables.
if (DRE->getDecl() == Var &&
!DRE->isDirectPropertyAccess()) {
bool shouldDiagnose = true;
if (auto *ParentExpr = Parent.getAsExpr()) {
if (isa<DotSyntaxBaseIgnoredExpr>(ParentExpr))
shouldDiagnose = false;
else if (Accessor->isSetter())
shouldDiagnose = !isa<LoadExpr>(ParentExpr);
}
if (shouldDiagnose) {
TC.diagnose(E->getLoc(), diag::recursive_accessor_reference,
Var->getName(), Accessor->isSetter());
}
}
// If this is a direct store in a "willSet", we reject this because
// it is about to get overwritten.
if (DRE->getDecl() == Var && DRE->isDirectPropertyAccess() &&
!dyn_cast_or_null<LoadExpr>(Parent.getAsExpr()) &&
Accessor->getAccessorKind() == AccessorKind::IsWillSet) {
TC.diagnose(E->getLoc(), diag::store_in_willset, Var->getName());
}
} else if (auto *MRE = dyn_cast<MemberRefExpr>(E)) {
// Handle instance and type computed variables.
// Find MemberRefExprs that have an implicit "self" base.
if (MRE->getMember().getDecl() == Var &&
isa<DeclRefExpr>(MRE->getBase()) &&
MRE->getBase()->isImplicit()) {
if (!MRE->isDirectPropertyAccess()) {
bool shouldDiagnose = false;
// Warn about any property access in the getter.
if (Accessor->isGetter())
shouldDiagnose = true;
// Warn about stores in the setter, but allow loads.
if (Accessor->isSetter())
shouldDiagnose = !dyn_cast_or_null<LoadExpr>(Parent.getAsExpr());
if (shouldDiagnose) {
TC.diagnose(E->getLoc(), diag::recursive_accessor_reference,
Var->getName(), Accessor->isSetter());
TC.diagnose(E->getLoc(),
diag::recursive_accessor_reference_silence)
.fixItInsert(E->getStartLoc(), "self.");
}
} else {
// If this is a direct store in a "willSet", we reject this because
// it is about to get overwritten.
if (!dyn_cast_or_null<LoadExpr>(Parent.getAsExpr()) &&
Accessor->getAccessorKind() == AccessorKind::IsWillSet) {
TC.diagnose(E->getLoc(), diag::store_in_willset, Var->getName());
}
}
}
} else if (auto *PE = dyn_cast<IdentityExpr>(E)) {
// Look through ParenExprs because a function argument of a single
// rvalue will have a LoadExpr /outside/ the ParenExpr.
return { true, PE->getSubExpr() };
}
return { true, E };
}
};
DiagnoseWalker walker(TC, var, fn);
const_cast<Expr *>(E)->walk(walker);
}
/// Look for any property references in closures that lack a "self." qualifier.
/// Within a closure, we require that the source code contain "self." explicitly
/// because 'self' is captured, not the property value. This is a common source
/// of confusion, so we force an explicit self.
static void diagnoseImplicitSelfUseInClosure(TypeChecker &TC, const Expr *E) {
class DiagnoseWalker : public ASTWalker {
TypeChecker &TC;
unsigned InClosure = 0;
// Keep track of DeclRefExpr's we've emitted diagnostics for, so we don't
// emit the same error on both the property access and on the underlying
// self reference.
SmallVector<Expr*, 2> DiagnosedSelfs;
public:
explicit DiagnoseWalker(TypeChecker &TC) : TC(TC) {}
/// Return true if this is an implicit reference to self.
static bool isImplicitSelfUse(Expr *E) {
auto *DRE = dyn_cast<DeclRefExpr>(E);
return DRE && DRE->isImplicit() && DRE->getDecl()->hasName() &&
DRE->getDecl()->getName().str() == "self";
}
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
// If this is an explicit closure expression - not an autoclosure - then
// we keep track of the fact that recursive walks are within the closure.
if (isa<ClosureExpr>(E))
++InClosure;
// If we see a property reference with an implicit base from within a
// closure, then reject it as requiring an explicit "self." qualifier. We
// do this in explicit closures, not autoclosures, because otherwise the
// transparence of autoclosures is lost.
if (auto *MRE = dyn_cast<MemberRefExpr>(E))
if (InClosure && isImplicitSelfUse(MRE->getBase())) {
TC.diagnose(MRE->getLoc(),
diag::property_use_in_closure_without_explicit_self,
MRE->getMember().getDecl()->getName())
.fixItInsert(MRE->getLoc(), "self.");
DiagnosedSelfs.push_back(MRE->getBase());
}
// Handle method calls with a specific diagnostic + fixit.
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(E))
if (InClosure && isImplicitSelfUse(DSCE->getBase()) &&
isa<DeclRefExpr>(DSCE->getFn())) {
auto MethodExpr = cast<DeclRefExpr>(DSCE->getFn());
TC.diagnose(DSCE->getLoc(),
diag::method_call_in_closure_without_explicit_self,
MethodExpr->getDecl()->getName())
.fixItInsert(DSCE->getLoc(), "self.");
DiagnosedSelfs.push_back(DSCE->getBase());
}
// Catch any other implicit uses of self with a generic diagnostic.
if (InClosure && isImplicitSelfUse(E)) {
// Make sure this isn't a subexpression of something we've already
// emitted a diagnostic for.
if (!std::count(DiagnosedSelfs.begin(), DiagnosedSelfs.end(), E))
TC.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure);
}
return { true, E };
}
Expr *walkToExprPost(Expr *E) {
if (isa<ClosureExpr>(E)) {
assert(InClosure);
--InClosure;
}
return E;
}
};
const_cast<Expr *>(E)->walk(DiagnoseWalker(TC));
}
//===--------------------------------------------------------------------===//
// Diagnose availability.
//===--------------------------------------------------------------------===//
/// Diagnose specific availability for a declaration.
///
/// Returns true if no further availability checking is needed to reject
/// the use of this declaration.
static bool diagAvailability(TypeChecker &TC, const AvailabilityAttr *Attr,
const ValueDecl *D, SourceRange R,
const DeclContext *DC) {
// FIXME: Implement matching on the platform. For now just
// do the '*' platform (all platforms).
if (Attr->hasPlatform())
return false;
if (Attr->IsUnvailable) {
auto Name = D->getFullName();
auto Message = Attr->Message;
SourceLoc Loc = R.Start;
if (Message.empty()) {
TC.diagnose(Loc, diag::availability_decl_unavailable, Name)
.highlight(R);
} else {
TC.diagnose(Loc, diag::availability_decl_unavailable_msg,
Name, Message)
.highlight(SourceRange(Loc, Loc));
}
auto DLoc = D->getLoc();
if (DLoc.isValid())
TC.diagnose(DLoc, diag::availability_marked_unavailable, Name)
.highlight(Attr->getRange());
}
return false;
}
/// Diagnose uses of unavailable declarations.
static void diagAvailability(TypeChecker &TC, const ValueDecl *D,
SourceRange R, const DeclContext *DC) {
if (!D)
return;
for (auto Attr : D->getAttrs())
if (auto AvailAttr = dyn_cast<AvailabilityAttr>(Attr))
if (diagAvailability(TC, AvailAttr, D, R, DC))
return;
}
namespace {
class AvailabilityWalker : public ASTWalker {
TypeChecker &TC;
const DeclContext *DC;
public:
AvailabilityWalker(TypeChecker &TC, const DeclContext *DC)
: TC(TC), DC(DC) {}
virtual Expr *walkToExprPost(Expr *E) override {
if (auto DR = dyn_cast<DeclRefExpr>(E))
diagAvailability(TC, DR->getDecl(), DR->getSourceRange(), DC);
if (auto MR = dyn_cast<MemberRefExpr>(E))
diagAvailability(TC, MR->getMember().getDecl(), MR->getSourceRange(), DC);
if (auto OCDR = dyn_cast<OtherConstructorDeclRefExpr>(E))
diagAvailability(TC, OCDR->getDecl(), OCDR->getConstructorLoc(), DC);
if (auto DMR = dyn_cast<DynamicMemberRefExpr>(E))
diagAvailability(TC, DMR->getMember().getDecl(), DMR->getNameLoc(), DC);
if (auto DS = dyn_cast<DynamicSubscriptExpr>(E))
diagAvailability(TC, DS->getMember().getDecl(), DS->getSourceRange(), DC);
if (auto S = dyn_cast<SubscriptExpr>(E)) {
if (S->hasDecl())
diagAvailability(TC, S->getDecl().getDecl(), S->getSourceRange(), DC);
}
return E;
}
};
}
/// Diagnose uses of unavailable declarations.
static void diagAvailability(TypeChecker &TC, const Expr *E,
const DeclContext *DC) {
AvailabilityWalker walker(TC, DC);
const_cast<Expr*>(E)->walk(walker);
}
//===--------------------------------------------------------------------===//
// High-level entry points.
//===--------------------------------------------------------------------===//
void swift::performExprDiagnostics(TypeChecker &TC, const Expr *E,
const DeclContext *DC) {
diagSelfAssignment(TC, E);
diagModuleOrMetatypeValue(TC, E);
diagRecursivePropertyAccess(TC, E, DC);
diagnoseImplicitSelfUseInClosure(TC, E);
diagAvailability(TC, E, DC);
}
void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) {
return diagUnreachableCode(TC, S);
}
//===--------------------------------------------------------------------===//
// Utility functions
//===--------------------------------------------------------------------===//
void swift::fixItAccessibility(InFlightDiagnostic &diag, ValueDecl *VD,
Accessibility desiredAccess, bool isForSetter) {
StringRef fixItString;
switch (desiredAccess) {
case Accessibility::Private: fixItString = "private "; break;
case Accessibility::Internal: fixItString = "internal "; break;
case Accessibility::Public: fixItString = "public "; break;
}
DeclAttribute *attr;
if (isForSetter) {
attr = VD->getAttrs().getAttribute<SetterAccessibilityAttr>();
if (auto setter = cast<AbstractStorageDecl>(VD)->getSetter())
setter->overwriteAccessibility(desiredAccess);
} else {
attr = VD->getAttrs().getAttribute<AccessibilityAttr>();
VD->overwriteAccessibility(desiredAccess);
}
if (isForSetter && VD->getAccessibility() == desiredAccess) {
assert(attr);
attr->setInvalid();
if (!attr->Range.isValid())
return;
// Remove the setter attribute along with a possible single trailing space.
SourceManager &sourceMgr = VD->getASTContext().SourceMgr;
SourceLoc nextCharLoc = Lexer::getLocForEndOfToken(sourceMgr,
attr->Range.End);
StringRef nextChar = sourceMgr.extractText({ nextCharLoc, 1 });
if (nextChar == " ")
diag.fixItRemoveChars(attr->Range.Start, nextCharLoc.getAdvancedLoc(1));
else
diag.fixItRemove(attr->Range);
} else if (attr) {
// This uses getLocation() instead of getRange() because we don't want to
// replace the "(set)" part of a setter attribute.
diag.fixItReplace(attr->getLocation(), fixItString.drop_back());
attr->setInvalid();
} else if (auto var = dyn_cast<VarDecl>(VD)) {
if (auto PBD = var->getParentPattern())
diag.fixItInsert(PBD->getStartLoc(), fixItString);
} else {
diag.fixItInsert(VD->getStartLoc(), fixItString);
}
}