//===--- 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(E)) E = LE->getSubExpr(); if (auto *DRE = dyn_cast(E)) return DRE->getDecl(); return nullptr; } static std::pair findReferencedDecl(const Expr *E) { if (auto *LE = dyn_cast(E)) E = LE->getSubExpr(); if (auto *D = findSimpleReferencedDecl(E)) return std::make_pair(nullptr, D); if (auto *MRE = dyn_cast(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(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(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 walkToExprPre(Expr *E) override { // Diagnose module values that don't appear as part of a qualification. if (auto *ME = dyn_cast(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(ParentExpr) || isa(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(Base)) Base = Conv->getSubExpr(); auto *DRE = dyn_cast(Base); auto *MRE = dyn_cast(Base); if ((DRE && isa(DRE->getDecl())) || (MRE && isa(MRE->getMember().getDecl()))) { // 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::Metatype: Diagnose = Diagnostic::TypeOfMetatypeValue; break; case ExprKind::IntegerLiteral: case ExprKind::FloatLiteral: case ExprKind::CharacterLiteral: case ExprKind::StringLiteral: case ExprKind::InterpolatedStringLiteral: case ExprKind::MagicIdentifierLiteral: case ExprKind::DiscardAssignment: case ExprKind::DeclRef: case ExprKind::SuperRef: 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::AddressOf: case ExprKind::NewArray: case ExprKind::RebindSelfInConstructor: case ExprKind::OpaqueValue: case ExprKind::BindOptional: case ExprKind::OptionalEvaluation: 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::Erasure: case ExprKind::DerivedToBase: case ExprKind::ArchetypeToSuper: case ExprKind::ScalarToTuple: case ExprKind::InjectIntoOptional: case ExprKind::BridgeToBlock: case ExprKind::ConditionalCheckedCast: case ExprKind::Isa: case ExprKind::Coerce: case ExprKind::If: case ExprKind::Assign: case ExprKind::DefaultValue: case ExprKind::UnresolvedPattern: 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(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(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(DC); if (!fn || !fn->isGetterOrSetter()) return; auto var = dyn_cast(fn->getAccessorStorageDecl()); if (!var) // Ignore subscripts return; class DiagnoseWalker : public ASTWalker { TypeChecker &TC; VarDecl *Var; bool IsSetter; public: explicit DiagnoseWalker(TypeChecker &TC, VarDecl *var, bool isSetter) : TC(TC), Var(var), IsSetter(isSetter) {} std::pair walkToExprPre(Expr *E) override { if (auto *DRE = dyn_cast(E)) { // Handle local and top-level computed variables. if (DRE->getDecl() == Var && !DRE->isDirectPropertyAccess()) { bool shouldDiagnose = true; if (auto *ParentExpr = Parent.getAsExpr()) { if (isa(ParentExpr)) shouldDiagnose = false; else if (IsSetter) shouldDiagnose = !isa(ParentExpr); } if (shouldDiagnose) { TC.diagnose(E->getLoc(), diag::recursive_accessor_reference, Var->getName(), IsSetter); } } } else if (auto *MRE = dyn_cast(E)) { // Handle instance and type computed variables. // Find MemberRefExprs that have an implicit "self" base. if (MRE->getMember().getDecl() == Var && isa(MRE->getBase()) && MRE->getBase()->isImplicit() && !MRE->isDirectPropertyAccess()) { bool shouldDiagnose = true; if (IsSetter) shouldDiagnose = !dyn_cast_or_null(Parent.getAsExpr()); if (shouldDiagnose) { TC.diagnose(E->getLoc(), diag::recursive_accessor_reference, Var->getName(), IsSetter); TC.diagnose(E->getLoc(), diag::recursive_accessor_reference_silence) .fixItInsert(E->getStartLoc(), "self."); } } } else if (auto *PE = dyn_cast(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->isSetter()); const_cast(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; public: explicit DiagnoseWalker(TypeChecker &TC) : TC(TC) {} std::pair 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(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(E)) if (InClosure && MRE->getBase()->isImplicit() && isa(MRE->getBase())) TC.diagnose(MRE->getLoc(), diag::property_use_in_closure_without_explicit_self, MRE->getMember().getDecl()->getName()) .fixItInsert(MRE->getLoc(), "self."); return { true, E }; } Expr *walkToExprPost(Expr *E) { if (isa(E)) { assert(InClosure); --InClosure; } return E; } }; const_cast(E)->walk(DiagnoseWalker(TC)); } //===--------------------------------------------------------------------===// // 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); } void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) { return diagUnreachableCode(TC, S); }