Skip to content
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

[0.23] Detect breaking changes #1607

Merged
merged 39 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a393107
add analyzer for detecting references to optionals
turbolent Apr 26, 2022
afc1e51
add test for reference-to-optional analyzer
turbolent Apr 26, 2022
4c09850
add analyzer for depreacted key functions
turbolent Apr 26, 2022
8496fc5
add analyzer for number supertype binary operations
turbolent Apr 26, 2022
966ea6a
pass code in pass
turbolent Apr 26, 2022
44bd480
add analyzer for missing commas in parameter lists
turbolent Apr 26, 2022
7cf0e51
list available analyzers in usage
turbolent Apr 26, 2022
0cfa676
add analyzer for supertype inference in array, dictionary, and condit…
turbolent Apr 26, 2022
6f4298d
add analyzer for external mutation
turbolent Apr 26, 2022
fbe3d77
write diagnostics serially
turbolent Apr 26, 2022
fd0fd78
allow custom error prefix and color
turbolent Apr 27, 2022
8e23a3f
improve diagnostics, add suggestions
turbolent Apr 27, 2022
c0f187f
add category to diagnostic (required vs recommended)
turbolent Apr 27, 2022
50359eb
add analyzer for incorrect reference operator
turbolent Apr 27, 2022
d52990c
add analyzer for storage read operations
turbolent Apr 27, 2022
a714445
add analyzer for Address.toString
turbolent Apr 27, 2022
8636476
add descriptions
turbolent Apr 27, 2022
993889e
put CSV analysis behind a flag
turbolent Apr 27, 2022
3f5f211
revert support for custom prefix
turbolent Apr 28, 2022
9ea30ee
add support for loading contracts on-demand
turbolent Apr 28, 2022
db967f3
bring back category
turbolent Apr 28, 2022
9460377
use CLI's FlowKit to laod contracts from networks
turbolent Apr 28, 2022
fda2dfe
add documentation
turbolent Apr 28, 2022
e11ab1d
add build and publish infrastructure
turbolent Apr 28, 2022
007284e
add flag to skip analysis and only parse and check
turbolent May 4, 2022
a052e46
add support for transactions and scripts
turbolent May 4, 2022
4dbc685
improve supertype inference analyzer
turbolent May 4, 2022
967f30d
report diagnostic for supertype inference of conditional expressions …
turbolent May 5, 2022
451d1f9
add command to analyze a transaction on-chain
turbolent May 5, 2022
c479490
mention transaction flag
turbolent May 5, 2022
c3ec0cf
bump version
turbolent May 5, 2022
b7a98c5
fix README
turbolent May 5, 2022
a5dc169
mention flow init
turbolent May 5, 2022
d12e782
handle tabs in errror pretty printer
turbolent May 5, 2022
b54aa90
bump version
turbolent May 5, 2022
e9177f1
add support for analyzing directories
turbolent Jun 6, 2022
a9a0587
bump version
turbolent Jun 6, 2022
998438b
document directory flag
turbolent Jun 6, 2022
7b88d16
Merge branch 'v0.23' into bastian/43-breaking-changes-analysis
turbolent Jun 16, 2022
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
4 changes: 4 additions & 0 deletions runtime/sema/check_array_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (checker *Checker) VisitArrayExpression(expression *ast.ArrayExpression) as

expectedType := UnwrapOptionalType(checker.expectedType)

if expectedType != nil {
checker.Elaboration.ArrayExpressionHasExpectedType[expression] = struct{}{}
}

inferType := true

