Skip to content

Commit 9e935d8

Browse files
authored
Merge pull request #22657 from asgerf/unified/unused-variable
Unified: Add unused-variable query
2 parents 1e30c74 + 6af7228 commit 9e935d8

7 files changed

Lines changed: 146 additions & 35 deletions

File tree

‎unified/extractor/src/languages/swift/swift.rs‎

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,11 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
531531
=>
532532
(identifier #{name})
533533
),
534+
rule!(
535+
(patternExpr pattern: @p)
536+
=>
537+
expr { p }
538+
),
534539
// A `let`/`var` value-binding pattern (`let x`) inside a case or `if case`
535540
// preserves the binding specifier around its inner pattern.
536541
rule!(
@@ -694,44 +699,11 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
694699
tree!((call_expr callee: {callee} argument: {args}))
695700
}
696701
),
697-
// A call or enum-case pattern argument. Both use the shared `argument`
698-
// shape, preserving the optional label as `name` and the child as `value`.
699-
// The pattern-only shapes (`patternExpr`, `discardAssignmentExpr`) are
700-
// matched first; they never occur as ordinary call arguments.
701-
rule!(
702-
(labeledExpr
703-
label: _? @@lbl
704-
expression: (functionCallExpr
705-
calledExpression: @constructor
706-
arguments: _* @elements) @@call)
707-
=>
708-
argument {
709-
let value = tree_at!(
710-
ctx,
711-
call,
712-
(call_expr callee: {constructor} argument: {elements})
713-
);
714-
tree!((argument
715-
name_node: (identifier #{lbl})?
716-
value: {value}))
717-
}
718-
),
719-
rule!(
720-
(labeledExpr label: _? @@lbl expression: (patternExpr pattern: @p))
721-
=>
722-
(argument name_node: (identifier #{lbl})? value: {p})
723-
),
724-
rule!(
725-
(labeledExpr label: _? @@lbl expression: (discardAssignmentExpr) @@wildcard)
726-
=>
727-
(argument name_node: (identifier #{lbl})? value: (identifier #{wildcard}))
728-
),
702+
// A call or enum-case pattern argument.
729703
rule!(
730704
(labeledExpr label: _? @@lbl expression: @val)
731705
=>
732-
argument {
733-
tree!((argument name_node: (identifier #{lbl})? value: {val}))
734-
}
706+
(argument name_node: (identifier #{lbl})? value: {val})
735707
),
736708
// Member access (`list.append`). The `declName` is itself a
737709
// `declReferenceExpr`; pull its `baseName` out as the member identifier.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
print(xs.map { $0 * 2 })
2+
3+
---
4+
5+
sourceFile
6+
endOfFileToken: endOfFile
7+
statements:
8+
codeBlockItem
9+
item:
10+
functionCallExpr
11+
leftParen: (
12+
rightParen: )
13+
arguments:
14+
labeledExpr
15+
expression:
16+
functionCallExpr
17+
arguments:
18+
additionalTrailingClosures:
19+
calledExpression:
20+
memberAccessExpr
21+
period: .
22+
declName:
23+
declReferenceExpr
24+
baseName: identifier "map"
25+
base:
26+
declReferenceExpr
27+
baseName: identifier "xs"
28+
trailingClosure:
29+
closureExpr
30+
leftBrace: {
31+
rightBrace: }
32+
statements:
33+
codeBlockItem
34+
item:
35+
infixOperatorExpr
36+
operator:
37+
binaryOperatorExpr
38+
operator: binaryOperator "*"
39+
leftOperand:
40+
declReferenceExpr
41+
baseName: dollarIdentifier "$0"
42+
rightOperand:
43+
integerLiteralExpr
44+
literal: integerLiteral "2"
45+
additionalTrailingClosures:
46+
calledExpression:
47+
declReferenceExpr
48+
baseName: identifier "print"
49+
50+
---
51+
52+
top_level source="⟨body⟩"
53+
body:
54+
block source="⟨stmt⟩"
55+
stmt:
56+
call_expr source="⟨callee⟩(⟨argument⟩)"
57+
callee: identifier "print" source="print"
58+
argument:
59+
argument source="⟨value⟩"
60+
value:
61+
call_expr source="⟨callee⟩ ⟨argument⟩"
62+
callee:
63+
member_access_expr source="⟨base⟩.⟨member_name_node⟩"
64+
base: identifier "xs" source="xs"
65+
member_name_node: identifier "map" source="map"
66+
argument:
67+
argument source="⟨value⟩"
68+
value:
69+
function_expr source="{ ⟨body⟩ }"
70+
body:
71+
block source="⟨stmt⟩"
72+
stmt:
73+
binary_expr source="⟨left⟩ ⟨operator⟩ ⟨right⟩"
74+
left: identifier "$0" source="$0"
75+
operator: infix_operator "*" source="*"
76+
right: int_literal "2" source="2"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print(xs.map { $0 * 2 })
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @name Unused variable
3+
* @description Unused variables may be an indication that the code is incomplete or has a typo.
4+
* @kind problem
5+
* @problem.severity recommendation
6+
* @id unified/unused-variable
7+
* @precision high
8+
*/
9+
10+
private import unified
11+
12+
private predicate isUnusedLocal(LocalName local) {
13+
local.getABinding().fromSource() and // ignore unused implicit locals, and ignore locals in built-ins
14+
not ignoreDeclaration(local.getABinding().getDeclaration()) and // ignore fields, method, etc
15+
not local.getName().regexpMatch("_.*") and
16+
not exists(LocalNameAccess access |
17+
access = local.getAnAccess() and
18+
not access instanceof NameBinding
19+
) and
20+
not local = any(UnqualifiedMemberAccess access).getImplicitQualifierVariable()
21+
}
22+
23+
private predicate ignoreDeclaration(AstNode n) {
24+
// ignore fields and methods etc
25+
n = any(ClassLikeDeclaration cls).getAMember()
26+
or
27+
// ignore top-level statements, which are often exported
28+
n = any(TopLevel t).getBody().getAStmt()
29+
or
30+
// Parameters are often needed to satisfy an interface
31+
n instanceof Parameter
32+
}
33+
34+
string getKind(LocalName local) {
35+
if local instanceof LocalVariable then result = "local variable" else result = "declaration"
36+
}
37+
38+
from LocalName local
39+
where isUnusedLocal(local)
40+
select local, "Unused " + getKind(local) + " '" + local.getName() + "'"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.swift:2:9:2:9 | a | Unused local variable 'a' |
2+
| test.swift:16:17:16:17 | b | Unused local variable 'b' |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
query: queries/unusedentities/UnusedVariable.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
func t1() -> Int {
2+
let a = 1 // $ Alert[unified/unused-variable]
3+
let b = 2
4+
return b
5+
}
6+
7+
func t2() -> Int {
8+
func foo(callback: (Int) -> Void) -> String {
9+
callback()
10+
return "df"
11+
}
12+
let a = 1
13+
print(
14+
foo { _ in
15+
print(a)
16+
let b = 2 // $ Alert[unified/unused-variable]
17+
})
18+
}

0 commit comments

Comments
 (0)