Skip to content

Generate unused variable warnings in top level statements #7023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
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
74 changes: 56 additions & 18 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1854,8 +1854,7 @@ bool swift::fixItOverrideDeclarationTypes(InFlightDiagnostic &diag,

namespace {
class VarDeclUsageChecker : public ASTWalker {
TypeChecker &TC;

DiagnosticEngine &Diags;
// Keep track of some information about a variable.
enum {
RK_Read = 1, ///< Whether it was ever read.
Expand Down Expand Up @@ -1883,16 +1882,18 @@ class VarDeclUsageChecker : public ASTWalker {
void operator=(const VarDeclUsageChecker &) = delete;

public:
VarDeclUsageChecker(TypeChecker &TC, AbstractFunctionDecl *AFD) : TC(TC) {
VarDeclUsageChecker(TypeChecker &TC, AbstractFunctionDecl *AFD) : Diags(TC.Diags) {
// Track the parameters of the function.
for (auto PL : AFD->getParameterLists())
for (auto param : *PL)
if (shouldTrackVarDecl(param))
VarDecls[param] = 0;

}

VarDeclUsageChecker(TypeChecker &TC, VarDecl *VD) : TC(TC) {

VarDeclUsageChecker(DiagnosticEngine &Diags) : Diags(Diags) {}

VarDeclUsageChecker(TypeChecker &TC, VarDecl *VD) : Diags(TC.Diags) {
// Track a specific VarDecl
VarDecls[VD] = 0;
}
Expand Down Expand Up @@ -1998,6 +1999,35 @@ class VarDeclUsageChecker : public ASTWalker {
// Don't walk into it though, it may not even be type checked yet.
return false;
}
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
// If this is a TopLevelCodeDecl, scan for global variables
auto *body = TLCD->getBody();
for (auto node : body->getElements()) {
if (node.is<Decl *>()) {
// Flag all variables in a PatternBindingDecl
Decl *D = node.get<Decl *>();
PatternBindingDecl *PBD = dyn_cast<PatternBindingDecl>(D);
if (!PBD) continue;
for (PatternBindingEntry PBE : PBD->getPatternList()) {
PBE.getPattern()->forEachVariable([&](VarDecl *VD) {
VarDecls[VD] = RK_Read|RK_Written;
});
}
} else if (node.is<Stmt *>()) {
// Flag all variables in guard statements
Stmt *S = node.get<Stmt *>();
GuardStmt *GS = dyn_cast<GuardStmt>(S);
if (!GS) continue;
for (StmtConditionElement SCE : GS->getCond()) {
if (auto pattern = SCE.getPatternOrNull()) {
pattern->forEachVariable([&](VarDecl *VD) {
VarDecls[VD] = RK_Read|RK_Written;
});
}
}
}
}
}


// Note that we ignore the initialization behavior of PatternBindingDecls,
Expand Down Expand Up @@ -2080,8 +2110,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
// produce a fixit hint with a parent map, but this is a lot of effort for
// a narrow case.
if (access & RK_CaptureList) {
TC.diagnose(var->getLoc(), diag::capture_never_used,
var->getName());
Diags.diagnose(var->getLoc(), diag::capture_never_used,
var->getName());
continue;
}

Expand All @@ -2097,8 +2127,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
SourceRange replaceRange(
pbd->getStartLoc(),
pbd->getPatternList()[0].getPattern()->getEndLoc());
TC.diagnose(var->getLoc(), diag::pbd_never_used,
var->getName(), varKind)
Diags.diagnose(var->getLoc(), diag::pbd_never_used,
var->getName(), varKind)
.fixItReplace(replaceRange, "_");
continue;
}
Expand Down Expand Up @@ -2132,8 +2162,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
noParens = isIsTest = true;
}

auto diagIF = TC.diagnose(var->getLoc(),
diag::pbd_never_used_stmtcond,
auto diagIF = Diags.diagnose(var->getLoc(),
diag::pbd_never_used_stmtcond,
var->getName());
auto introducerLoc = SC->getCond()[0].getIntroducerLoc();
diagIF.fixItReplaceChars(introducerLoc,
Expand All @@ -2160,8 +2190,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
// let (a,b) = foo()
// Just rewrite the one variable with a _.
unsigned varKind = var->isLet();
TC.diagnose(var->getLoc(), diag::variable_never_used,
var->getName(), varKind)
Diags.diagnose(var->getLoc(), diag::variable_never_used,
var->getName(), varKind)
.fixItReplace(var->getLoc(), "_");
continue;
}
Expand Down Expand Up @@ -2194,18 +2224,18 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
// If this is a parameter explicitly marked 'var', remove it.
unsigned varKind = isa<ParamDecl>(var);
if (FixItLoc.isInvalid())
TC.diagnose(var->getLoc(), diag::variable_never_mutated,
var->getName(), varKind);
Diags.diagnose(var->getLoc(), diag::variable_never_mutated,
var->getName(), varKind);
else
TC.diagnose(var->getLoc(), diag::variable_never_mutated,
var->getName(), varKind)
Diags.diagnose(var->getLoc(), diag::variable_never_mutated,
var->getName(), varKind)
.fixItReplace(FixItLoc, "let");
continue;
}

// If this is a variable that was only written to, emit a warning.
if ((access & RK_Read) == 0) {
TC.diagnose(var->getLoc(), diag::variable_never_read, var->getName(),
Diags.diagnose(var->getLoc(), diag::variable_never_read, var->getName(),
isa<ParamDecl>(var));
continue;
}
Expand Down Expand Up @@ -2376,6 +2406,14 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigStmt *ICS) {
}
}

/// Apply the warnings managed by VarDeclUsageChecker to the top level
/// code declarations that haven't been checked yet.
void swift::
performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD) {
VarDeclUsageChecker checker(TC.Diags);
TLCD->walk(checker);
}

/// Perform diagnostics for func/init/deinit declarations.
void swift::performAbstractFuncDeclDiagnostics(TypeChecker &TC,
AbstractFunctionDecl *AFD) {
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/MiscDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace swift {
class Expr;
class InFlightDiagnostic;
class Stmt;
class TopLevelCodeDecl;
class TypeChecker;
class ValueDecl;

Expand All @@ -41,6 +42,9 @@ void performStmtDiagnostics(TypeChecker &TC, const Stmt *S);

void performAbstractFuncDeclDiagnostics(TypeChecker &TC,
AbstractFunctionDecl *AFD);

/// Perform diagnostics on the top level code declaration.
void performTopLevelDeclDiagnostics(TypeChecker &TC, TopLevelCodeDecl *TLCD);

/// Emit a fix-it to set the accessibility of \p VD to \p desiredAccess.
///
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1585,4 +1585,5 @@ void TypeChecker::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {
StmtChecker(*this, TLCD).typeCheckStmt(Body);
TLCD->setBody(Body);
checkTopLevelErrorHandling(TLCD);
performTopLevelDeclDiagnostics(*this, TLCD);
}
1 change: 1 addition & 0 deletions lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "swift/Subsystems.h"
#include "TypeChecker.h"
#include "MiscDiagnostics.h"
#include "GenericTypeResolver.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ASTVisitor.h"
Expand Down
4 changes: 2 additions & 2 deletions test/ClangImporter/blocks_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func checkTypeImpl<T>(_ a: inout T, _: T.Type) {}
do {
var blockOpt = blockWithoutNullability()
checkTypeImpl(&blockOpt, Optional<my_block_t>.self)
var block: my_block_t = blockWithoutNullability()
var _: my_block_t = blockWithoutNullability()
}
do {
var block = blockWithNonnull()
Expand All @@ -41,7 +41,7 @@ do {
do {
var blockOpt = blockWithNullUnspecified()
checkTypeImpl(&blockOpt, Optional<my_block_t>.self)
var block: my_block_t = blockWithNullUnspecified()
var _: my_block_t = blockWithNullUnspecified()
}
do {
var block = blockWithNullable()
Expand Down
32 changes: 16 additions & 16 deletions test/Compatibility/tuple_arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ do {
}

do {
var a = 3
var b = 4
var c = (3)
var d = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (3) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

concrete(a)
concrete((a))
Expand Down Expand Up @@ -535,9 +535,9 @@ do {
}

do {
var a = 3
var b = 4
var c = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (a, b) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}

_ = InitTwo(a, b)
_ = InitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}}
Expand Down Expand Up @@ -583,9 +583,9 @@ do {
}

do {
var a = 3
var b = 4
var d = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

var s1 = SubscriptTwo()
_ = s1[a, b]
Expand Down Expand Up @@ -952,9 +952,9 @@ do {
}

do {
var a = 3
var b = 4
var c = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (a, b) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}

// _ = GenericInit<(Int, Int)>(a, b) // Crashes in Swift 3
_ = GenericInit<(Int, Int)>((a, b)) // expected-error {{expression type 'GenericInit<(Int, Int)>' is ambiguous without more context}}
Expand Down Expand Up @@ -1017,9 +1017,9 @@ do {
}

do {
var a = 3.0
var b = 4.0
var d = (a, b)
var a = 3.0 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4.0 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

var s1 = GenericSubscript<(Double, Double)>()
_ = s1[a, b]
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/operator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ do {
let a: String? = "a"
let b: String? = "b"
let c: String? = "c"
let d: String? = a! + b! + c!
let _: String? = a! + b! + c!

let x: Double = 1
_ = x + x + x
Expand Down
8 changes: 4 additions & 4 deletions test/Constraints/patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ default:

// <rdar://problem/19382878> Introduce new x? pattern
switch Optional(42) {
case let x?: break
case let x?: break // expected-warning{{immutable value 'x' was never used; consider replacing with '_' or removing it}}
case nil: break
}

Expand Down Expand Up @@ -181,9 +181,9 @@ case x ?? 42: break // match value
default: break
}

for (var x) in 0...100 {}
for var x in 0...100 {} // rdar://20167543
for (let x) in 0...100 {} // expected-error {{'let' pattern cannot appear nested in an already immutable context}}
for (var x) in 0...100 {} // expected-warning{{variable 'x' was never used; consider replacing with '_' or removing it}}
for var x in 0...100 {} // rdar://20167543 expected-warning{{variable 'x' was never used; consider replacing with '_' or removing it}}
for (let x) in 0...100 { _ = x} // expected-error {{'let' pattern cannot appear nested in an already immutable context}}

var (let y) = 42 // expected-error {{'let' cannot appear nested inside another 'var' or 'let' pattern}}
let (var z) = 42 // expected-error {{'var' cannot appear nested inside another 'var' or 'let' pattern}}
Expand Down
32 changes: 16 additions & 16 deletions test/Constraints/tuple_arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ do {
}

do {
var a = 3
var b = 4
var c = (3)
var d = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (3) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

concrete(a)
concrete((a))
Expand Down Expand Up @@ -520,9 +520,9 @@ do {
}

do {
var a = 3
var b = 4
var c = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (a, b) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}

_ = InitTwo(a, b)
_ = InitTwo((a, b)) // expected-error {{missing argument for parameter #2 in call}}
Expand Down Expand Up @@ -568,9 +568,9 @@ do {
}

do {
var a = 3
var b = 4
var d = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

var s1 = SubscriptTwo()
_ = s1[a, b]
Expand Down Expand Up @@ -937,9 +937,9 @@ do {
}

do {
var a = 3
var b = 4
var c = (a, b)
var a = 3 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var c = (a, b) // expected-warning {{variable 'c' was never mutated; consider changing to 'let' constant}}

_ = GenericInit<(Int, Int)>(a, b) // expected-error {{extra argument in call}}
_ = GenericInit<(Int, Int)>((a, b))
Expand Down Expand Up @@ -1002,9 +1002,9 @@ do {
}

do {
var a = 3.0
var b = 4.0
var d = (a, b)
var a = 3.0 // expected-warning {{variable 'a' was never mutated; consider changing to 'let' constant}}
var b = 4.0 // expected-warning {{variable 'b' was never mutated; consider changing to 'let' constant}}
var d = (a, b) // expected-warning {{variable 'd' was never mutated; consider changing to 'let' constant}}

var s1 = GenericSubscript<(Double, Double)>()
_ = s1[a, b] // expected-error {{extra argument in call}}
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/availability_query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ if 1 != 2, #available(iOS 8.0, *) {}

// Pattern then #available(iOS 8.0, *) {
if case 42 = 42, #available(iOS 8.0, *) {}
if let x = Optional(42), #available(iOS 8.0, *) {}
if let _ = Optional(42), #available(iOS 8.0, *) {}

// Allow "macOS" as well.
if #available(macOS 10.51, *) {
Expand Down
2 changes: 1 addition & 1 deletion test/Parse/brace_recovery_eof.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

// Make sure source ranges satisfy the verifier.
for foo in [1, 2] { // expected-note {{to match this opening '{'}}
var x = foo
_ = foo
// expected-error@+1{{expected '}' at end of brace statement}}
Loading