var elementType Type
Expand Down
4 changes: 4 additions & 0 deletions runtime/sema/check_dictionary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func (checker *Checker) VisitDictionaryExpression(expression *ast.DictionaryExpr

expectedType := UnwrapOptionalType(checker.expectedType)

if expectedType != nil {
checker.Elaboration.DictionaryExpressionHasExpectedType[expression] = struct{}{}
}

if expectedMapType, ok := expectedType.(*DictionaryType); ok {
keyType = expectedMapType.KeyType
valueType = expectedMapType.ValueType
Expand Down
4 changes: 4 additions & 0 deletions runtime/sema/elaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ type Elaboration struct {
MemberExpressionMemberInfos map[*ast.MemberExpression]MemberInfo
MemberExpressionExpectedTypes map[*ast.MemberExpression]Type
ArrayExpressionArgumentTypes map[*ast.ArrayExpression][]Type
ArrayExpressionHasExpectedType map[*ast.ArrayExpression]struct{}
ArrayExpressionArrayType map[*ast.ArrayExpression]ArrayType
DictionaryExpressionType map[*ast.DictionaryExpression]*DictionaryType
DictionaryExpressionHasExpectedType map[*ast.DictionaryExpression]struct{}
DictionaryExpressionEntryTypes map[*ast.DictionaryExpression][]DictionaryEntryType
IntegerExpressionType map[*ast.IntegerExpression]Type
FixedPointExpression map[*ast.FixedPointExpression]Type
Expand Down Expand Up @@ -125,8 +127,10 @@ func NewElaboration() *Elaboration {
MemberExpressionMemberInfos: map[*ast.MemberExpression]MemberInfo{},
MemberExpressionExpectedTypes: map[*ast.MemberExpression]Type{},
ArrayExpressionArgumentTypes: map[*ast.ArrayExpression][]Type{},
ArrayExpressionHasExpectedType: map[*ast.ArrayExpression]struct{}{},
ArrayExpressionArrayType: map[*ast.ArrayExpression]ArrayType{},
DictionaryExpressionType: map[*ast.DictionaryExpression]*DictionaryType{},
DictionaryExpressionHasExpectedType: map[*ast.DictionaryExpression]struct{}{},
DictionaryExpressionEntryTypes: map[*ast.DictionaryExpression][]DictionaryEntryType{},
IntegerExpressionType: map[*ast.IntegerExpression]Type{},
FixedPointExpression: map[*ast.FixedPointExpression]Type{},
Expand Down
19 changes: 19 additions & 0 deletions tools/contract-analyzer/analyzers/analyzers.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,15 @@ var SupertypeInferenceAnalyzer = (func() *analysis.Analyzer {
var typeTuples []typeTuple

var kind string
var reportMissingExpectedType bool

switch element := element.(type) {
case *ast.ArrayExpression:
kind = "arrays"

_, hasExpectedType := elaboration.ArrayExpressionHasExpectedType[element]
reportMissingExpectedType = !hasExpectedType

argumentTypes := elaboration.ArrayExpressionArgumentTypes[element]
if len(argumentTypes) < 2 {
return
Expand All @@ -384,6 +388,9 @@ var SupertypeInferenceAnalyzer = (func() *analysis.Analyzer {
case *ast.DictionaryExpression:
kind = "dictionaries"

_, hasExpectedType := elaboration.DictionaryExpressionHasExpectedType[element]
reportMissingExpectedType = !hasExpectedType

turbolent marked this conversation as resolved.
Show resolved Hide resolved
entryTypes := elaboration.DictionaryExpressionEntryTypes[element]
if len(entryTypes) < 2 {
return
Expand Down Expand Up @@ -434,6 +441,18 @@ var SupertypeInferenceAnalyzer = (func() *analysis.Analyzer {
return
}

if reportMissingExpectedType {
report(
analysis.Diagnostic{
Location: location,
Range: ast.NewRangeFromPositioned(element),
Category: "check recommended",
Message: fmt.Sprintf("type inference for %s will change", kind),
SecondaryMessage: "ensure the newly inferred type is correct",
},
)
}

},
)

Expand Down
51 changes: 39 additions & 12 deletions tools/contract-analyzer/analyzers/analyzers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package analyzers_test
import (
"testing"

"github.com/stretchr/testify/require"

"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/tools/analysis"
"github.com/onflow/cadence/tools/contract-analyzer/analyzers"
"github.com/stretchr/testify/require"
)

var testLocation = common.StringLocation("test")
Expand Down Expand Up @@ -202,11 +203,14 @@ func TestSupertypeInferenceAnalyzer(t *testing.T) {

diagnostics := testAnalyzers(t,
`
// same types
pub let a = [1, 2]
// same types, annotation
pub let a: [UInt64] = [1, 2]

// same types, no annotation
pub let b = [UInt64(1), 2]

// different types
pub let b = [true as AnyStruct, "true"]
pub let c = [true as AnyStruct, "true"]
`,
analyzers.SupertypeInferenceAnalyzer,
)
Expand All @@ -216,8 +220,18 @@ func TestSupertypeInferenceAnalyzer(t *testing.T) {
[]analysis.Diagnostic{
{
Range: ast.Range{
StartPos: ast.Position{Offset: 122, Line: 6, Column: 26},
EndPos: ast.Position{Offset: 148, Line: 6, Column: 52},
StartPos: ast.Position{Offset: 154, Line: 6, Column: 26},
EndPos: ast.Position{Offset: 167, Line: 6, Column: 39},
},
Location: testLocation,
Category: "check recommended",
Message: "type inference for arrays will change",
SecondaryMessage: "ensure the newly inferred type is correct",
},
{
Range: ast.Range{
StartPos: ast.Position{Offset: 229, Line: 9, Column: 26},
EndPos: ast.Position{Offset: 255, Line: 9, Column: 52},
},
Location: testLocation,
Category: "check required",
Expand All @@ -236,11 +250,14 @@ func TestSupertypeInferenceAnalyzer(t *testing.T) {
diagnostics := testAnalyzers(t,
`

// same types
pub let a = {1: "1", 2: "2"}
// same types, annotation
pub let a: {UInt64: String} = {1: "1", 2: "2"}

// same types, no annotation
pub let b = {UInt64(1): "1", 2: "2"}

// different value types
pub let b = {1: "1" as AnyStruct, 2: true}
pub let c = {1: "1" as AnyStruct, 2: true}
`,
analyzers.SupertypeInferenceAnalyzer,
)
Expand All @@ -250,8 +267,18 @@ func TestSupertypeInferenceAnalyzer(t *testing.T) {
[]analysis.Diagnostic{
{
Range: ast.Range{
StartPos: ast.Position{Offset: 139, Line: 7, Column: 26},
EndPos: ast.Position{Offset: 168, Line: 7, Column: 55},
StartPos: ast.Position{Offset: 169, Line: 7, Column: 26},
EndPos: ast.Position{Offset: 192, Line: 7, Column: 49},
},
Location: testLocation,
Category: "check recommended",
Message: "type inference for dictionaries will change",
SecondaryMessage: "ensure the newly inferred type is correct",
},
{
Range: ast.Range{
StartPos: ast.Position{Offset: 260, Line: 10, Column: 26},
EndPos: ast.Position{Offset: 289, Line: 10, Column: 55},
},
Location: testLocation,
Category: "check required",
Expand Down Expand Up @@ -559,7 +586,7 @@ func TestAddressToStringAnalyzer(t *testing.T) {
pub contract Test {
pub fun test() {
let address: Address = 0x1
let string = address.toString()
let string = address.toString()
}
}
`,
Expand Down